From 21fb42652422ecfcd3cd2170236e093a284ce45b Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 28 Oct 2016 18:16:30 +0200 Subject: [PATCH 001/709] Make sure date is localised. --- app/Http/Middleware/Range.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Http/Middleware/Range.php b/app/Http/Middleware/Range.php index c483088515..149e6d8eb3 100644 --- a/app/Http/Middleware/Range.php +++ b/app/Http/Middleware/Range.php @@ -70,12 +70,12 @@ class Range // set start, end and finish: $this->setRange(); - // get variables for date range: - $this->datePicker(); - // set view variables. $this->configureView(); + // get variables for date range: + $this->datePicker(); + // set more view variables: $this->configureList(); } From 28eb54dc967c4c413d5c58f5e705845561387c4b Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 28 Oct 2016 19:01:52 +0200 Subject: [PATCH 002/709] Initial split for report options. --- resources/lang/en_US/firefly.php | 7 +++-- resources/views/reports/index.twig | 46 +++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 186fe8a8ea..961567281a 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -604,8 +604,8 @@ return [ 'in' => 'In', 'out' => 'Out', 'topX' => 'top :number', - 'show_full_list' => 'Show entire list', - 'show_only_top' => 'Show only top :number', + 'show_full_list' => 'Show entire list', + 'show_only_top' => 'Show only top :number', 'sum_of_year' => 'Sum of year', 'sum_of_years' => 'Sum of years', 'average_of_year' => 'Average of year', @@ -630,6 +630,9 @@ return [ 'balance_amount' => 'Expenses in budget ":budget" paid from account ":account" between :start and :end', 'no_audit_activity' => 'No activity was recorded on account :account_name between :start and :end.', 'audit_end_balance' => 'Account balance of :account_name at the end of :end was: :balance', + 'reports_extra_options' => 'Extra options', + 'report_has_no_extra_options' => 'This report has no extra options', + 'reports_submit' => 'View report', // charts: 'chart' => 'Chart', diff --git a/resources/views/reports/index.twig b/resources/views/reports/index.twig index 62ef34f336..c895ca4a9c 100644 --- a/resources/views/reports/index.twig +++ b/resources/views/reports/index.twig @@ -5,17 +5,19 @@ {% endblock %} {% block content %} +
-
-
-
-

{{ 'reports'|_ }}

-
-
-

- {{ 'more_info_help'|_ }} -

-
+ +
+
+
+

{{ 'reports'|_ }}

+
+
+

+ {{ 'more_info_help'|_ }} +

+
@@ -81,16 +83,34 @@
+
+
+
+
+

{{ 'reports_extra_options'|_ }}

+
+
+

+ {{ 'report_has_no_extra_options'|_ }} +

+
+
+ +
+
+

{{ 'reports_submit'|_ }}

+
+
- - +
-
+ +
From 73f1491d2dbf844e19327a6cdea359ed80fd2517 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 29 Oct 2016 07:44:46 +0200 Subject: [PATCH 003/709] Updates for translations. --- app/Http/Controllers/AccountController.php | 12 +++++++-- .../Admin/ConfigurationController.php | 11 ++++++-- app/Http/Controllers/AttachmentController.php | 12 +++++++-- app/Http/Controllers/BillController.php | 14 +++++++--- app/Http/Controllers/BudgetController.php | 16 ++++++++--- app/Http/Controllers/CategoryController.php | 12 +++++++-- app/Http/Controllers/Controller.php | 16 ++++++++--- app/Http/Controllers/CurrencyController.php | 12 +++++++-- app/Http/Controllers/ExportController.php | 12 +++++++-- app/Http/Controllers/HomeController.php | 16 +++++------ app/Http/Controllers/ImportController.php | 11 ++++++-- app/Http/Controllers/NewUserController.php | 7 +++++ app/Http/Controllers/PiggyBankController.php | 16 ++++++++--- .../Controllers/PreferencesController.php | 12 +++++++-- app/Http/Controllers/ProfileController.php | 27 +++++++++++++------ .../Controllers/Report/AccountController.php | 1 + app/Http/Controllers/ReportController.php | 26 +++++++++--------- app/Http/Controllers/RuleController.php | 12 +++++++-- app/Http/Controllers/RuleGroupController.php | 16 ++++++++--- app/Http/Controllers/SearchController.php | 7 +++++ app/Http/Controllers/TagController.php | 23 ++++++++++------ .../Transaction/MassController.php | 12 +++++++-- .../Transaction/SingleController.php | 6 ++--- .../Transaction/SplitController.php | 5 ++-- .../Controllers/TransactionController.php | 12 +++++++-- resources/lang/en_US/firefly.php | 1 - resources/views/layout/default.twig | 9 +++---- resources/views/reports/index.twig | 3 --- 28 files changed, 247 insertions(+), 92 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index d11462dc02..09fe795a2d 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -46,8 +46,16 @@ class AccountController extends Controller public function __construct() { parent::__construct(); - View::share('mainTitleIcon', 'fa-credit-card'); - View::share('title', trans('firefly.accounts')); + + // translations: + $this->middleware( + function ($request, $next) { + View::share('mainTitleIcon', 'fa-credit-card'); + View::share('title', trans('firefly.accounts')); + + return $next($request); + } + ); } /** diff --git a/app/Http/Controllers/Admin/ConfigurationController.php b/app/Http/Controllers/Admin/ConfigurationController.php index 147dfe31c6..33753166d2 100644 --- a/app/Http/Controllers/Admin/ConfigurationController.php +++ b/app/Http/Controllers/Admin/ConfigurationController.php @@ -37,8 +37,15 @@ class ConfigurationController extends Controller { parent::__construct(); - View::share('title', strval(trans('firefly.administration'))); - View::share('mainTitleIcon', 'fa-hand-spock-o'); + + $this->middleware( + function ($request, $next) { + View::share('title', strval(trans('firefly.administration'))); + View::share('mainTitleIcon', 'fa-hand-spock-o'); + + return $next($request); + } + ); } diff --git a/app/Http/Controllers/AttachmentController.php b/app/Http/Controllers/AttachmentController.php index 05e71a9a8f..e68a40ad8b 100644 --- a/app/Http/Controllers/AttachmentController.php +++ b/app/Http/Controllers/AttachmentController.php @@ -42,8 +42,16 @@ class AttachmentController extends Controller public function __construct() { parent::__construct(); - View::share('mainTitleIcon', 'fa-paperclip'); - View::share('title', trans('firefly.attachments')); + + // translations: + $this->middleware( + function ($request, $next) { + View::share('mainTitleIcon', 'fa-paperclip'); + View::share('title', trans('firefly.attachments')); + + return $next($request); + } + ); } /** diff --git a/app/Http/Controllers/BillController.php b/app/Http/Controllers/BillController.php index d6095d8360..31de91d724 100644 --- a/app/Http/Controllers/BillController.php +++ b/app/Http/Controllers/BillController.php @@ -38,8 +38,16 @@ class BillController extends Controller public function __construct() { parent::__construct(); - View::share('title', trans('firefly.bills')); - View::share('mainTitleIcon', 'fa-calendar-o'); + + + $this->middleware( + function ($request, $next) { + View::share('title', trans('firefly.bills')); + View::share('mainTitleIcon', 'fa-calendar-o'); + + return $next($request); + } + ); } /** @@ -140,7 +148,7 @@ class BillController extends Controller // paid in this period? $bill->paidDates = $repository->getPaidDatesInRange($bill, $start, $end); - $bill->payDates = $repository->getPayDatesInRange($bill, $start, $end); + $bill->payDates = $repository->getPayDatesInRange($bill, $start, $end); $lastDate = clone $start; if ($bill->paidDates->count() >= $bill->payDates->count()) { $lastDate = $end; diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index 439dfb6d8b..fdbe4bfe7d 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -47,9 +47,17 @@ class BudgetController extends Controller public function __construct() { parent::__construct(); - View::share('title', trans('firefly.budgets')); - View::share('mainTitleIcon', 'fa-tasks'); + View::share('hideBudgets', true); + + $this->middleware( + function ($request, $next) { + View::share('title', trans('firefly.budgets')); + View::share('mainTitleIcon', 'fa-tasks'); + + return $next($request); + } + ); } /** @@ -359,8 +367,8 @@ class BudgetController extends Controller */ public function store(BudgetFormRequest $request, BudgetRepositoryInterface $repository) { - $data = $request->getBudgetData(); - $budget = $repository->store($data); + $data = $request->getBudgetData(); + $budget = $repository->store($data); Session::flash('success', strval(trans('firefly.stored_new_budget', ['name' => e($budget->name)]))); Preferences::mark(); diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index 342c581440..f80d3a16bc 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -43,8 +43,16 @@ class CategoryController extends Controller public function __construct() { parent::__construct(); - View::share('title', trans('firefly.categories')); - View::share('mainTitleIcon', 'fa-bar-chart'); + + + $this->middleware( + function ($request, $next) { + View::share('title', trans('firefly.categories')); + View::share('mainTitleIcon', 'fa-bar-chart'); + + return $next($request); + } + ); } /** diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 6101c947f6..58917455dc 100755 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -46,10 +46,18 @@ class Controller extends BaseController View::share('hideBills', false); View::share('hideTags', false); - // save some formats: - $this->monthFormat = (string)trans('config.month'); - $this->monthAndDayFormat = (string)trans('config.month_and_day'); - $this->dateTimeFormat = (string)trans('config.date_time'); + + // translations: + + $this->middleware( + function ($request, $next) { + $this->monthFormat = (string)trans('config.month'); + $this->monthAndDayFormat = (string)trans('config.month_and_day'); + $this->dateTimeFormat = (string)trans('config.date_time'); + + return $next($request); + } + ); } diff --git a/app/Http/Controllers/CurrencyController.php b/app/Http/Controllers/CurrencyController.php index 1b3e817aa7..e1abc73e27 100644 --- a/app/Http/Controllers/CurrencyController.php +++ b/app/Http/Controllers/CurrencyController.php @@ -39,8 +39,16 @@ class CurrencyController extends Controller public function __construct() { parent::__construct(); - View::share('title', trans('firefly.currencies')); - View::share('mainTitleIcon', 'fa-usd'); + + + $this->middleware( + function ($request, $next) { + View::share('title', trans('firefly.currencies')); + View::share('mainTitleIcon', 'fa-usd'); + + return $next($request); + } + ); } /** diff --git a/app/Http/Controllers/ExportController.php b/app/Http/Controllers/ExportController.php index f8c20bc747..7deaae0f59 100644 --- a/app/Http/Controllers/ExportController.php +++ b/app/Http/Controllers/ExportController.php @@ -41,8 +41,16 @@ class ExportController extends Controller public function __construct() { parent::__construct(); - View::share('mainTitleIcon', 'fa-file-archive-o'); - View::share('title', trans('firefly.export_data')); + + + $this->middleware( + function ($request, $next) { + View::share('mainTitleIcon', 'fa-file-archive-o'); + View::share('title', trans('firefly.export_data')); + + return $next($request); + } + ); } /** diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 08a1153e5d..05f3daa755 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -26,7 +26,7 @@ use Log; use Preferences; use Route; use Session; - +use View; /** * Class HomeController @@ -41,6 +41,8 @@ class HomeController extends Controller public function __construct() { parent::__construct(); + View::share('title', 'Firefly III'); + View::share('mainTitleIcon', 'fa-fire'); } /** @@ -128,11 +130,9 @@ class HomeController extends Controller return redirect(route('new-user.index')); } - $title = 'Firefly'; - $subTitle = trans('firefly.welcomeBack'); - $mainTitleIcon = 'fa-fire'; - $transactions = []; - $frontPage = Preferences::get( + $subTitle = trans('firefly.welcomeBack'); + $transactions = []; + $frontPage = Preferences::get( 'frontPageAccounts', $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray() ); /** @var Carbon $start */ @@ -166,7 +166,7 @@ class HomeController extends Controller // these routes are not relevant for the help pages: $ignore = ['login', 'registe', 'logout', 'two-fac', 'lost-two', 'confirm', 'resend', 'do_confirm', 'testFla', 'json.', 'piggy-banks.add', 'piggy-banks.remove', 'preferences.', 'rules.rule.up', 'rules.rule.down', 'rules.rule-group.up', 'rules.rule-group.down', 'popup.report', - 'admin.users.domains.block-','import.json','help.' + 'admin.users.domains.block-', 'import.json', 'help.', ]; $routes = Route::getRoutes(); @@ -178,7 +178,7 @@ class HomeController extends Controller $methods = $route->getMethods(); if (!is_null($name) && strlen($name) > 0 && in_array('GET', $methods) && !$this->startsWithAny($ignore, $name)) { - echo sprintf('touch %s.md', $name)."\n"; + echo sprintf('touch %s.md', $name) . "\n"; } } diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index 05047e0e81..bbf09436c2 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -41,8 +41,15 @@ class ImportController extends Controller public function __construct() { parent::__construct(); - View::share('mainTitleIcon', 'fa-archive'); - View::share('title', trans('firefly.import_data_full')); + + $this->middleware( + function ($request, $next) { + View::share('mainTitleIcon', 'fa-archive'); + View::share('title', trans('firefly.import_data_full')); + + return $next($request); + } + ); } /** diff --git a/app/Http/Controllers/NewUserController.php b/app/Http/Controllers/NewUserController.php index 1fe99c6918..870b8ac966 100644 --- a/app/Http/Controllers/NewUserController.php +++ b/app/Http/Controllers/NewUserController.php @@ -32,6 +32,13 @@ class NewUserController extends Controller public function __construct() { parent::__construct(); + + $this->middleware( + function ($request, $next) { + + return $next($request); + } + ); } diff --git a/app/Http/Controllers/PiggyBankController.php b/app/Http/Controllers/PiggyBankController.php index 70c3ae8487..15c98f6e5e 100644 --- a/app/Http/Controllers/PiggyBankController.php +++ b/app/Http/Controllers/PiggyBankController.php @@ -45,8 +45,16 @@ class PiggyBankController extends Controller public function __construct() { parent::__construct(); - View::share('title', trans('firefly.piggyBanks')); - View::share('mainTitleIcon', 'fa-sort-amount-asc'); + + + $this->middleware( + function ($request, $next) { + View::share('title', trans('firefly.piggyBanks')); + View::share('mainTitleIcon', 'fa-sort-amount-asc'); + + return $next($request); + } + ); } /** @@ -364,7 +372,7 @@ class PiggyBankController extends Controller */ public function store(PiggyBankFormRequest $request, PiggyBankRepositoryInterface $repository) { - $data = $request->getPiggyBankData(); + $data = $request->getPiggyBankData(); $piggyBank = $repository->store($data); Session::flash('success', strval(trans('firefly.stored_piggy_bank', ['name' => e($piggyBank->name)]))); @@ -390,7 +398,7 @@ class PiggyBankController extends Controller */ public function update(PiggyBankRepositoryInterface $repository, PiggyBankFormRequest $request, PiggyBank $piggyBank) { - $data = $request->getPiggyBankData(); + $data = $request->getPiggyBankData(); $piggyBank = $repository->update($piggyBank, $data); Session::flash('success', strval(trans('firefly.updated_piggy_bank', ['name' => e($piggyBank->name)]))); diff --git a/app/Http/Controllers/PreferencesController.php b/app/Http/Controllers/PreferencesController.php index ae371c7e98..a0ec6288b3 100644 --- a/app/Http/Controllers/PreferencesController.php +++ b/app/Http/Controllers/PreferencesController.php @@ -35,8 +35,16 @@ class PreferencesController extends Controller public function __construct() { parent::__construct(); - View::share('title', trans('firefly.preferences')); - View::share('mainTitleIcon', 'fa-gear'); + + + $this->middleware( + function ($request, $next) { + View::share('title', trans('firefly.preferences')); + View::share('mainTitleIcon', 'fa-gear'); + + return $next($request); + } + ); } /** diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 5d368c09f5..23c42d3c1e 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -36,8 +36,15 @@ class ProfileController extends Controller { parent::__construct(); - View::share('title', trans('firefly.profile')); - View::share('mainTitleIcon', 'fa-user'); + + $this->middleware( + function ($request, $next) { + View::share('title', trans('firefly.profile')); + View::share('mainTitleIcon', 'fa-user'); + + return $next($request); + } + ); } /** @@ -45,9 +52,11 @@ class ProfileController extends Controller */ public function changePassword() { - return view('profile.change-password')->with('title', auth()->user()->email)->with('subTitle', trans('firefly.change_your_password'))->with( - 'mainTitleIcon', 'fa-user' - ); + $title = auth()->user()->email; + $subTitle = strval(trans('firefly.change_your_password')); + $subTitleIcon = 'fa-key'; + + return view('profile.change-password', compact('title', 'subTitle', 'subTitleIcon')); } /** @@ -55,9 +64,11 @@ class ProfileController extends Controller */ public function deleteAccount() { - return view('profile.delete-account')->with('title', auth()->user()->email)->with('subTitle', trans('firefly.delete_account'))->with( - 'mainTitleIcon', 'fa-user' - ); + $title = auth()->user()->email; + $subTitle = strval(trans('firefly.delete_account')); + $subTitleIcon = 'fa-trash'; + + return view('profile.delete-account', compact('title', 'subTitle', 'subTitleIcon')); } /** diff --git a/app/Http/Controllers/Report/AccountController.php b/app/Http/Controllers/Report/AccountController.php index 582ff0bf83..85da130558 100644 --- a/app/Http/Controllers/Report/AccountController.php +++ b/app/Http/Controllers/Report/AccountController.php @@ -53,6 +53,7 @@ class AccountController extends Controller $result = view('reports.partials.accounts', compact('accountReport'))->render(); $cache->store($result); + return $result; } } diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index 3bdeaf7f44..ab3833555a 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -49,8 +49,18 @@ class ReportController extends Controller { parent::__construct(); - View::share('title', trans('firefly.reports')); - View::share('mainTitleIcon', 'fa-line-chart'); + + $this->middleware( + function ($request, $next) { + View::share('title', trans('firefly.reports')); + View::share('mainTitleIcon', 'fa-line-chart'); + + $this->helper = app(ReportHelperInterface::class); + $this->budgetHelper = app(BudgetReportHelperInterface::class); + + return $next($request); + } + ); } @@ -61,7 +71,7 @@ class ReportController extends Controller */ public function index(AccountRepositoryInterface $repository) { - $this->createRepositories(); + /** @var Carbon $start */ $start = clone session('first'); $months = $this->helper->listOfMonths($start); @@ -92,7 +102,6 @@ class ReportController extends Controller */ public function report(string $reportType, Carbon $start, Carbon $end, Collection $accounts) { - $this->createRepositories(); // throw an error if necessary. if ($end < $start) { throw new FireflyException('End date cannot be before start date, silly!'); @@ -200,15 +209,6 @@ class ReportController extends Controller return view('reports.audit.report', compact('start', 'end', 'reportType', 'accountIds', 'accounts', 'auditData', 'hideable', 'defaultShow')); } - /** - * - */ - private function createRepositories() - { - $this->helper = app(ReportHelperInterface::class); - $this->budgetHelper = app(BudgetReportHelperInterface::class); - } - /** * @param $reportType * @param Carbon $start diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 4b0687a559..3f55f44e35 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -42,8 +42,16 @@ class RuleController extends Controller public function __construct() { parent::__construct(); - View::share('title', trans('firefly.rules')); - View::share('mainTitleIcon', 'fa-random'); + + + $this->middleware( + function ($request, $next) { + View::share('title', trans('firefly.rules')); + View::share('mainTitleIcon', 'fa-random'); + + return $next($request); + } + ); } /** diff --git a/app/Http/Controllers/RuleGroupController.php b/app/Http/Controllers/RuleGroupController.php index af62ca2085..f1ab10422b 100644 --- a/app/Http/Controllers/RuleGroupController.php +++ b/app/Http/Controllers/RuleGroupController.php @@ -41,8 +41,16 @@ class RuleGroupController extends Controller public function __construct() { parent::__construct(); - View::share('title', trans('firefly.rules')); - View::share('mainTitleIcon', 'fa-random'); + + + $this->middleware( + function ($request, $next) { + View::share('title', trans('firefly.rules')); + View::share('mainTitleIcon', 'fa-random'); + + return $next($request); + } + ); } /** @@ -204,8 +212,8 @@ class RuleGroupController extends Controller */ public function store(RuleGroupFormRequest $request, RuleGroupRepositoryInterface $repository) { - $data = $request->getRuleGroupData(); - $ruleGroup = $repository->store($data); + $data = $request->getRuleGroupData(); + $ruleGroup = $repository->store($data); Session::flash('success', strval(trans('firefly.created_new_rule_group', ['title' => $ruleGroup->title]))); Preferences::mark(); diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index 2066537371..bb602229d3 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -29,6 +29,13 @@ class SearchController extends Controller public function __construct() { parent::__construct(); + + $this->middleware( + function ($request, $next) { + + return $next($request); + } + ); } /** diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index 8459a12af8..fc7013d41a 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -50,15 +50,22 @@ class TagController extends Controller public function __construct() { parent::__construct(); - View::share('title', 'Tags'); - View::share('mainTitleIcon', 'fa-tags'); View::share('hideTags', true); - $this->tagOptions = [ - 'nothing' => trans('firefly.regular_tag'), - 'balancingAct' => trans('firefly.balancing_act'), - 'advancePayment' => trans('firefly.advance_payment'), - ]; - View::share('tagOptions', $this->tagOptions); + + $this->middleware( + function ($request, $next) { + View::share('title', 'Tags'); + View::share('mainTitleIcon', 'fa-tags'); + $this->tagOptions = [ + 'nothing' => trans('firefly.regular_tag'), + 'balancingAct' => trans('firefly.balancing_act'), + 'advancePayment' => trans('firefly.advance_payment'), + ]; + View::share('tagOptions', $this->tagOptions); + + return $next($request); + } + ); } /** diff --git a/app/Http/Controllers/Transaction/MassController.php b/app/Http/Controllers/Transaction/MassController.php index 2d9140ce1d..f32e8df053 100644 --- a/app/Http/Controllers/Transaction/MassController.php +++ b/app/Http/Controllers/Transaction/MassController.php @@ -41,8 +41,16 @@ class MassController extends Controller public function __construct() { parent::__construct(); - View::share('title', trans('firefly.transactions')); - View::share('mainTitleIcon', 'fa-repeat'); + + + $this->middleware( + function ($request, $next) { + View::share('title', trans('firefly.transactions')); + View::share('mainTitleIcon', 'fa-repeat'); + + return $next($request); + } + ); } /** diff --git a/app/Http/Controllers/Transaction/SingleController.php b/app/Http/Controllers/Transaction/SingleController.php index f6e9a4d22c..dc005002ad 100644 --- a/app/Http/Controllers/Transaction/SingleController.php +++ b/app/Http/Controllers/Transaction/SingleController.php @@ -59,8 +59,6 @@ class SingleController extends Controller public function __construct() { parent::__construct(); - View::share('title', trans('firefly.transactions')); - View::share('mainTitleIcon', 'fa-repeat'); $maxFileSize = Steam::phpBytes(ini_get('upload_max_filesize')); $maxPostSize = Steam::phpBytes(ini_get('post_max_size')); @@ -75,11 +73,13 @@ class SingleController extends Controller $this->piggyBanks = app(PiggyBankRepositoryInterface::class); $this->attachments = app(AttachmentHelperInterface::class); + View::share('title', trans('firefly.transactions')); + View::share('mainTitleIcon', 'fa-repeat'); + return $next($request); } ); - } /** diff --git a/app/Http/Controllers/Transaction/SplitController.php b/app/Http/Controllers/Transaction/SplitController.php index 2247c30fd3..d1f3504670 100644 --- a/app/Http/Controllers/Transaction/SplitController.php +++ b/app/Http/Controllers/Transaction/SplitController.php @@ -63,8 +63,7 @@ class SplitController extends Controller public function __construct() { parent::__construct(); - View::share('mainTitleIcon', 'fa-share-alt'); - View::share('title', trans('firefly.split-transactions')); + // some useful repositories: $this->middleware( @@ -74,6 +73,8 @@ class SplitController extends Controller $this->tasker = app(JournalTaskerInterface::class); $this->attachments = app(AttachmentHelperInterface::class); $this->currencies = app(CurrencyRepositoryInterface::class); + View::share('mainTitleIcon', 'fa-share-alt'); + View::share('title', trans('firefly.split-transactions')); return $next($request); } diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 8cd732a617..bd2a3d4c1e 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -35,8 +35,16 @@ class TransactionController extends Controller public function __construct() { parent::__construct(); - View::share('title', trans('firefly.transactions')); - View::share('mainTitleIcon', 'fa-repeat'); + + + $this->middleware( + function ($request, $next) { + View::share('title', trans('firefly.transactions')); + View::share('mainTitleIcon', 'fa-repeat'); + + return $next($request); + } + ); } diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 961567281a..609d08c0a2 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -619,7 +619,6 @@ return [ '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', 'report_date_range' => 'Date range', - 'report_include_help' => 'In all cases, transfers to shared accounts count as expenses, and transfers from shared accounts count as income.', 'report_preset_ranges' => 'Pre-set ranges', 'shared' => 'Shared', 'fiscal_year' => 'Fiscal year', diff --git a/resources/views/layout/default.twig b/resources/views/layout/default.twig index 376add29e6..921138dd81 100644 --- a/resources/views/layout/default.twig +++ b/resources/views/layout/default.twig @@ -4,13 +4,12 @@ - Firefly - {% if title != "Firefly" %} - // {{ title }} + <title>Firefly III + {% if title != "Firefly III" %} + » {{ title }} {% endif %} - {% if subTitle %} - // {{ subTitle }} + » {{ subTitle }} {% endif %} diff --git a/resources/views/reports/index.twig b/resources/views/reports/index.twig index c895ca4a9c..0fe5cfbbc7 100644 --- a/resources/views/reports/index.twig +++ b/resources/views/reports/index.twig @@ -45,9 +45,6 @@
{% endfor %} -

- {{ 'report_include_help'|_ }} -

From a3148dc172fde56d23f95510a3332f085e8dde0a Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 29 Oct 2016 09:03:14 +0200 Subject: [PATCH 004/709] Code for #321 --- app/Rules/Actions/SetSourceAccount.php | 140 +++ config/firefly.php | 3 + resources/lang/en_US/firefly.php | 1222 ++++++++++++------------ 3 files changed, 755 insertions(+), 610 deletions(-) create mode 100644 app/Rules/Actions/SetSourceAccount.php diff --git a/app/Rules/Actions/SetSourceAccount.php b/app/Rules/Actions/SetSourceAccount.php new file mode 100644 index 0000000000..5e625fe886 --- /dev/null +++ b/app/Rules/Actions/SetSourceAccount.php @@ -0,0 +1,140 @@ +action = $action; + } + + /** + * @param TransactionJournal $journal + * + * @return bool + */ + public function act(TransactionJournal $journal): bool + { + $this->journal = $journal; + $this->repository = app(AccountRepositoryInterface::class, [$journal->user]); + $count = $journal->transactions()->count(); + if ($count > 2) { + Log::error(sprintf('Cannot change source account of journal #%d because it is a split journal.', $journal->id)); + + return true; + } + + // journal type: + $type = $journal->transactionType->type; + // if this is a transfer or a withdrawal, the new source account must be an asset account or a default account, and it MUST exist: + if (($type === TransactionType::WITHDRAWAL || $type === TransactionType::TRANSFER) && !$this->findAssetAccount()) { + Log::error( + sprintf( + 'Cannot change source account of journal #%d because no asset account with name "%s" exists.', + $journal->id, $this->action->action_value + ) + ); + + return true; + } + + // if this is a deposit, the new source account must be a revenue account and may be created: + if ($type === TransactionType::DEPOSIT) { + $this->findRevenueAccount(); + } + + Log::debug(sprintf('New source account is #%d ("%s").', $this->newSourceAccount->id, $this->newSourceAccount->name)); + + // update source transaction with new source account: + // get source transaction: + $transaction = $journal->transactions()->where('amount', '<', 0)->first(); + $transaction->account_id = $this->newSourceAccount->id; + $transaction->save(); + Log::debug(sprintf('Updated transaction #%d and gave it new account ID.', $transaction->id)); + + return true; + } + + /** + * @return bool + */ + private function findAssetAccount(): bool + { + $account = $this->repository->findByName($this->action->action_value, [AccountType::DEFAULT, AccountType::ASSET]); + + if (is_null($account->id)) { + Log::debug(sprintf('There is NO asset account called "%s".', $this->action->action_value)); + + return false; + } + Log::debug(sprintf('There exists an asset account called "%s". ID is #%d', $this->action->action_value, $account->id)); + $this->newSourceAccount = $account; + + return true; + } + + /** + * + */ + private function findRevenueAccount() + { + $account = $this->repository->findByName($this->action->action_value, [AccountType::REVENUE]); + if (is_null($account->id)) { + // create new revenue account with this name: + $data = [ + 'name' => $this->action->action_value, + 'accountType' => 'revenue', + 'virtualBalance' => 0, + 'active' => true, + 'iban' => null, + ]; + $account = $this->repository->store($data); + } + Log::debug(sprintf('Found or created revenue account #%d ("%s")', $account->id, $account->name)); + $this->newSourceAccount = $account; + } +} diff --git a/config/firefly.php b/config/firefly.php index be4ee78bfd..f93f5fe6f5 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -184,6 +184,9 @@ return [ 'set_description' => 'FireflyIII\Rules\Actions\SetDescription', 'append_description' => 'FireflyIII\Rules\Actions\AppendDescription', 'prepend_description' => 'FireflyIII\Rules\Actions\PrependDescription', + + 'set_source_account' => 'FireflyIII\Rules\Actions\SetSourceAccount', + //'set_destination_account' => 'FireflyIII\Rules\Actions\SetDestinationAccount', ], 'rule-actions-text' => [ 'set_category', diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 609d08c0a2..a2a639d8e5 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -11,668 +11,670 @@ return [ // general stuff: - 'language_incomplete' => 'incomplete translation', - 'close' => 'Close', - 'actions' => 'Actions', - 'edit' => 'Edit', - 'delete' => 'Delete', - 'welcomeBack' => 'What\'s playing?', - 'everything' => 'Everything', - 'customRange' => 'Custom range', - 'apply' => 'Apply', - 'cancel' => 'Cancel', - 'from' => 'From', - 'to' => 'To', - 'showEverything' => 'Show everything', - 'never' => 'Never', - 'search_results_for' => 'Search results for ":query"', - 'bounced_error' => 'The message sent to :email bounced, so no access for you.', - 'deleted_error' => 'These credentials do not match our records.', - 'general_blocked_error' => 'Your account has been disabled, so you cannot login.', - 'expired_error' => 'Your account has expired, and can no longer be used.', - 'removed_amount' => 'Removed :amount', - 'added_amount' => 'Added :amount', - 'asset_account_role_help' => 'Any extra options resulting from your choice can be set later.', - 'Opening balance' => 'Opening balance', - 'create_new_stuff' => 'Create new stuff', - 'new_withdrawal' => 'New withdrawal', - 'new_deposit' => 'New deposit', - 'new_transfer' => 'New transfer', - 'new_asset_account' => 'New asset account', - 'new_expense_account' => 'New expense account', - 'new_revenue_account' => 'New revenue account', - 'new_budget' => 'New budget', - 'new_bill' => 'New bill', - 'block_account_logout' => 'You have been logged out. Blocked accounts cannot use this site. Did you register with a valid email address?', - 'flash_success' => 'Success!', - 'flash_info' => 'Message', - 'flash_warning' => 'Warning!', - 'flash_error' => 'Error!', - 'flash_info_multiple' => 'There is one message|There are :count messages', - 'flash_error_multiple' => 'There is one error|There are :count errors', - 'net_worth' => 'Net worth', - 'route_has_no_help' => 'There is no help for this route, or there is no help available in your language.', - 'two_factor_welcome' => 'Hello, :user!', - 'two_factor_enter_code' => 'To continue, please enter your two factor authentication code. Your application can generate it for you.', - 'two_factor_code_here' => 'Enter code here', - 'two_factor_title' => 'Two factor authentication', - 'authenticate' => 'Authenticate', - 'two_factor_forgot_title' => 'Lost two factor authentication', - 'two_factor_forgot' => 'I forgot my two-factor thing.', - 'two_factor_lost_header' => 'Lost your two factor authentication?', - 'two_factor_lost_intro' => 'Unfortunately, this is not something you can reset from the web interface. You have two choices.', - 'two_factor_lost_fix_self' => 'If you run your own instance of Firefly III, check the logs in storage/logs for instructions.', - 'two_factor_lost_fix_owner' => 'Otherwise, email the site owner, :site_owner and ask them to reset your two factor authentication.', - 'warning_much_data' => ':days days of data may take a while to load.', - 'registered' => 'You have registered successfully!', - 'search' => 'Search', - 'no_budget_pointer' => 'You seem to have no budgets yet. You should create some on the budgets-page. Budgets can help you keep track of expenses.', - 'source_accounts' => 'Source account(s)', - 'destination_accounts' => 'Destination account(s)', - 'user_id_is' => 'Your user id is :user', - 'field_supports_markdown' => 'This field supports Markdown.', + 'language_incomplete' => 'incomplete translation', + 'close' => 'Close', + 'actions' => 'Actions', + 'edit' => 'Edit', + 'delete' => 'Delete', + 'welcomeBack' => 'What\'s playing?', + 'everything' => 'Everything', + 'customRange' => 'Custom range', + 'apply' => 'Apply', + 'cancel' => 'Cancel', + 'from' => 'From', + 'to' => 'To', + 'showEverything' => 'Show everything', + 'never' => 'Never', + 'search_results_for' => 'Search results for ":query"', + 'bounced_error' => 'The message sent to :email bounced, so no access for you.', + 'deleted_error' => 'These credentials do not match our records.', + 'general_blocked_error' => 'Your account has been disabled, so you cannot login.', + 'expired_error' => 'Your account has expired, and can no longer be used.', + 'removed_amount' => 'Removed :amount', + 'added_amount' => 'Added :amount', + 'asset_account_role_help' => 'Any extra options resulting from your choice can be set later.', + 'Opening balance' => 'Opening balance', + 'create_new_stuff' => 'Create new stuff', + 'new_withdrawal' => 'New withdrawal', + 'new_deposit' => 'New deposit', + 'new_transfer' => 'New transfer', + 'new_asset_account' => 'New asset account', + 'new_expense_account' => 'New expense account', + 'new_revenue_account' => 'New revenue account', + 'new_budget' => 'New budget', + 'new_bill' => 'New bill', + 'block_account_logout' => 'You have been logged out. Blocked accounts cannot use this site. Did you register with a valid email address?', + 'flash_success' => 'Success!', + 'flash_info' => 'Message', + 'flash_warning' => 'Warning!', + 'flash_error' => 'Error!', + 'flash_info_multiple' => 'There is one message|There are :count messages', + 'flash_error_multiple' => 'There is one error|There are :count errors', + 'net_worth' => 'Net worth', + 'route_has_no_help' => 'There is no help for this route, or there is no help available in your language.', + 'two_factor_welcome' => 'Hello, :user!', + 'two_factor_enter_code' => 'To continue, please enter your two factor authentication code. Your application can generate it for you.', + 'two_factor_code_here' => 'Enter code here', + 'two_factor_title' => 'Two factor authentication', + 'authenticate' => 'Authenticate', + 'two_factor_forgot_title' => 'Lost two factor authentication', + 'two_factor_forgot' => 'I forgot my two-factor thing.', + 'two_factor_lost_header' => 'Lost your two factor authentication?', + 'two_factor_lost_intro' => 'Unfortunately, this is not something you can reset from the web interface. You have two choices.', + 'two_factor_lost_fix_self' => 'If you run your own instance of Firefly III, check the logs in storage/logs for instructions.', + 'two_factor_lost_fix_owner' => 'Otherwise, email the site owner, :site_owner and ask them to reset your two factor authentication.', + 'warning_much_data' => ':days days of data may take a while to load.', + 'registered' => 'You have registered successfully!', + 'search' => 'Search', + 'no_budget_pointer' => 'You seem to have no budgets yet. You should create some on the budgets-page. Budgets can help you keep track of expenses.', + 'source_accounts' => 'Source account(s)', + 'destination_accounts' => 'Destination account(s)', + 'user_id_is' => 'Your user id is :user', + 'field_supports_markdown' => 'This field supports Markdown.', // repeat frequencies: - 'repeat_freq_yearly' => 'yearly', - 'repeat_freq_monthly' => 'monthly', - 'weekly' => 'weekly', - 'quarterly' => 'quarterly', - 'half-year' => 'every half year', - 'yearly' => 'yearly', + 'repeat_freq_yearly' => 'yearly', + 'repeat_freq_monthly' => 'monthly', + 'weekly' => 'weekly', + 'quarterly' => 'quarterly', + 'half-year' => 'every half year', + 'yearly' => 'yearly', // account confirmation: - 'confirm_account_header' => 'Please confirm your account', - 'confirm_account_intro' => 'An email has been sent to the address you used during your registration. Please check it out for further instructions. If you did not get this message, you can have Firefly send it again.', - 'confirm_account_resend_email' => 'Send me the confirmation message I need to activate my account.', - 'account_is_confirmed' => 'Your account has been confirmed!', - 'invalid_activation_code' => 'It seems the code you are using is not valid, or has expired.', - 'confirm_account_is_resent_header' => 'The confirmation has been resent', - 'confirm_account_is_resent_text' => 'The confirmation message has been resent. If you still did not receive the confirmation message, please contact the site owner at :owner or check the log files to see what went wrong.', - 'confirm_account_is_resent_go_home' => 'Go to the index page of Firefly', - 'confirm_account_not_resent_header' => 'Something went wrong :(', - 'confirm_account_not_resent_intro' => 'The confirmation message has been not resent. If you still did not receive the confirmation message, please contact the site owner at :owner instead. Possibly, you have tried to resend the activation message too often. You can have Firefly III try to resend the confirmation message every hour.', - 'confirm_account_not_resent_go_home' => 'Go to the index page of Firefly', + 'confirm_account_header' => 'Please confirm your account', + 'confirm_account_intro' => 'An email has been sent to the address you used during your registration. Please check it out for further instructions. If you did not get this message, you can have Firefly send it again.', + 'confirm_account_resend_email' => 'Send me the confirmation message I need to activate my account.', + 'account_is_confirmed' => 'Your account has been confirmed!', + 'invalid_activation_code' => 'It seems the code you are using is not valid, or has expired.', + 'confirm_account_is_resent_header' => 'The confirmation has been resent', + 'confirm_account_is_resent_text' => 'The confirmation message has been resent. If you still did not receive the confirmation message, please contact the site owner at :owner or check the log files to see what went wrong.', + 'confirm_account_is_resent_go_home' => 'Go to the index page of Firefly', + 'confirm_account_not_resent_header' => 'Something went wrong :(', + 'confirm_account_not_resent_intro' => 'The confirmation message has been not resent. If you still did not receive the confirmation message, please contact the site owner at :owner instead. Possibly, you have tried to resend the activation message too often. You can have Firefly III try to resend the confirmation message every hour.', + 'confirm_account_not_resent_go_home' => 'Go to the index page of Firefly', // export data: - 'import_and_export' => 'Import and export', - 'export_data' => 'Export data', - 'export_data_intro' => 'For backup purposes, when migrating to another system or when migrating to another Firefly III installation.', - 'export_format' => 'Export format', - 'export_format_csv' => 'Comma separated values (CSV file)', - 'export_format_mt940' => 'MT940 compatible format', - 'export_included_accounts' => 'Export transactions from these accounts', - 'include_old_uploads_help' => 'Firefly III does not throw away the original CSV files you have imported in the past. You can include them in your export.', - 'do_export' => 'Export', - 'export_status_never_started' => 'The export has not started yet', - 'export_status_make_exporter' => 'Creating exporter thing...', - 'export_status_collecting_journals' => 'Collecting your transactions...', - 'export_status_collected_journals' => 'Collected your transactions!', - 'export_status_converting_to_export_format' => 'Converting your transactions...', - 'export_status_converted_to_export_format' => 'Converted your transactions!', - 'export_status_creating_journal_file' => 'Creating the export file...', - 'export_status_created_journal_file' => 'Created the export file!', - 'export_status_collecting_attachments' => 'Collecting all your attachments...', - 'export_status_collected_attachments' => 'Collected all your attachments!', - 'export_status_collecting_old_uploads' => 'Collecting all your previous uploads...', - 'export_status_collected_old_uploads' => 'Collected all your previous uploads!', - 'export_status_creating_config_file' => 'Creating a configuration file...', - 'export_status_created_config_file' => 'Created a configuration file!', - 'export_status_creating_zip_file' => 'Creating a zip file...', - 'export_status_created_zip_file' => 'Created a zip file!', - 'export_status_finished' => 'Export has succesfully finished! Yay!', - 'export_data_please_wait' => 'Please wait...', - 'attachment_explanation' => 'The file called \':attachment_name\' (#:attachment_id) was originally uploaded to :type \':description\' (#:journal_id) dated :date for the amount of :amount.', + 'import_and_export' => 'Import and export', + 'export_data' => 'Export data', + 'export_data_intro' => 'For backup purposes, when migrating to another system or when migrating to another Firefly III installation.', + 'export_format' => 'Export format', + 'export_format_csv' => 'Comma separated values (CSV file)', + 'export_format_mt940' => 'MT940 compatible format', + 'export_included_accounts' => 'Export transactions from these accounts', + 'include_old_uploads_help' => 'Firefly III does not throw away the original CSV files you have imported in the past. You can include them in your export.', + 'do_export' => 'Export', + 'export_status_never_started' => 'The export has not started yet', + 'export_status_make_exporter' => 'Creating exporter thing...', + 'export_status_collecting_journals' => 'Collecting your transactions...', + 'export_status_collected_journals' => 'Collected your transactions!', + 'export_status_converting_to_export_format' => 'Converting your transactions...', + 'export_status_converted_to_export_format' => 'Converted your transactions!', + 'export_status_creating_journal_file' => 'Creating the export file...', + 'export_status_created_journal_file' => 'Created the export file!', + 'export_status_collecting_attachments' => 'Collecting all your attachments...', + 'export_status_collected_attachments' => 'Collected all your attachments!', + 'export_status_collecting_old_uploads' => 'Collecting all your previous uploads...', + 'export_status_collected_old_uploads' => 'Collected all your previous uploads!', + 'export_status_creating_config_file' => 'Creating a configuration file...', + 'export_status_created_config_file' => 'Created a configuration file!', + 'export_status_creating_zip_file' => 'Creating a zip file...', + 'export_status_created_zip_file' => 'Created a zip file!', + 'export_status_finished' => 'Export has succesfully finished! Yay!', + 'export_data_please_wait' => 'Please wait...', + 'attachment_explanation' => 'The file called \':attachment_name\' (#:attachment_id) was originally uploaded to :type \':description\' (#:journal_id) dated :date for the amount of :amount.', // rules - 'rules' => 'Rules', - 'rules_explanation' => 'Here you can manage rules. Rules are triggered when a transaction is created or updated. Then, if the transaction has certain properties (called "triggers") Firefly will execute the "actions". Combined, you can make Firefly respond in a certain way to new transactions.', - 'rule_name' => 'Name of rule', - 'rule_triggers' => 'Rule triggers when', - 'rule_actions' => 'Rule will', - 'new_rule' => 'New rule', - 'new_rule_group' => 'New rule group', - 'rule_priority_up' => 'Give rule more priority', - 'rule_priority_down' => 'Give rule less priority', - 'make_new_rule_group' => 'Make new rule group', - 'store_new_rule_group' => 'Store new rule group', - 'created_new_rule_group' => 'New rule group ":title" stored!', - 'updated_rule_group' => 'Successfully updated rule group ":title".', - 'edit_rule_group' => 'Edit rule group ":title"', - 'delete_rule_group' => 'Delete rule group ":title"', - 'deleted_rule_group' => 'Deleted rule group ":title"', - 'update_rule_group' => 'Update rule group', - 'no_rules_in_group' => 'There are no rules in this group', - 'move_rule_group_up' => 'Move rule group up', - 'move_rule_group_down' => 'Move rule group down', - 'save_rules_by_moving' => 'Save these rule(s) by moving them to another rule group:', - 'make_new_rule' => 'Make new rule in rule group ":title"', - 'rule_help_stop_processing' => 'When you check this box, later rules in this group will not be executed.', - 'rule_help_active' => 'Inactive rules will never fire.', - 'stored_new_rule' => 'Stored new rule with title ":title"', - 'deleted_rule' => 'Deleted rule with title ":title"', - 'store_new_rule' => 'Store new rule', - 'updated_rule' => 'Updated rule with title ":title"', - 'default_rule_group_name' => 'Default rules', - 'default_rule_group_description' => 'All your rules not in a particular group.', - 'default_rule_name' => 'Your first default rule', - 'default_rule_description' => 'This rule is an example. You can safely delete it.', - 'default_rule_trigger_description' => 'The Man Who Sold the World', - 'default_rule_trigger_from_account' => 'David Bowie', - 'default_rule_action_prepend' => 'Bought the world from ', - 'default_rule_action_set_category' => 'Large expenses', - 'trigger' => 'Trigger', - 'trigger_value' => 'Trigger on value', - 'stop_processing_other_triggers' => 'Stop processing other triggers', - 'add_rule_trigger' => 'Add new trigger', - 'action' => 'Action', - 'action_value' => 'Action value', - 'stop_executing_other_actions' => 'Stop executing other actions', - 'add_rule_action' => 'Add new action', - 'edit_rule' => 'Edit rule ":title"', - 'delete_rule' => 'Delete rule ":title"', - 'update_rule' => 'Update rule', - 'test_rule_triggers' => 'See matching transactions', - 'warning_transaction_subset' => 'For performance reasons this list is limited to :max_num_transactions and may only show a subset of matching transactions', - 'warning_no_matching_transactions' => 'No matching transactions found. Please note that for performance reasons, only the last :num_transactions transactions have been checked.', - 'warning_no_valid_triggers' => 'No valid triggers provided.', - 'execute_on_existing_transactions' => 'Execute for existing transactions', - 'execute_on_existing_transactions_intro' => 'When a rule or group has been changed or added, you can execute it for existing transactions', - 'execute_on_existing_transactions_short' => 'Existing transactions', - 'executed_group_on_existing_transactions' => 'Executed group ":title" for existing transactions', - 'execute_group_on_existing_transactions' => 'Execute group ":title" for existing transactions', - 'include_transactions_from_accounts' => 'Include transactions from these accounts', - 'execute' => 'Execute', + 'rules' => 'Rules', + 'rules_explanation' => 'Here you can manage rules. Rules are triggered when a transaction is created or updated. Then, if the transaction has certain properties (called "triggers") Firefly will execute the "actions". Combined, you can make Firefly respond in a certain way to new transactions.', + 'rule_name' => 'Name of rule', + 'rule_triggers' => 'Rule triggers when', + 'rule_actions' => 'Rule will', + 'new_rule' => 'New rule', + 'new_rule_group' => 'New rule group', + 'rule_priority_up' => 'Give rule more priority', + 'rule_priority_down' => 'Give rule less priority', + 'make_new_rule_group' => 'Make new rule group', + 'store_new_rule_group' => 'Store new rule group', + 'created_new_rule_group' => 'New rule group ":title" stored!', + 'updated_rule_group' => 'Successfully updated rule group ":title".', + 'edit_rule_group' => 'Edit rule group ":title"', + 'delete_rule_group' => 'Delete rule group ":title"', + 'deleted_rule_group' => 'Deleted rule group ":title"', + 'update_rule_group' => 'Update rule group', + 'no_rules_in_group' => 'There are no rules in this group', + 'move_rule_group_up' => 'Move rule group up', + 'move_rule_group_down' => 'Move rule group down', + 'save_rules_by_moving' => 'Save these rule(s) by moving them to another rule group:', + 'make_new_rule' => 'Make new rule in rule group ":title"', + 'rule_help_stop_processing' => 'When you check this box, later rules in this group will not be executed.', + 'rule_help_active' => 'Inactive rules will never fire.', + 'stored_new_rule' => 'Stored new rule with title ":title"', + 'deleted_rule' => 'Deleted rule with title ":title"', + 'store_new_rule' => 'Store new rule', + 'updated_rule' => 'Updated rule with title ":title"', + 'default_rule_group_name' => 'Default rules', + 'default_rule_group_description' => 'All your rules not in a particular group.', + 'default_rule_name' => 'Your first default rule', + 'default_rule_description' => 'This rule is an example. You can safely delete it.', + 'default_rule_trigger_description' => 'The Man Who Sold the World', + 'default_rule_trigger_from_account' => 'David Bowie', + 'default_rule_action_prepend' => 'Bought the world from ', + 'default_rule_action_set_category' => 'Large expenses', + 'trigger' => 'Trigger', + 'trigger_value' => 'Trigger on value', + 'stop_processing_other_triggers' => 'Stop processing other triggers', + 'add_rule_trigger' => 'Add new trigger', + 'action' => 'Action', + 'action_value' => 'Action value', + 'stop_executing_other_actions' => 'Stop executing other actions', + 'add_rule_action' => 'Add new action', + 'edit_rule' => 'Edit rule ":title"', + 'delete_rule' => 'Delete rule ":title"', + 'update_rule' => 'Update rule', + 'test_rule_triggers' => 'See matching transactions', + 'warning_transaction_subset' => 'For performance reasons this list is limited to :max_num_transactions and may only show a subset of matching transactions', + 'warning_no_matching_transactions' => 'No matching transactions found. Please note that for performance reasons, only the last :num_transactions transactions have been checked.', + 'warning_no_valid_triggers' => 'No valid triggers provided.', + 'execute_on_existing_transactions' => 'Execute for existing transactions', + 'execute_on_existing_transactions_intro' => 'When a rule or group has been changed or added, you can execute it for existing transactions', + 'execute_on_existing_transactions_short' => 'Existing transactions', + 'executed_group_on_existing_transactions' => 'Executed group ":title" for existing transactions', + 'execute_group_on_existing_transactions' => 'Execute group ":title" for existing transactions', + 'include_transactions_from_accounts' => 'Include transactions from these accounts', + 'execute' => 'Execute', // actions and triggers - 'rule_trigger_user_action' => 'User action is ":trigger_value"', - 'rule_trigger_from_account_starts' => 'Source account starts with ":trigger_value"', - 'rule_trigger_from_account_ends' => 'Source account ends with ":trigger_value"', - 'rule_trigger_from_account_is' => 'Source account is ":trigger_value"', - 'rule_trigger_from_account_contains' => 'Source account contains ":trigger_value"', - 'rule_trigger_to_account_starts' => 'Destination account starts with ":trigger_value"', - 'rule_trigger_to_account_ends' => 'Destination account ends with ":trigger_value"', - 'rule_trigger_to_account_is' => 'Destination account is ":trigger_value"', - 'rule_trigger_to_account_contains' => 'Destination account contains ":trigger_value"', - 'rule_trigger_transaction_type' => 'Transaction is of type ":trigger_value"', - 'rule_trigger_amount_less' => 'Amount is less than :trigger_value', - 'rule_trigger_amount_exactly' => 'Amount is :trigger_value', - 'rule_trigger_amount_more' => 'Amount is more than :trigger_value', - 'rule_trigger_description_starts' => 'Description starts with ":trigger_value"', - 'rule_trigger_description_ends' => 'Description ends with ":trigger_value"', - 'rule_trigger_description_contains' => 'Description contains ":trigger_value"', - 'rule_trigger_description_is' => 'Description is ":trigger_value"', - 'rule_trigger_from_account_starts_choice' => 'Source account starts with..', - 'rule_trigger_from_account_ends_choice' => 'Source account ends with..', - 'rule_trigger_from_account_is_choice' => 'Source account is..', - 'rule_trigger_from_account_contains_choice' => 'Source account contains..', - 'rule_trigger_to_account_starts_choice' => 'Destination account starts with..', - 'rule_trigger_to_account_ends_choice' => 'Destination account ends with..', - 'rule_trigger_to_account_is_choice' => 'Destination account is..', - 'rule_trigger_to_account_contains_choice' => 'Destination account contains..', - 'rule_trigger_transaction_type_choice' => 'Transaction is of type..', - 'rule_trigger_amount_less_choice' => 'Amount is less than..', - 'rule_trigger_amount_exactly_choice' => 'Amount is..', - 'rule_trigger_amount_more_choice' => 'Amount is more than..', - 'rule_trigger_description_starts_choice' => 'Description starts with..', - 'rule_trigger_description_ends_choice' => 'Description ends with..', - 'rule_trigger_description_contains_choice' => 'Description contains..', - 'rule_trigger_description_is_choice' => 'Description is..', - 'rule_trigger_store_journal' => 'When a journal is created', - 'rule_trigger_update_journal' => 'When a journal is updated', - 'rule_action_set_category' => 'Set category to ":action_value"', - 'rule_action_clear_category' => 'Clear category', - 'rule_action_set_budget' => 'Set budget to ":action_value"', - 'rule_action_clear_budget' => 'Clear budget', - 'rule_action_add_tag' => 'Add tag ":action_value"', - 'rule_action_remove_tag' => 'Remove tag ":action_value"', - 'rule_action_remove_all_tags' => 'Remove all tags', - 'rule_action_set_description' => 'Set description to ":action_value"', - 'rule_action_append_description' => 'Append description with ":action_value"', - 'rule_action_prepend_description' => 'Prepend description with ":action_value"', - 'rule_action_set_category_choice' => 'Set category to..', - 'rule_action_clear_category_choice' => 'Clear any category', - 'rule_action_set_budget_choice' => 'Set budget to..', - 'rule_action_clear_budget_choice' => 'Clear any budget', - 'rule_action_add_tag_choice' => 'Add tag..', - 'rule_action_remove_tag_choice' => 'Remove tag..', - 'rule_action_remove_all_tags_choice' => 'Remove all tags', - 'rule_action_set_description_choice' => 'Set description to..', - 'rule_action_append_description_choice' => 'Append description with..', - 'rule_action_prepend_description_choice' => 'Prepend description with..', - + 'rule_trigger_user_action' => 'User action is ":trigger_value"', + 'rule_trigger_from_account_starts' => 'Source account starts with ":trigger_value"', + 'rule_trigger_from_account_ends' => 'Source account ends with ":trigger_value"', + 'rule_trigger_from_account_is' => 'Source account is ":trigger_value"', + 'rule_trigger_from_account_contains' => 'Source account contains ":trigger_value"', + 'rule_trigger_to_account_starts' => 'Destination account starts with ":trigger_value"', + 'rule_trigger_to_account_ends' => 'Destination account ends with ":trigger_value"', + 'rule_trigger_to_account_is' => 'Destination account is ":trigger_value"', + 'rule_trigger_to_account_contains' => 'Destination account contains ":trigger_value"', + 'rule_trigger_transaction_type' => 'Transaction is of type ":trigger_value"', + 'rule_trigger_amount_less' => 'Amount is less than :trigger_value', + 'rule_trigger_amount_exactly' => 'Amount is :trigger_value', + 'rule_trigger_amount_more' => 'Amount is more than :trigger_value', + 'rule_trigger_description_starts' => 'Description starts with ":trigger_value"', + 'rule_trigger_description_ends' => 'Description ends with ":trigger_value"', + 'rule_trigger_description_contains' => 'Description contains ":trigger_value"', + 'rule_trigger_description_is' => 'Description is ":trigger_value"', + 'rule_trigger_from_account_starts_choice' => 'Source account starts with..', + 'rule_trigger_from_account_ends_choice' => 'Source account ends with..', + 'rule_trigger_from_account_is_choice' => 'Source account is..', + 'rule_trigger_from_account_contains_choice' => 'Source account contains..', + 'rule_trigger_to_account_starts_choice' => 'Destination account starts with..', + 'rule_trigger_to_account_ends_choice' => 'Destination account ends with..', + 'rule_trigger_to_account_is_choice' => 'Destination account is..', + 'rule_trigger_to_account_contains_choice' => 'Destination account contains..', + 'rule_trigger_transaction_type_choice' => 'Transaction is of type..', + 'rule_trigger_amount_less_choice' => 'Amount is less than..', + 'rule_trigger_amount_exactly_choice' => 'Amount is..', + 'rule_trigger_amount_more_choice' => 'Amount is more than..', + 'rule_trigger_description_starts_choice' => 'Description starts with..', + 'rule_trigger_description_ends_choice' => 'Description ends with..', + 'rule_trigger_description_contains_choice' => 'Description contains..', + 'rule_trigger_description_is_choice' => 'Description is..', + 'rule_trigger_store_journal' => 'When a journal is created', + 'rule_trigger_update_journal' => 'When a journal is updated', + 'rule_action_set_category' => 'Set category to ":action_value"', + 'rule_action_clear_category' => 'Clear category', + 'rule_action_set_budget' => 'Set budget to ":action_value"', + 'rule_action_clear_budget' => 'Clear budget', + 'rule_action_add_tag' => 'Add tag ":action_value"', + 'rule_action_remove_tag' => 'Remove tag ":action_value"', + 'rule_action_remove_all_tags' => 'Remove all tags', + 'rule_action_set_description' => 'Set description to ":action_value"', + 'rule_action_append_description' => 'Append description with ":action_value"', + 'rule_action_prepend_description' => 'Prepend description with ":action_value"', + 'rule_action_set_category_choice' => 'Set category to..', + 'rule_action_clear_category_choice' => 'Clear any category', + 'rule_action_set_budget_choice' => 'Set budget to..', + 'rule_action_clear_budget_choice' => 'Clear any budget', + 'rule_action_add_tag_choice' => 'Add tag..', + 'rule_action_remove_tag_choice' => 'Remove tag..', + 'rule_action_remove_all_tags_choice' => 'Remove all tags', + 'rule_action_set_description_choice' => 'Set description to..', + 'rule_action_append_description_choice' => 'Append description with..', + 'rule_action_prepend_description_choice' => 'Prepend description with..', + 'rule_action_set_source_account_choice' => 'Set source account to...', + 'rule_action_set_source_account' => 'Set source account to :action_value', + 'rule_action_set_destination_account_choice' => 'Set destination account to...', // tags - 'store_new_tag' => 'Store new tag', - 'update_tag' => 'Update tag', - 'no_location_set' => 'No location set.', - 'meta_data' => 'Meta data', - 'location' => 'Location', + 'store_new_tag' => 'Store new tag', + 'update_tag' => 'Update tag', + 'no_location_set' => 'No location set.', + 'meta_data' => 'Meta data', + 'location' => 'Location', // preferences - 'pref_home_screen_accounts' => 'Home screen accounts', - 'pref_home_screen_accounts_help' => 'Which accounts should be displayed on the home page?', - 'pref_view_range' => 'View range', - 'pref_view_range_help' => 'Some charts are automatically grouped in periods. What period would you prefer?', - 'pref_1D' => 'One day', - 'pref_1W' => 'One week', - 'pref_1M' => 'One month', - 'pref_3M' => 'Three months (quarter)', - 'pref_6M' => 'Six months', - 'pref_1Y' => 'One year', - 'pref_languages' => 'Languages', - 'pref_languages_help' => 'Firefly III supports several languages. Which one do you prefer?', - 'pref_custom_fiscal_year' => 'Fiscal year settings', - 'pref_custom_fiscal_year_label' => 'Enabled', - 'pref_custom_fiscal_year_help' => 'In countries that use a financial year other than January 1 to December 31, you can switch this on and specify start / end days of the fiscal year', - 'pref_fiscal_year_start_label' => 'Fiscal year start date', - 'pref_two_factor_auth' => '2-step verification', - 'pref_two_factor_auth_help' => 'When you enable 2-step verification (also known as two-factor authentication), you add an extra layer of security to your account. You sign in with something you know (your password) and something you have (a verification code). Verification codes are generated by an application on your phone, such as Authy or Google Authenticator.', - 'pref_enable_two_factor_auth' => 'Enable 2-step verification', - 'pref_two_factor_auth_disabled' => '2-step verification code removed and disabled', - 'pref_two_factor_auth_remove_it' => 'Don\'t forget to remove the account from your authentication app!', - 'pref_two_factor_auth_code' => 'Verify code', - 'pref_two_factor_auth_code_help' => 'Scan the QR code with an application on your phone such as Authy or Google Authenticator and enter the generated code.', - 'pref_two_factor_auth_reset_code' => 'Reset verification code', - 'pref_two_factor_auth_remove_code' => 'Remove verification code', - 'pref_two_factor_auth_remove_will_disable' => '(this will also disable two-factor authentication)', - 'pref_save_settings' => 'Save settings', - 'saved_preferences' => 'Preferences saved!', - 'preferences_general' => 'General', - 'preferences_frontpage' => 'Home screen', - 'preferences_security' => 'Security', - 'preferences_layout' => 'Layout', - 'pref_home_show_deposits' => 'Show deposits on the home screen', - 'pref_home_show_deposits_info' => 'The home screen already shows your expense accounts. Should it also show your revenue accounts?', - 'pref_home_do_show_deposits' => 'Yes, show them', - 'successful_count' => 'of which :count successful', - 'transaction_page_size_title' => 'Page size', - 'transaction_page_size_help' => 'Any list of transactions shows at most this many transactions', - 'transaction_page_size_label' => 'Page size', - 'between_dates' => '(:start and :end)', - 'pref_optional_fields_transaction' => 'Optional fields for transactions', - 'pref_optional_fields_transaction_help' => 'By default not all fields are enabled when creating a new transaction (because of the clutter). Below, you can enable these fields if you think they could be useful for you. Of course, any field that is disabled, but already filled in, will be visible regardless of the setting.', - 'optional_tj_date_fields' => 'Date fields', - 'optional_tj_business_fields' => 'Business fields', - 'optional_tj_attachment_fields' => 'Attachment fields', - 'pref_optional_tj_interest_date' => 'Interest date', - 'pref_optional_tj_book_date' => 'Book date', - 'pref_optional_tj_process_date' => 'Processing date', - 'pref_optional_tj_due_date' => 'Due date', - 'pref_optional_tj_payment_date' => 'Payment date', - 'pref_optional_tj_invoice_date' => 'Invoice date', - 'pref_optional_tj_internal_reference' => 'Internal reference', - 'pref_optional_tj_notes' => 'Notes', - 'pref_optional_tj_attachments' => 'Attachments', - 'optional_field_meta_dates' => 'Dates', - 'optional_field_meta_business' => 'Business', - 'optional_field_attachments' => 'Attachments', - 'optional_field_meta_data' => 'Optional meta data', + 'pref_home_screen_accounts' => 'Home screen accounts', + 'pref_home_screen_accounts_help' => 'Which accounts should be displayed on the home page?', + 'pref_view_range' => 'View range', + 'pref_view_range_help' => 'Some charts are automatically grouped in periods. What period would you prefer?', + 'pref_1D' => 'One day', + 'pref_1W' => 'One week', + 'pref_1M' => 'One month', + 'pref_3M' => 'Three months (quarter)', + 'pref_6M' => 'Six months', + 'pref_1Y' => 'One year', + 'pref_languages' => 'Languages', + 'pref_languages_help' => 'Firefly III supports several languages. Which one do you prefer?', + 'pref_custom_fiscal_year' => 'Fiscal year settings', + 'pref_custom_fiscal_year_label' => 'Enabled', + 'pref_custom_fiscal_year_help' => 'In countries that use a financial year other than January 1 to December 31, you can switch this on and specify start / end days of the fiscal year', + 'pref_fiscal_year_start_label' => 'Fiscal year start date', + 'pref_two_factor_auth' => '2-step verification', + 'pref_two_factor_auth_help' => 'When you enable 2-step verification (also known as two-factor authentication), you add an extra layer of security to your account. You sign in with something you know (your password) and something you have (a verification code). Verification codes are generated by an application on your phone, such as Authy or Google Authenticator.', + 'pref_enable_two_factor_auth' => 'Enable 2-step verification', + 'pref_two_factor_auth_disabled' => '2-step verification code removed and disabled', + 'pref_two_factor_auth_remove_it' => 'Don\'t forget to remove the account from your authentication app!', + 'pref_two_factor_auth_code' => 'Verify code', + 'pref_two_factor_auth_code_help' => 'Scan the QR code with an application on your phone such as Authy or Google Authenticator and enter the generated code.', + 'pref_two_factor_auth_reset_code' => 'Reset verification code', + 'pref_two_factor_auth_remove_code' => 'Remove verification code', + 'pref_two_factor_auth_remove_will_disable' => '(this will also disable two-factor authentication)', + 'pref_save_settings' => 'Save settings', + 'saved_preferences' => 'Preferences saved!', + 'preferences_general' => 'General', + 'preferences_frontpage' => 'Home screen', + 'preferences_security' => 'Security', + 'preferences_layout' => 'Layout', + 'pref_home_show_deposits' => 'Show deposits on the home screen', + 'pref_home_show_deposits_info' => 'The home screen already shows your expense accounts. Should it also show your revenue accounts?', + 'pref_home_do_show_deposits' => 'Yes, show them', + 'successful_count' => 'of which :count successful', + 'transaction_page_size_title' => 'Page size', + 'transaction_page_size_help' => 'Any list of transactions shows at most this many transactions', + 'transaction_page_size_label' => 'Page size', + 'between_dates' => '(:start and :end)', + 'pref_optional_fields_transaction' => 'Optional fields for transactions', + 'pref_optional_fields_transaction_help' => 'By default not all fields are enabled when creating a new transaction (because of the clutter). Below, you can enable these fields if you think they could be useful for you. Of course, any field that is disabled, but already filled in, will be visible regardless of the setting.', + 'optional_tj_date_fields' => 'Date fields', + 'optional_tj_business_fields' => 'Business fields', + 'optional_tj_attachment_fields' => 'Attachment fields', + 'pref_optional_tj_interest_date' => 'Interest date', + 'pref_optional_tj_book_date' => 'Book date', + 'pref_optional_tj_process_date' => 'Processing date', + 'pref_optional_tj_due_date' => 'Due date', + 'pref_optional_tj_payment_date' => 'Payment date', + 'pref_optional_tj_invoice_date' => 'Invoice date', + 'pref_optional_tj_internal_reference' => 'Internal reference', + 'pref_optional_tj_notes' => 'Notes', + 'pref_optional_tj_attachments' => 'Attachments', + 'optional_field_meta_dates' => 'Dates', + 'optional_field_meta_business' => 'Business', + 'optional_field_attachments' => 'Attachments', + 'optional_field_meta_data' => 'Optional meta data', // profile: - 'change_your_password' => 'Change your password', - 'delete_account' => 'Delete account', - 'current_password' => 'Current password', - 'new_password' => 'New password', - 'new_password_again' => 'New password (again)', - 'delete_your_account' => 'Delete your account', - 'delete_your_account_help' => 'Deleting your account will also delete any accounts, transactions, anything you might have saved into Firefly III. It\'ll be GONE.', - 'delete_your_account_password' => 'Enter your password to continue.', - 'password' => 'Password', - 'are_you_sure' => 'Are you sure? You cannot undo this.', - 'delete_account_button' => 'DELETE your account', - 'invalid_current_password' => 'Invalid current password!', - 'password_changed' => 'Password changed!', - 'should_change' => 'The idea is to change your password.', - 'invalid_password' => 'Invalid password!', + 'change_your_password' => 'Change your password', + 'delete_account' => 'Delete account', + 'current_password' => 'Current password', + 'new_password' => 'New password', + 'new_password_again' => 'New password (again)', + 'delete_your_account' => 'Delete your account', + 'delete_your_account_help' => 'Deleting your account will also delete any accounts, transactions, anything you might have saved into Firefly III. It\'ll be GONE.', + 'delete_your_account_password' => 'Enter your password to continue.', + 'password' => 'Password', + 'are_you_sure' => 'Are you sure? You cannot undo this.', + 'delete_account_button' => 'DELETE your account', + 'invalid_current_password' => 'Invalid current password!', + 'password_changed' => 'Password changed!', + 'should_change' => 'The idea is to change your password.', + 'invalid_password' => 'Invalid password!', // attachments - 'nr_of_attachments' => 'One attachment|:count attachments', - 'attachments' => 'Attachments', - 'edit_attachment' => 'Edit attachment ":name"', - 'update_attachment' => 'Update attachment', - 'delete_attachment' => 'Delete attachment ":name"', - 'attachment_deleted' => 'Deleted attachment ":name"', - 'attachment_updated' => 'Updated attachment ":name"', - 'upload_max_file_size' => 'Maximum file size: :size', + 'nr_of_attachments' => 'One attachment|:count attachments', + 'attachments' => 'Attachments', + 'edit_attachment' => 'Edit attachment ":name"', + 'update_attachment' => 'Update attachment', + 'delete_attachment' => 'Delete attachment ":name"', + 'attachment_deleted' => 'Deleted attachment ":name"', + 'attachment_updated' => 'Updated attachment ":name"', + 'upload_max_file_size' => 'Maximum file size: :size', // tour: - 'prev' => 'Prev', - 'next' => 'Next', - 'end-tour' => 'End tour', - 'pause' => 'Pause', + 'prev' => 'Prev', + 'next' => 'Next', + 'end-tour' => 'End tour', + 'pause' => 'Pause', // transaction index - 'title_expenses' => 'Expenses', - 'title_withdrawal' => 'Expenses', - 'title_revenue' => 'Revenue / income', - 'title_deposit' => 'Revenue / income', - 'title_transfer' => 'Transfers', - 'title_transfers' => 'Transfers', + 'title_expenses' => 'Expenses', + 'title_withdrawal' => 'Expenses', + 'title_revenue' => 'Revenue / income', + 'title_deposit' => 'Revenue / income', + 'title_transfer' => 'Transfers', + 'title_transfers' => 'Transfers', // create new stuff: - 'create_new_withdrawal' => 'Create new withdrawal', - 'create_new_deposit' => 'Create new deposit', - 'create_new_transfer' => 'Create new transfer', - 'create_new_asset' => 'Create new asset account', - 'create_new_expense' => 'Create new expense account', - 'create_new_revenue' => 'Create new revenue account', - 'create_new_piggy_bank' => 'Create new piggy bank', - 'create_new_bill' => 'Create new bill', + 'create_new_withdrawal' => 'Create new withdrawal', + 'create_new_deposit' => 'Create new deposit', + 'create_new_transfer' => 'Create new transfer', + 'create_new_asset' => 'Create new asset account', + 'create_new_expense' => 'Create new expense account', + 'create_new_revenue' => 'Create new revenue account', + 'create_new_piggy_bank' => 'Create new piggy bank', + 'create_new_bill' => 'Create new bill', // currencies: - 'create_currency' => 'Create a new currency', - 'store_currency' => 'Store new currency', - 'update_currency' => 'Update currency', - 'new_default_currency' => ':name is now the default currency.', - 'cannot_delete_currency' => 'Cannot delete :name because it is still in use.', - 'deleted_currency' => 'Currency :name deleted', - 'created_currency' => 'Currency :name created', - 'updated_currency' => 'Currency :name updated', - 'ask_site_owner' => 'Please ask :owner to add, remove or edit currencies.', - 'currencies_intro' => 'Firefly III supports various currencies which you can set and enable here.', - 'make_default_currency' => 'make default', - 'default_currency' => 'default', + 'create_currency' => 'Create a new currency', + 'store_currency' => 'Store new currency', + 'update_currency' => 'Update currency', + 'new_default_currency' => ':name is now the default currency.', + 'cannot_delete_currency' => 'Cannot delete :name because it is still in use.', + 'deleted_currency' => 'Currency :name deleted', + 'created_currency' => 'Currency :name created', + 'updated_currency' => 'Currency :name updated', + 'ask_site_owner' => 'Please ask :owner to add, remove or edit currencies.', + 'currencies_intro' => 'Firefly III supports various currencies which you can set and enable here.', + 'make_default_currency' => 'make default', + 'default_currency' => 'default', // new user: - 'submit' => 'Submit', - 'getting_started' => 'Getting started', - 'to_get_started' => 'To get started with Firefly, please enter your current bank\'s name, and the balance of your checking account:', - 'savings_balance_text' => 'If you have a savings account, please enter the current balance of your savings account:', - 'cc_balance_text' => 'If you have a credit card, please enter your credit card\'s limit.', - 'stored_new_account_new_user' => 'Yay! Your new account has been stored.', - 'stored_new_accounts_new_user' => 'Yay! Your new accounts have been stored.', + 'submit' => 'Submit', + 'getting_started' => 'Getting started', + 'to_get_started' => 'To get started with Firefly, please enter your current bank\'s name, and the balance of your checking account:', + 'savings_balance_text' => 'If you have a savings account, please enter the current balance of your savings account:', + 'cc_balance_text' => 'If you have a credit card, please enter your credit card\'s limit.', + 'stored_new_account_new_user' => 'Yay! Your new account has been stored.', + 'stored_new_accounts_new_user' => 'Yay! Your new accounts have been stored.', // forms: - 'mandatoryFields' => 'Mandatory fields', - 'optionalFields' => 'Optional fields', - 'options' => 'Options', + 'mandatoryFields' => 'Mandatory fields', + 'optionalFields' => 'Optional fields', + 'options' => 'Options', // budgets: - 'create_new_budget' => 'Create a new budget', - 'store_new_budget' => 'Store new budget', - 'stored_new_budget' => 'Stored new budget ":name"', - 'available_between' => 'Available between :start and :end', - 'transactionsWithoutBudget' => 'Expenses without budget', - 'transactions_no_budget' => 'Expenses without budget between :start and :end', - 'spent_between' => 'Spent between :start and :end', - 'createBudget' => 'New budget', - 'inactiveBudgets' => 'Inactive budgets', - 'without_budget_between' => 'Transactions without a budget between :start and :end', - 'budget_in_month' => ':name in :month', - 'delete_budget' => 'Delete budget ":name"', - 'deleted_budget' => 'Deleted budget ":name"', - 'edit_budget' => 'Edit budget ":name"', - 'updated_budget' => 'Updated budget ":name"', - 'update_amount' => 'Update amount', - 'update_budget' => 'Update budget', - 'update_budget_amount_range' => 'Update (expected) available amount between :start and :end', + 'create_new_budget' => 'Create a new budget', + 'store_new_budget' => 'Store new budget', + 'stored_new_budget' => 'Stored new budget ":name"', + 'available_between' => 'Available between :start and :end', + 'transactionsWithoutBudget' => 'Expenses without budget', + 'transactions_no_budget' => 'Expenses without budget between :start and :end', + 'spent_between' => 'Spent between :start and :end', + 'createBudget' => 'New budget', + 'inactiveBudgets' => 'Inactive budgets', + 'without_budget_between' => 'Transactions without a budget between :start and :end', + 'budget_in_month' => ':name in :month', + 'delete_budget' => 'Delete budget ":name"', + 'deleted_budget' => 'Deleted budget ":name"', + 'edit_budget' => 'Edit budget ":name"', + 'updated_budget' => 'Updated budget ":name"', + 'update_amount' => 'Update amount', + 'update_budget' => 'Update budget', + 'update_budget_amount_range' => 'Update (expected) available amount between :start and :end', // bills: - 'matching_on' => 'Matching on', - 'between_amounts' => 'between :low and :high.', - 'repeats' => 'Repeats', - 'connected_journals' => 'Connected transactions', - 'auto_match_on' => 'Automatically matched by Firefly', - 'auto_match_off' => 'Not automatically matched by Firefly', - 'next_expected_match' => 'Next expected match', - 'delete_bill' => 'Delete bill ":name"', - 'deleted_bill' => 'Deleted bill ":name"', - 'edit_bill' => 'Edit bill ":name"', - 'more' => 'More', - 'rescan_old' => 'Rescan old transactions', - 'update_bill' => 'Update bill', - 'updated_bill' => 'Updated bill ":name"', - 'store_new_bill' => 'Store new bill', - 'stored_new_bill' => 'Stored new bill ":name"', - 'cannot_scan_inactive_bill' => 'Inactive bills cannot be scanned.', - 'rescanned_bill' => 'Rescanned everything.', - 'average_bill_amount_year' => 'Average bill amount (:year)', - 'average_bill_amount_overall' => 'Average bill amount (overall)', - 'not_or_not_yet' => 'Not (yet)', - 'not_expected_period' => 'Not expected this period', + 'matching_on' => 'Matching on', + 'between_amounts' => 'between :low and :high.', + 'repeats' => 'Repeats', + 'connected_journals' => 'Connected transactions', + 'auto_match_on' => 'Automatically matched by Firefly', + 'auto_match_off' => 'Not automatically matched by Firefly', + 'next_expected_match' => 'Next expected match', + 'delete_bill' => 'Delete bill ":name"', + 'deleted_bill' => 'Deleted bill ":name"', + 'edit_bill' => 'Edit bill ":name"', + 'more' => 'More', + 'rescan_old' => 'Rescan old transactions', + 'update_bill' => 'Update bill', + 'updated_bill' => 'Updated bill ":name"', + 'store_new_bill' => 'Store new bill', + 'stored_new_bill' => 'Stored new bill ":name"', + 'cannot_scan_inactive_bill' => 'Inactive bills cannot be scanned.', + 'rescanned_bill' => 'Rescanned everything.', + 'average_bill_amount_year' => 'Average bill amount (:year)', + 'average_bill_amount_overall' => 'Average bill amount (overall)', + 'not_or_not_yet' => 'Not (yet)', + 'not_expected_period' => 'Not expected this period', // accounts: - 'details_for_asset' => 'Details for asset account ":name"', - 'details_for_expense' => 'Details for expense account ":name"', - 'details_for_revenue' => 'Details for revenue account ":name"', - 'details_for_cash' => 'Details for cash account ":name"', - 'store_new_asset_account' => 'Store new asset account', - 'store_new_expense_account' => 'Store new expense account', - 'store_new_revenue_account' => 'Store new revenue account', - 'edit_asset_account' => 'Edit asset account ":name"', - 'edit_expense_account' => 'Edit expense account ":name"', - 'edit_revenue_account' => 'Edit revenue account ":name"', - 'delete_asset_account' => 'Delete asset account ":name"', - 'delete_expense_account' => 'Delete expense account ":name"', - 'delete_revenue_account' => 'Delete revenue account ":name"', - 'asset_deleted' => 'Successfully deleted asset account ":name"', - 'expense_deleted' => 'Successfully deleted expense account ":name"', - 'revenue_deleted' => 'Successfully deleted revenue account ":name"', - 'update_asset_account' => 'Update asset account', - 'update_expense_account' => 'Update expense account', - 'update_revenue_account' => 'Update revenue account', - 'make_new_asset_account' => 'Create a new asset account', - 'make_new_expense_account' => 'Create a new expense account', - 'make_new_revenue_account' => 'Create a new revenue account', - 'asset_accounts' => 'Asset accounts', - 'expense_accounts' => 'Expense accounts', - 'revenue_accounts' => 'Revenue accounts', - 'cash_accounts' => 'Cash accounts', - 'Cash account' => 'Cash account', - 'account_type' => 'Account type', - 'save_transactions_by_moving' => 'Save these transaction(s) by moving them to another account:', - 'stored_new_account' => 'New account ":name" stored!', - 'updated_account' => 'Updated account ":name"', - 'credit_card_options' => 'Credit card options', + 'details_for_asset' => 'Details for asset account ":name"', + 'details_for_expense' => 'Details for expense account ":name"', + 'details_for_revenue' => 'Details for revenue account ":name"', + 'details_for_cash' => 'Details for cash account ":name"', + 'store_new_asset_account' => 'Store new asset account', + 'store_new_expense_account' => 'Store new expense account', + 'store_new_revenue_account' => 'Store new revenue account', + 'edit_asset_account' => 'Edit asset account ":name"', + 'edit_expense_account' => 'Edit expense account ":name"', + 'edit_revenue_account' => 'Edit revenue account ":name"', + 'delete_asset_account' => 'Delete asset account ":name"', + 'delete_expense_account' => 'Delete expense account ":name"', + 'delete_revenue_account' => 'Delete revenue account ":name"', + 'asset_deleted' => 'Successfully deleted asset account ":name"', + 'expense_deleted' => 'Successfully deleted expense account ":name"', + 'revenue_deleted' => 'Successfully deleted revenue account ":name"', + 'update_asset_account' => 'Update asset account', + 'update_expense_account' => 'Update expense account', + 'update_revenue_account' => 'Update revenue account', + 'make_new_asset_account' => 'Create a new asset account', + 'make_new_expense_account' => 'Create a new expense account', + 'make_new_revenue_account' => 'Create a new revenue account', + 'asset_accounts' => 'Asset accounts', + 'expense_accounts' => 'Expense accounts', + 'revenue_accounts' => 'Revenue accounts', + 'cash_accounts' => 'Cash accounts', + 'Cash account' => 'Cash account', + 'account_type' => 'Account type', + 'save_transactions_by_moving' => 'Save these transaction(s) by moving them to another account:', + 'stored_new_account' => 'New account ":name" stored!', + 'updated_account' => 'Updated account ":name"', + 'credit_card_options' => 'Credit card options', // categories: - 'new_category' => 'New category', - 'create_new_category' => 'Create a new category', - 'without_category' => 'Without a category', - 'update_category' => 'Update category', - 'updated_category' => 'Updated category ":name"', - 'categories' => 'Categories', - 'edit_category' => 'Edit category ":name"', - 'no_category' => '(no category)', - 'category' => 'Category', - 'delete_category' => 'Delete category ":name"', - 'deleted_category' => 'Deleted category ":name"', - 'store_category' => 'Store new category', - 'stored_category' => 'Stored new category ":name"', - 'without_category_between' => 'Without category between :start and :end', + 'new_category' => 'New category', + 'create_new_category' => 'Create a new category', + 'without_category' => 'Without a category', + 'update_category' => 'Update category', + 'updated_category' => 'Updated category ":name"', + 'categories' => 'Categories', + 'edit_category' => 'Edit category ":name"', + 'no_category' => '(no category)', + 'category' => 'Category', + 'delete_category' => 'Delete category ":name"', + 'deleted_category' => 'Deleted category ":name"', + 'store_category' => 'Store new category', + 'stored_category' => 'Stored new category ":name"', + 'without_category_between' => 'Without category between :start and :end', // transactions: - 'update_withdrawal' => 'Update withdrawal', - 'update_deposit' => 'Update deposit', - 'update_transfer' => 'Update transfer', - 'updated_withdrawal' => 'Updated withdrawal ":description"', - 'updated_deposit' => 'Updated deposit ":description"', - 'updated_transfer' => 'Updated transfer ":description"', - 'delete_withdrawal' => 'Delete withdrawal ":description"', - 'delete_deposit' => 'Delete deposit ":description"', - 'delete_transfer' => 'Delete transfer ":description"', - 'deleted_withdrawal' => 'Successfully deleted withdrawal ":description"', - 'deleted_deposit' => 'Successfully deleted deposit ":description"', - 'deleted_transfer' => 'Successfully deleted transfer ":description"', - 'stored_journal' => 'Successfully created new transaction ":description"', - 'select_transactions' => 'Select transactions', - 'stop_selection' => 'Stop selecting transactions', - 'edit_selected' => 'Edit selected', - 'delete_selected' => 'Delete selected', - 'mass_delete_journals' => 'Delete a number of transactions', - 'mass_edit_journals' => 'Edit a number of transactions', - 'cannot_edit_other_fields' => 'You cannot mass-edit other fields than the ones here, because there is no room to show them. Please follow the link and edit them by one-by-one, if you need to edit these fields.', - 'perm-delete-many' => 'Deleting many items in one go can be very disruptive. Please be cautious.', - 'mass_deleted_transactions_success' => 'Deleted :amount transaction(s).', - 'mass_edited_transactions_success' => 'Updated :amount transaction(s)', + 'update_withdrawal' => 'Update withdrawal', + 'update_deposit' => 'Update deposit', + 'update_transfer' => 'Update transfer', + 'updated_withdrawal' => 'Updated withdrawal ":description"', + 'updated_deposit' => 'Updated deposit ":description"', + 'updated_transfer' => 'Updated transfer ":description"', + 'delete_withdrawal' => 'Delete withdrawal ":description"', + 'delete_deposit' => 'Delete deposit ":description"', + 'delete_transfer' => 'Delete transfer ":description"', + 'deleted_withdrawal' => 'Successfully deleted withdrawal ":description"', + 'deleted_deposit' => 'Successfully deleted deposit ":description"', + 'deleted_transfer' => 'Successfully deleted transfer ":description"', + 'stored_journal' => 'Successfully created new transaction ":description"', + 'select_transactions' => 'Select transactions', + 'stop_selection' => 'Stop selecting transactions', + 'edit_selected' => 'Edit selected', + 'delete_selected' => 'Delete selected', + 'mass_delete_journals' => 'Delete a number of transactions', + 'mass_edit_journals' => 'Edit a number of transactions', + 'cannot_edit_other_fields' => 'You cannot mass-edit other fields than the ones here, because there is no room to show them. Please follow the link and edit them by one-by-one, if you need to edit these fields.', + 'perm-delete-many' => 'Deleting many items in one go can be very disruptive. Please be cautious.', + 'mass_deleted_transactions_success' => 'Deleted :amount transaction(s).', + 'mass_edited_transactions_success' => 'Updated :amount transaction(s)', // new user: - 'welcome' => 'Welcome to Firefly!', + 'welcome' => 'Welcome to Firefly!', // home page: - 'yourAccounts' => 'Your accounts', - 'budgetsAndSpending' => 'Budgets and spending', - 'savings' => 'Savings', - 'markAsSavingsToContinue' => 'Mark your asset accounts as "Savings account" to fill this panel', - 'createPiggyToContinue' => 'Create piggy banks to fill this panel.', - 'newWithdrawal' => 'New expense', - 'newDeposit' => 'New deposit', - 'newTransfer' => 'New transfer', - 'moneyIn' => 'Money in', - 'moneyOut' => 'Money out', - 'billsToPay' => 'Bills to pay', - 'billsPaid' => 'Bills paid', - 'divided' => 'divided', - 'toDivide' => 'left to divide', + 'yourAccounts' => 'Your accounts', + 'budgetsAndSpending' => 'Budgets and spending', + 'savings' => 'Savings', + 'markAsSavingsToContinue' => 'Mark your asset accounts as "Savings account" to fill this panel', + 'createPiggyToContinue' => 'Create piggy banks to fill this panel.', + 'newWithdrawal' => 'New expense', + 'newDeposit' => 'New deposit', + 'newTransfer' => 'New transfer', + 'moneyIn' => 'Money in', + 'moneyOut' => 'Money out', + 'billsToPay' => 'Bills to pay', + 'billsPaid' => 'Bills paid', + 'divided' => 'divided', + 'toDivide' => 'left to divide', // menu and titles, should be recycled as often as possible: - 'currency' => 'Currency', - 'preferences' => 'Preferences', - 'logout' => 'Logout', - 'searchPlaceholder' => 'Search...', - 'dashboard' => 'Dashboard', - 'currencies' => 'Currencies', - 'accounts' => 'Accounts', - 'Asset account' => 'Asset account', - 'Default account' => 'Asset account', - 'Expense account' => 'Expense account', - 'Revenue account' => 'Revenue account', - 'Initial balance account' => 'Initial balance account', - 'budgets' => 'Budgets', - 'tags' => 'Tags', - 'reports' => 'Reports', - 'transactions' => 'Transactions', - 'expenses' => 'Expenses', - 'income' => 'Revenue / income', - 'transfers' => 'Transfers', - 'moneyManagement' => 'Money management', - 'piggyBanks' => 'Piggy banks', - 'bills' => 'Bills', - 'withdrawal' => 'Withdrawal', - 'deposit' => 'Deposit', - 'account' => 'Account', - 'transfer' => 'Transfer', - 'Withdrawal' => 'Withdrawal', - 'Deposit' => 'Deposit', - 'Transfer' => 'Transfer', - 'bill' => 'Bill', - 'yes' => 'Yes', - 'no' => 'No', - 'amount' => 'Amount', - 'overview' => 'Overview', - 'saveOnAccount' => 'Save on account', - 'unknown' => 'Unknown', - 'daily' => 'Daily', - 'monthly' => 'Monthly', - 'profile' => 'Profile', - 'errors' => 'Errors', + 'currency' => 'Currency', + 'preferences' => 'Preferences', + 'logout' => 'Logout', + 'searchPlaceholder' => 'Search...', + 'dashboard' => 'Dashboard', + 'currencies' => 'Currencies', + 'accounts' => 'Accounts', + 'Asset account' => 'Asset account', + 'Default account' => 'Asset account', + 'Expense account' => 'Expense account', + 'Revenue account' => 'Revenue account', + 'Initial balance account' => 'Initial balance account', + 'budgets' => 'Budgets', + 'tags' => 'Tags', + 'reports' => 'Reports', + 'transactions' => 'Transactions', + 'expenses' => 'Expenses', + 'income' => 'Revenue / income', + 'transfers' => 'Transfers', + 'moneyManagement' => 'Money management', + 'piggyBanks' => 'Piggy banks', + 'bills' => 'Bills', + 'withdrawal' => 'Withdrawal', + 'deposit' => 'Deposit', + 'account' => 'Account', + 'transfer' => 'Transfer', + 'Withdrawal' => 'Withdrawal', + 'Deposit' => 'Deposit', + 'Transfer' => 'Transfer', + 'bill' => 'Bill', + 'yes' => 'Yes', + 'no' => 'No', + 'amount' => 'Amount', + 'overview' => 'Overview', + 'saveOnAccount' => 'Save on account', + 'unknown' => 'Unknown', + 'daily' => 'Daily', + 'monthly' => 'Monthly', + 'profile' => 'Profile', + 'errors' => 'Errors', // reports: - 'report_default' => 'Default financial report for :start until :end', - 'report_audit' => 'Transaction history overview for :start until :end', - 'quick_link_reports' => 'Quick links', - 'quick_link_default_report' => 'Default financial report', - 'quick_link_audit_report' => 'Transaction history overview', - 'report_this_month_quick' => 'Current month, all accounts', - 'report_this_year_quick' => 'Current year, all accounts', - 'report_this_fiscal_year_quick' => 'Current fiscal year, all accounts', - 'report_all_time_quick' => 'All-time, all accounts', - 'reports_can_bookmark' => 'Remember that reports can be bookmarked.', - 'incomeVsExpenses' => 'Income vs. expenses', - 'accountBalances' => 'Account balances', - 'balanceStartOfYear' => 'Balance at start of year', - 'balanceEndOfYear' => 'Balance at end of year', - 'balanceStartOfMonth' => 'Balance at start of month', - 'balanceEndOfMonth' => 'Balance at end of month', - 'balanceStart' => 'Balance at start of period', - 'balanceEnd' => 'Balance at end of period', - 'reportsOwnAccounts' => 'Reports for your own accounts', - 'reportsOwnAccountsAndShared' => 'Reports for your own accounts and shared accounts', - 'splitByAccount' => 'Split by account', - 'balancedByTransfersAndTags' => 'Balanced by transfers and tags', - 'coveredWithTags' => 'Covered with tags', - 'leftUnbalanced' => 'Left unbalanced', - 'expectedBalance' => 'Expected balance', - 'outsideOfBudgets' => 'Outside of budgets', - 'leftInBudget' => 'Left in budget', - 'sumOfSums' => 'Sum of sums', - 'noCategory' => '(no category)', - 'notCharged' => 'Not charged (yet)', - 'inactive' => 'Inactive', - 'active' => 'Active', - 'difference' => 'Difference', - 'in' => 'In', - 'out' => 'Out', - 'topX' => 'top :number', - 'show_full_list' => 'Show entire list', - 'show_only_top' => 'Show only top :number', - 'sum_of_year' => 'Sum of year', - 'sum_of_years' => 'Sum of years', - 'average_of_year' => 'Average of year', - 'average_of_years' => 'Average of years', - 'categories_earned_in_year' => 'Categories (by earnings)', - 'categories_spent_in_year' => 'Categories (by spendings)', - 'report_type' => 'Report type', - 'report_type_default' => 'Default financial report', - 'report_type_audit' => 'Transaction history overview (audit)', - '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', - 'report_date_range' => 'Date range', - 'report_preset_ranges' => 'Pre-set ranges', - 'shared' => 'Shared', - 'fiscal_year' => 'Fiscal year', - 'income_entry' => 'Income from account ":name" between :start and :end', - 'expense_entry' => 'Expenses to account ":name" between :start and :end', - 'category_entry' => 'Expenses in category ":name" between :start and :end', - 'budget_spent_amount' => 'Expenses in budget ":budget" between :start and :end', - 'balance_amount' => 'Expenses in budget ":budget" paid from account ":account" between :start and :end', - 'no_audit_activity' => 'No activity was recorded on account :account_name between :start and :end.', - 'audit_end_balance' => 'Account balance of :account_name at the end of :end was: :balance', - 'reports_extra_options' => 'Extra options', - 'report_has_no_extra_options' => 'This report has no extra options', - 'reports_submit' => 'View report', + 'report_default' => 'Default financial report for :start until :end', + 'report_audit' => 'Transaction history overview for :start until :end', + 'quick_link_reports' => 'Quick links', + 'quick_link_default_report' => 'Default financial report', + 'quick_link_audit_report' => 'Transaction history overview', + 'report_this_month_quick' => 'Current month, all accounts', + 'report_this_year_quick' => 'Current year, all accounts', + 'report_this_fiscal_year_quick' => 'Current fiscal year, all accounts', + 'report_all_time_quick' => 'All-time, all accounts', + 'reports_can_bookmark' => 'Remember that reports can be bookmarked.', + 'incomeVsExpenses' => 'Income vs. expenses', + 'accountBalances' => 'Account balances', + 'balanceStartOfYear' => 'Balance at start of year', + 'balanceEndOfYear' => 'Balance at end of year', + 'balanceStartOfMonth' => 'Balance at start of month', + 'balanceEndOfMonth' => 'Balance at end of month', + 'balanceStart' => 'Balance at start of period', + 'balanceEnd' => 'Balance at end of period', + 'reportsOwnAccounts' => 'Reports for your own accounts', + 'reportsOwnAccountsAndShared' => 'Reports for your own accounts and shared accounts', + 'splitByAccount' => 'Split by account', + 'balancedByTransfersAndTags' => 'Balanced by transfers and tags', + 'coveredWithTags' => 'Covered with tags', + 'leftUnbalanced' => 'Left unbalanced', + 'expectedBalance' => 'Expected balance', + 'outsideOfBudgets' => 'Outside of budgets', + 'leftInBudget' => 'Left in budget', + 'sumOfSums' => 'Sum of sums', + 'noCategory' => '(no category)', + 'notCharged' => 'Not charged (yet)', + 'inactive' => 'Inactive', + 'active' => 'Active', + 'difference' => 'Difference', + 'in' => 'In', + 'out' => 'Out', + 'topX' => 'top :number', + 'show_full_list' => 'Show entire list', + 'show_only_top' => 'Show only top :number', + 'sum_of_year' => 'Sum of year', + 'sum_of_years' => 'Sum of years', + 'average_of_year' => 'Average of year', + 'average_of_years' => 'Average of years', + 'categories_earned_in_year' => 'Categories (by earnings)', + 'categories_spent_in_year' => 'Categories (by spendings)', + 'report_type' => 'Report type', + 'report_type_default' => 'Default financial report', + 'report_type_audit' => 'Transaction history overview (audit)', + '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', + 'report_date_range' => 'Date range', + 'report_preset_ranges' => 'Pre-set ranges', + 'shared' => 'Shared', + 'fiscal_year' => 'Fiscal year', + 'income_entry' => 'Income from account ":name" between :start and :end', + 'expense_entry' => 'Expenses to account ":name" between :start and :end', + 'category_entry' => 'Expenses in category ":name" between :start and :end', + 'budget_spent_amount' => 'Expenses in budget ":budget" between :start and :end', + 'balance_amount' => 'Expenses in budget ":budget" paid from account ":account" between :start and :end', + 'no_audit_activity' => 'No activity was recorded on account :account_name between :start and :end.', + 'audit_end_balance' => 'Account balance of :account_name at the end of :end was: :balance', + 'reports_extra_options' => 'Extra options', + 'report_has_no_extra_options' => 'This report has no extra options', + 'reports_submit' => 'View report', // charts: - 'chart' => 'Chart', - 'dayOfMonth' => 'Day of the month', - 'month' => 'Month', - 'budget' => 'Budget', - 'spent' => 'Spent', - 'earned' => 'Earned', - 'overspent' => 'Overspent', - 'left' => 'Left', - 'no_budget' => '(no budget)', - 'maxAmount' => 'Maximum amount', - 'minAmount' => 'Minumum amount', - 'billEntry' => 'Current bill entry', - 'name' => 'Name', - 'date' => 'Date', - 'paid' => 'Paid', - 'unpaid' => 'Unpaid', - 'day' => 'Day', - 'budgeted' => 'Budgeted', - 'period' => 'Period', - 'balance' => 'Balance', - 'summary' => 'Summary', - 'sum' => 'Sum', - 'average' => 'Average', - 'balanceFor' => 'Balance for :name', + 'chart' => 'Chart', + 'dayOfMonth' => 'Day of the month', + 'month' => 'Month', + 'budget' => 'Budget', + 'spent' => 'Spent', + 'earned' => 'Earned', + 'overspent' => 'Overspent', + 'left' => 'Left', + 'no_budget' => '(no budget)', + 'maxAmount' => 'Maximum amount', + 'minAmount' => 'Minumum amount', + 'billEntry' => 'Current bill entry', + 'name' => 'Name', + 'date' => 'Date', + 'paid' => 'Paid', + 'unpaid' => 'Unpaid', + 'day' => 'Day', + 'budgeted' => 'Budgeted', + 'period' => 'Period', + 'balance' => 'Balance', + 'summary' => 'Summary', + 'sum' => 'Sum', + 'average' => 'Average', + 'balanceFor' => 'Balance for :name', // piggy banks: - 'add_money_to_piggy' => 'Add money to piggy bank ":name"', - 'piggy_bank' => 'Piggy bank', - 'new_piggy_bank' => 'Create new piggy bank', - 'store_piggy_bank' => 'Store new piggy bank', - 'stored_piggy_bank' => 'Store new piggy bank ":name"', - 'account_status' => 'Account status', - 'left_for_piggy_banks' => 'Left for piggy banks', - 'sum_of_piggy_banks' => 'Sum of piggy banks', - 'saved_so_far' => 'Saved so far', - 'left_to_save' => 'Left to save', - 'add_money_to_piggy_title' => 'Add money to piggy bank ":name"', - 'remove_money_from_piggy_title' => 'Remove money from piggy bank ":name"', - 'add' => 'Add', + 'add_money_to_piggy' => 'Add money to piggy bank ":name"', + 'piggy_bank' => 'Piggy bank', + 'new_piggy_bank' => 'Create new piggy bank', + 'store_piggy_bank' => 'Store new piggy bank', + 'stored_piggy_bank' => 'Store new piggy bank ":name"', + 'account_status' => 'Account status', + 'left_for_piggy_banks' => 'Left for piggy banks', + 'sum_of_piggy_banks' => 'Sum of piggy banks', + 'saved_so_far' => 'Saved so far', + 'left_to_save' => 'Left to save', + 'add_money_to_piggy_title' => 'Add money to piggy bank ":name"', + 'remove_money_from_piggy_title' => 'Remove money from piggy bank ":name"', + 'add' => 'Add', 'remove' => 'Remove', 'max_amount_add' => 'The maximum amount you can add is', From b50e5d7e5933733aa53a629f5dce76c4152cad48 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 29 Oct 2016 09:22:51 +0200 Subject: [PATCH 005/709] Can also change destination in new rule. --- app/Rules/Actions/SetDestinationAccount.php | 141 ++++++++++++++++++++ config/firefly.php | 4 +- resources/lang/en_US/firefly.php | 1 + 3 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 app/Rules/Actions/SetDestinationAccount.php diff --git a/app/Rules/Actions/SetDestinationAccount.php b/app/Rules/Actions/SetDestinationAccount.php new file mode 100644 index 0000000000..4f6b4d28d5 --- /dev/null +++ b/app/Rules/Actions/SetDestinationAccount.php @@ -0,0 +1,141 @@ +action = $action; + } + + /** + * @param TransactionJournal $journal + * + * @return bool + */ + public function act(TransactionJournal $journal): bool + { + $this->journal = $journal; + $this->repository = app(AccountRepositoryInterface::class, [$journal->user]); + $count = $journal->transactions()->count(); + if ($count > 2) { + Log::error(sprintf('Cannot change destination account of journal #%d because it is a split journal.', $journal->id)); + + return true; + } + + // journal type: + $type = $journal->transactionType->type; + + // if this is a deposit or a transfer, the destination account must be an asset account or a default account, and it MUST exist: + if (($type === TransactionType::DEPOSIT || $type === TransactionType::TRANSFER) && !$this->findAssetAccount()) { + Log::error( + sprintf( + 'Cannot change destination account of journal #%d because no asset account with name "%s" exists.', + $journal->id, $this->action->action_value + ) + ); + + return true; + } + + // if this is a withdrawal, the new destination account must be a expense account and may be created: + if ($type === TransactionType::WITHDRAWAL) { + $this->findExpenseAccount(); + } + + Log::debug(sprintf('New destination account is #%d ("%s").', $this->newDestinationAccount->id, $this->newDestinationAccount->name)); + + // update destination transaction with new destination account: + // get destination transaction: + $transaction = $journal->transactions()->where('amount', '>', 0)->first(); + $transaction->account_id = $this->newDestinationAccount->id; + $transaction->save(); + Log::debug(sprintf('Updated transaction #%d and gave it new account ID.', $transaction->id)); + + return true; + } + + /** + * @return bool + */ + private function findAssetAccount(): bool + { + $account = $this->repository->findByName($this->action->action_value, [AccountType::DEFAULT, AccountType::ASSET]); + + if (is_null($account->id)) { + Log::debug(sprintf('There is NO asset account called "%s".', $this->action->action_value)); + + return false; + } + Log::debug(sprintf('There exists an asset account called "%s". ID is #%d', $this->action->action_value, $account->id)); + $this->newDestinationAccount = $account; + + return true; + } + + /** + * + */ + private function findExpenseAccount() + { + $account = $this->repository->findByName($this->action->action_value, [AccountType::REVENUE]); + if (is_null($account->id)) { + // create new revenue account with this name: + $data = [ + 'name' => $this->action->action_value, + 'accountType' => 'expense', + 'virtualBalance' => 0, + 'active' => true, + 'iban' => null, + ]; + $account = $this->repository->store($data); + } + Log::debug(sprintf('Found or created expense account #%d ("%s")', $account->id, $account->name)); + $this->newDestinationAccount = $account; + } +} diff --git a/config/firefly.php b/config/firefly.php index f93f5fe6f5..9d5113793b 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -185,8 +185,8 @@ return [ 'append_description' => 'FireflyIII\Rules\Actions\AppendDescription', 'prepend_description' => 'FireflyIII\Rules\Actions\PrependDescription', - 'set_source_account' => 'FireflyIII\Rules\Actions\SetSourceAccount', - //'set_destination_account' => 'FireflyIII\Rules\Actions\SetDestinationAccount', + 'set_source_account' => 'FireflyIII\Rules\Actions\SetSourceAccount', + 'set_destination_account' => 'FireflyIII\Rules\Actions\SetDestinationAccount', ], 'rule-actions-text' => [ 'set_category', diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index a2a639d8e5..cd3074e301 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -241,6 +241,7 @@ return [ 'rule_action_set_source_account_choice' => 'Set source account to...', 'rule_action_set_source_account' => 'Set source account to :action_value', 'rule_action_set_destination_account_choice' => 'Set destination account to...', + 'rule_action_set_destination_account' => 'Set destination account to :action_value', // tags 'store_new_tag' => 'Store new tag', 'update_tag' => 'Update tag', From ba65e982fdffe04b2fc404c4601641f21d41698d Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 29 Oct 2016 11:42:04 +0200 Subject: [PATCH 006/709] Forgot to include model [skip ci] --- app/Repositories/Journal/JournalTasker.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Repositories/Journal/JournalTasker.php b/app/Repositories/Journal/JournalTasker.php index a51f356a28..26e3c63274 100644 --- a/app/Repositories/Journal/JournalTasker.php +++ b/app/Repositories/Journal/JournalTasker.php @@ -16,6 +16,7 @@ namespace FireflyIII\Repositories\Journal; use Carbon\Carbon; use Crypt; use DB; +use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\User; From 39749aa11392dac4b900921b96a263b9740ca218 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 29 Oct 2016 15:14:33 +0200 Subject: [PATCH 007/709] First code set for #330 --- .../Transaction/ConvertController.php | 108 ++++++++++ app/Http/breadcrumbs.php | 16 +- app/Models/TransactionType.php | 20 ++ config/firefly.php | 1 + resources/lang/en_US/firefly.php | 16 ++ resources/lang/en_US/form.php | 184 ++++++++++-------- resources/views/transactions/convert.twig | 176 +++++++++++++++++ resources/views/transactions/show.twig | 38 +++- routes/web.php | 6 +- 9 files changed, 473 insertions(+), 92 deletions(-) create mode 100644 app/Http/Controllers/Transaction/ConvertController.php create mode 100644 resources/views/transactions/convert.twig diff --git a/app/Http/Controllers/Transaction/ConvertController.php b/app/Http/Controllers/Transaction/ConvertController.php new file mode 100644 index 0000000000..4b47b22714 --- /dev/null +++ b/app/Http/Controllers/Transaction/ConvertController.php @@ -0,0 +1,108 @@ +middleware( + function ($request, $next) { + $this->accounts = app(AccountRepositoryInterface::class); + + View::share('title', trans('firefly.transactions')); + View::share('mainTitleIcon', 'fa-exchange'); + + return $next($request); + } + ); + } + + /** + * @param TransactionType $destinationType + * @param TransactionJournal $journal + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View + */ + public function convert(TransactionType $destinationType, TransactionJournal $journal) + { + $positiveAmount = TransactionJournal::amountPositive($journal); + $assetAccounts = ExpandedForm::makeSelectList($this->accounts->getActiveAccountsByType([AccountType::DEFAULT, AccountType::ASSET])); + $sourceType = $journal->transactionType; + + $subTitle = trans('firefly.convert_to_' . $destinationType->type, ['description' => $journal->description]); + $subTitleIcon = 'fa-exchange'; + + if ($sourceType->type === $destinationType->type) { + Session::flash('info', trans('firefly.convert_is_already_type_' . $destinationType->type)); + + return redirect(route('transactions.show', [$journal->id])); + } + if ($journal->transactions()->count() > 2) { + Session::flash('error', trans('firefly.cannot_convert_split_journl')); + + return redirect(route('transactions.show', [$journal->id])); + } + $sourceAccount = TransactionJournal::sourceAccountList($journal)->first(); + $destinationAccount = TransactionJournal::destinationAccountList($journal)->first(); + + return view( + 'transactions.convert', compact( + 'sourceType', 'destinationType', 'journal', 'assetAccounts', + 'positiveAmount', 'sourceAccount', 'destinationAccount', 'sourceType', + 'subTitle', 'subTitleIcon' + + ) + ); + + + // convert withdrawal to deposit requires a new source account () + // or to transfer requires + } + + public function submit(Request $request) + { + echo '
';
+
+        var_dump($request->all());
+
+
+        exit;
+    }
+
+}
\ No newline at end of file
diff --git a/app/Http/breadcrumbs.php b/app/Http/breadcrumbs.php
index 44c4fc707e..9de3b720ac 100644
--- a/app/Http/breadcrumbs.php
+++ b/app/Http/breadcrumbs.php
@@ -25,6 +25,7 @@ use FireflyIII\Models\RuleGroup;
 use FireflyIII\Models\Tag;
 use FireflyIII\Models\TransactionCurrency;
 use FireflyIII\Models\TransactionJournal;
+use FireflyIII\Models\TransactionType;
 use FireflyIII\User;
 
 /**
@@ -595,13 +596,24 @@ Breadcrumbs::register(
 Breadcrumbs::register(
     'transactions.show', function (BreadCrumbGenerator $breadcrumbs, TransactionJournal $journal) {
 
-    $what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
+    $what = strtolower($journal->transactionType->type);
     $breadcrumbs->parent('transactions.index', $what);
     $breadcrumbs->push($journal->description, route('transactions.show', [$journal->id]));
-
 }
 );
 
+Breadcrumbs::register(
+    'transactions.convert', function (BreadCrumbGenerator $breadcrumbs, TransactionType $destinationType, TransactionJournal $journal) {
+
+    $breadcrumbs->parent('transactions.show', $journal);
+    $breadcrumbs->push(
+        trans('firefly.convert_to_' . $destinationType->type, ['description' => $journal->description]),
+        route('transactions.convert', [strtolower($destinationType->type), $journal->id])
+    );
+}
+);
+
+
 /**
  * SPLIT
  */
diff --git a/app/Models/TransactionType.php b/app/Models/TransactionType.php
index 84a1da7ccf..51a912ca12 100644
--- a/app/Models/TransactionType.php
+++ b/app/Models/TransactionType.php
@@ -15,6 +15,7 @@ namespace FireflyIII\Models;
 
 use Illuminate\Database\Eloquent\Model;
 use Illuminate\Database\Eloquent\SoftDeletes;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
 /**
  * FireflyIII\Models\TransactionType
@@ -43,6 +44,25 @@ class TransactionType extends Model
 
     protected $dates = ['created_at', 'updated_at', 'deleted_at'];
 
+    /**
+     * @param string $type
+     *
+     * @return Model|null|static
+     */
+    public static function routeBinder(string $type)
+    {
+        if (!auth()->check()) {
+            throw new NotFoundHttpException;
+        }
+        $transactionType = self::where('type', $type)->first();
+        if (!is_null($transactionType)) {
+            return $transactionType;
+        }
+        throw new NotFoundHttpException;
+
+    }
+
+
     /**
      * @return bool
      */
diff --git a/config/firefly.php b/config/firefly.php
index 9d5113793b..69b5e13029 100644
--- a/config/firefly.php
+++ b/config/firefly.php
@@ -137,6 +137,7 @@ return [
         'bill'              => 'FireflyIII\Models\Bill',
         'budget'            => 'FireflyIII\Models\Budget',
         'category'          => 'FireflyIII\Models\Category',
+        'transaction_type'  => 'FireflyIII\Models\TransactionType',
         'currency'          => 'FireflyIII\Models\TransactionCurrency',
         'limitrepetition'   => 'FireflyIII\Models\LimitRepetition',
         'piggyBank'         => 'FireflyIII\Models\PiggyBank',
diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php
index cd3074e301..b1d1d18a1a 100644
--- a/resources/lang/en_US/firefly.php
+++ b/resources/lang/en_US/firefly.php
@@ -242,6 +242,7 @@ return [
     'rule_action_set_source_account'             => 'Set source account to :action_value',
     'rule_action_set_destination_account_choice' => 'Set destination account to...',
     'rule_action_set_destination_account'        => 'Set destination account to :action_value',
+
     // tags
     'store_new_tag'                              => 'Store new tag',
     'update_tag'                                 => 'Update tag',
@@ -352,6 +353,21 @@ return [
     'title_transfer'                             => 'Transfers',
     'title_transfers'                            => 'Transfers',
 
+    // convert stuff:
+    'convert_is_already_type_Withdrawal'         => 'This transaction is already a withdrawal',
+    'convert_is_already_type_Deposit'            => 'This transaction is already a deposit',
+    'convert_is_already_type_Transfer'           => 'This transaction is already a transfer',
+    'convert_to_Withdrawal'                      => 'Convert ":description" to a withdrawal',
+    'convert_to_Deposit'                         => 'Convert ":description" to a deposit',
+    'convert_to_Transfer'                        => 'Convert ":description" to a transfer',
+    'convert_options_WithdrawalDeposit'          => 'Convert a withdrawal into a deposit',
+    'convert_options_WithdrawalTransfer'         => 'Convert a withdrawal into a transfer',
+    'convert_options_DepositTransfer'            => 'Convert a deposit into a transfer',
+    'convert_options_DepositWithdrawal'          => 'Convert a deposit into a withdrawal',
+    'convert_options_TransferWithdrawal'         => 'Convert a transfer into a withdrawal',
+    'convert_options_TransferDeposit'            => 'Convert a transfer into a deposit',
+
+
     // create new stuff:
     'create_new_withdrawal'                      => 'Create new withdrawal',
     'create_new_deposit'                         => 'Create new deposit',
diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php
index 5cfd1a3688..cb264972d0 100644
--- a/resources/lang/en_US/form.php
+++ b/resources/lang/en_US/form.php
@@ -31,6 +31,8 @@ return [
     'journal_source_account_id'      => 'Asset account (source)',
     'account_from_id'                => 'From account',
     'account_to_id'                  => 'To account',
+    'source_account'                 => 'Source account',
+    'destination_account'            => 'Destination account',
     'journal_destination_account_id' => 'Asset account (destination)',
     'asset_destination_account'      => 'Asset account (destination)',
     'asset_source_account'           => 'Asset account (source)',
@@ -58,95 +60,107 @@ return [
     'description'                    => 'Description',
     'expense_account'                => 'Expense account',
     'revenue_account'                => 'Revenue account',
-    'amount'                         => 'Amount',
-    'date'                           => 'Date',
-    'interest_date'                  => 'Interest date',
-    'book_date'                      => 'Book date',
-    'process_date'                   => 'Processing date',
-    'category'                       => 'Category',
-    'tags'                           => 'Tags',
-    'deletePermanently'              => 'Delete permanently',
-    'cancel'                         => 'Cancel',
-    'targetdate'                     => 'Target date',
-    'tag'                            => 'Tag',
-    'under'                          => 'Under',
-    'symbol'                         => 'Symbol',
-    'code'                           => 'Code',
-    'iban'                           => 'IBAN',
-    'accountNumber'                  => 'Account number',
-    'has_headers'                    => 'Headers',
-    'date_format'                    => 'Date format',
-    'specifix'                       => 'Bank- or file specific fixes',
-    'attachments[]'                  => 'Attachments',
-    'store_new_withdrawal'           => 'Store new withdrawal',
-    'store_new_deposit'              => 'Store new deposit',
-    'store_new_transfer'             => 'Store new transfer',
-    'add_new_withdrawal'             => 'Add a new withdrawal',
-    'add_new_deposit'                => 'Add a new deposit',
-    'add_new_transfer'               => 'Add a new transfer',
-    'noPiggybank'                    => '(no piggy bank)',
-    'title'                          => 'Title',
-    'notes'                          => 'Notes',
-    'filename'                       => 'File name',
-    'mime'                           => 'Mime type',
-    'size'                           => 'Size',
-    'trigger'                        => 'Trigger',
-    'stop_processing'                => 'Stop processing',
-    'start_date'                     => 'Start of range',
-    'end_date'                       => 'End of range',
-    'export_start_range'             => 'Start of export range',
-    'export_end_range'               => 'End of export range',
-    'export_format'                  => 'File format',
-    'include_attachments'            => 'Include uploaded attachments',
-    'include_old_uploads'            => 'Include imported data',
-    'accounts'                       => 'Export transactions from these accounts',
-    'delete_account'                 => 'Delete account ":name"',
-    'delete_bill'                    => 'Delete bill ":name"',
-    'delete_budget'                  => 'Delete budget ":name"',
-    'delete_category'                => 'Delete category ":name"',
-    'delete_currency'                => 'Delete currency ":name"',
-    'delete_journal'                 => 'Delete transaction with description ":description"',
-    'delete_attachment'              => 'Delete attachment ":name"',
-    'delete_rule'                    => 'Delete rule ":title"',
-    'delete_rule_group'              => 'Delete rule group ":title"',
-    'attachment_areYouSure'          => 'Are you sure you want to delete the attachment named ":name"?',
-    'account_areYouSure'             => 'Are you sure you want to delete the account named ":name"?',
-    'bill_areYouSure'                => 'Are you sure you want to delete the bill named ":name"?',
-    'rule_areYouSure'                => 'Are you sure you want to delete the rule titled ":title"?',
-    'ruleGroup_areYouSure'           => 'Are you sure you want to delete the rule group titled ":title"?',
-    'budget_areYouSure'              => 'Are you sure you want to delete the budget named ":name"?',
-    'category_areYouSure'            => 'Are you sure you want to delete the category named ":name"?',
-    'currency_areYouSure'            => 'Are you sure you want to delete the currency named ":name"?',
-    'piggyBank_areYouSure'           => 'Are you sure you want to delete the piggy bank named ":name"?',
-    'journal_areYouSure'             => 'Are you sure you want to delete the transaction described ":description"?',
-    'mass_journal_are_you_sure'      => 'Are you sure you want to delete these transactions?',
-    'tag_areYouSure'                 => 'Are you sure you want to delete the tag ":tag"?',
-    'permDeleteWarning'              => 'Deleting stuff from Firely is permanent and cannot be undone.',
-    'mass_make_selection'            => 'You can still prevent items from being deleted by removing the checkbox.',
-    'delete_all_permanently'         => 'Delete selected permanently',
-    'update_all_journals'            => 'Update these transactions',
-    'also_delete_transactions'       => 'The only transaction connected to this account will be deleted as well.|All :count transactions connected to this account will be deleted as well.',
-    'also_delete_rules'              => 'The only rule connected to this rule group will be deleted as well.|All :count rules connected to this rule group will be deleted as well.',
-    'also_delete_piggyBanks'         => 'The only piggy bank connected to this account will be deleted as well.|All :count piggy bank connected to this account will be deleted as well.',
-    'bill_keep_transactions'         => 'The only transaction connected to this bill will not be deleted.|All :count transactions connected to this bill will spared deletion.',
-    'budget_keep_transactions'       => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will spared deletion.',
-    'category_keep_transactions'     => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will spared deletion.',
-    'tag_keep_transactions'          => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will spared deletion.',
+
+    'revenue_account_source'      => 'Revenue account (source)',
+    'source_account_asset'        => 'Source account (asset account)',
+    'destination_account_expense' => 'Destination account (expense account)',
+    'destination_account_asset' => 'Destination account (asset account)',
+    'source_account_revenue'      => 'Source account (revenue account)',
+    'type'                        => 'Type',
+    'convert_Withdrawal'          => 'Convert withdrawal',
+    'convert_Deposit'          => 'Convert deposit',
+    'convert_Transfer'          => 'Convert transfer',
+
+
+    'amount'                     => 'Amount',
+    'date'                       => 'Date',
+    'interest_date'              => 'Interest date',
+    'book_date'                  => 'Book date',
+    'process_date'               => 'Processing date',
+    'category'                   => 'Category',
+    'tags'                       => 'Tags',
+    'deletePermanently'          => 'Delete permanently',
+    'cancel'                     => 'Cancel',
+    'targetdate'                 => 'Target date',
+    'tag'                        => 'Tag',
+    'under'                      => 'Under',
+    'symbol'                     => 'Symbol',
+    'code'                       => 'Code',
+    'iban'                       => 'IBAN',
+    'accountNumber'              => 'Account number',
+    'has_headers'                => 'Headers',
+    'date_format'                => 'Date format',
+    'specifix'                   => 'Bank- or file specific fixes',
+    'attachments[]'              => 'Attachments',
+    'store_new_withdrawal'       => 'Store new withdrawal',
+    'store_new_deposit'          => 'Store new deposit',
+    'store_new_transfer'         => 'Store new transfer',
+    'add_new_withdrawal'         => 'Add a new withdrawal',
+    'add_new_deposit'            => 'Add a new deposit',
+    'add_new_transfer'           => 'Add a new transfer',
+    'noPiggybank'                => '(no piggy bank)',
+    'title'                      => 'Title',
+    'notes'                      => 'Notes',
+    'filename'                   => 'File name',
+    'mime'                       => 'Mime type',
+    'size'                       => 'Size',
+    'trigger'                    => 'Trigger',
+    'stop_processing'            => 'Stop processing',
+    'start_date'                 => 'Start of range',
+    'end_date'                   => 'End of range',
+    'export_start_range'         => 'Start of export range',
+    'export_end_range'           => 'End of export range',
+    'export_format'              => 'File format',
+    'include_attachments'        => 'Include uploaded attachments',
+    'include_old_uploads'        => 'Include imported data',
+    'accounts'                   => 'Export transactions from these accounts',
+    'delete_account'             => 'Delete account ":name"',
+    'delete_bill'                => 'Delete bill ":name"',
+    'delete_budget'              => 'Delete budget ":name"',
+    'delete_category'            => 'Delete category ":name"',
+    'delete_currency'            => 'Delete currency ":name"',
+    'delete_journal'             => 'Delete transaction with description ":description"',
+    'delete_attachment'          => 'Delete attachment ":name"',
+    'delete_rule'                => 'Delete rule ":title"',
+    'delete_rule_group'          => 'Delete rule group ":title"',
+    'attachment_areYouSure'      => 'Are you sure you want to delete the attachment named ":name"?',
+    'account_areYouSure'         => 'Are you sure you want to delete the account named ":name"?',
+    'bill_areYouSure'            => 'Are you sure you want to delete the bill named ":name"?',
+    'rule_areYouSure'            => 'Are you sure you want to delete the rule titled ":title"?',
+    'ruleGroup_areYouSure'       => 'Are you sure you want to delete the rule group titled ":title"?',
+    'budget_areYouSure'          => 'Are you sure you want to delete the budget named ":name"?',
+    'category_areYouSure'        => 'Are you sure you want to delete the category named ":name"?',
+    'currency_areYouSure'        => 'Are you sure you want to delete the currency named ":name"?',
+    'piggyBank_areYouSure'       => 'Are you sure you want to delete the piggy bank named ":name"?',
+    'journal_areYouSure'         => 'Are you sure you want to delete the transaction described ":description"?',
+    'mass_journal_are_you_sure'  => 'Are you sure you want to delete these transactions?',
+    'tag_areYouSure'             => 'Are you sure you want to delete the tag ":tag"?',
+    'permDeleteWarning'          => 'Deleting stuff from Firely is permanent and cannot be undone.',
+    'mass_make_selection'        => 'You can still prevent items from being deleted by removing the checkbox.',
+    'delete_all_permanently'     => 'Delete selected permanently',
+    'update_all_journals'        => 'Update these transactions',
+    'also_delete_transactions'   => 'The only transaction connected to this account will be deleted as well.|All :count transactions connected to this account will be deleted as well.',
+    'also_delete_rules'          => 'The only rule connected to this rule group will be deleted as well.|All :count rules connected to this rule group will be deleted as well.',
+    'also_delete_piggyBanks'     => 'The only piggy bank connected to this account will be deleted as well.|All :count piggy bank connected to this account will be deleted as well.',
+    'bill_keep_transactions'     => 'The only transaction connected to this bill will not be deleted.|All :count transactions connected to this bill will spared deletion.',
+    'budget_keep_transactions'   => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will spared deletion.',
+    'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will spared deletion.',
+    'tag_keep_transactions'      => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will spared deletion.',
 
     // admin
-    'domain'                         => 'Domain',
-    'single_user_mode'               => 'Single user mode',
+    'domain'                     => 'Domain',
+    'single_user_mode'           => 'Single user mode',
 
     // import
-    'import_file'                    => 'Import file',
-    'configuration_file'             => 'Configuration file',
-    'import_file_type'               => 'Import file type',
-    'csv_comma'                      => 'A comma (,)',
-    'csv_semicolon'                  => 'A semicolon (;)',
-    'csv_tab'                        => 'A tab (invisible)',
-    'csv_delimiter'                  => 'CSV field delimiter',
-    'csv_import_account'             => 'Default import account',
-    'csv_config'                     => 'CSV import configuration',
+    'import_file'                => 'Import file',
+    'configuration_file'         => 'Configuration file',
+    'import_file_type'           => 'Import file type',
+    'csv_comma'                  => 'A comma (,)',
+    'csv_semicolon'              => 'A semicolon (;)',
+    'csv_tab'                    => 'A tab (invisible)',
+    'csv_delimiter'              => 'CSV field delimiter',
+    'csv_import_account'         => 'Default import account',
+    'csv_config'                 => 'CSV import configuration',
 
 
     'due_date'           => 'Due date',
diff --git a/resources/views/transactions/convert.twig b/resources/views/transactions/convert.twig
new file mode 100644
index 0000000000..c7617480a6
--- /dev/null
+++ b/resources/views/transactions/convert.twig
@@ -0,0 +1,176 @@
+{% extends "./layout/default.twig" %}
+
+{% block breadcrumbs %}
+    {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, destinationType, journal) }}
+{% endblock %}
+
+{% block content %}
+    
+ +
+
+
+
+

{{ ('convert_options_'~sourceType.type~destinationType.type)|_ }}

+
+
+ {{ ExpandedForm.staticText('type', sourceType.type|_) }} + {{ ExpandedForm.staticText('description', ''~journal.description~'') }} + {{ ExpandedForm.staticText('date', journal.date.formatLocalized(monthAndDayFormat)) }} + + {# in case of withdrawal #} + {% if sourceType.type == "Withdrawal" %} + {{ ExpandedForm.staticText('source_account_asset', ''~sourceAccount.name~'') }} + {{ ExpandedForm.staticText('destination_account_expense', ''~destinationAccount.name~'') }} + {% endif %} + + {# in case of deposit #} + {% if sourceType.type == "Deposit" %} + {{ ExpandedForm.staticText('source_account_revenue', ''~sourceAccount.name~'') }} + {{ ExpandedForm.staticText('destination_account_asset', ''~destinationAccount.name~'') }} + {% endif %} + + {# in case of transfer #} + {% if sourceType.type == "Transfer" %} + {{ ExpandedForm.staticText('source_account_asset', ''~sourceAccount.name~'') }} + {{ ExpandedForm.staticText('destination_account_asset', ''~destinationAccount.name~'') }} + {% endif %} + + {# ONE #} + {% if sourceType.type == 'Withdrawal' and destinationType.type == 'Deposit' %} +

+ If you convert this withdrawal into a deposit, {{ positiveAmount|formatAmount }} + will be deposited into {{ sourceAccount.name }} + instead of taken from it. + +

+

+ Please pick the revenue account where the money will come from. + +

+ + {{ ExpandedForm.text('source_account_revenue', destinationAccount.name) }} + {% endif %} + + {# TWO #} + {% if sourceType.type == 'Withdrawal' and destinationType.type == 'Transfer' %} +

+ If you convert this withdrawal into a transfer, {{ positiveAmount|formatAmount }} + will be transferred from {{ sourceAccount.name }} + to a new asset account, instead of being paid to + {{ destinationAccount.name }}. +

+ +

+ + Please pick the asset account where the money will go to. + +

+ {{ ExpandedForm.select('destination_account_asset', assetAccounts) }} + + + {% endif %} + + {# THREE #} + {% if sourceType.type == 'Deposit' and destinationType.type == 'Withdrawal' %} +

+ + If you convert this deposit into a withdrawal, {{ positiveAmount|formatAmount }} + will be removed from {{ destinationAccount.name }} + instead of added to it. + +

+

+ + Please pick the expense account where the money will go to. + +

+ {{ ExpandedForm.text('destination_account_expense', destinationAccount.name) }} + + {% endif %} + + {# FOUR #} + {% if sourceType.type == 'Deposit' and destinationType.type == 'Transfer' %} + +

+ + If you convert this deposit into a transfer, {{ positiveAmount|formatAmount }} will be transferred + from an asset account of your choice into + {{ destinationAccount.name }}. + +

+

+ + Please pick the asset account where the money will come from. + +

+ + {{ ExpandedForm.select('source_account_asset', assetAccounts) }} + {% endif %} + + {# FIVE #} + {% if sourceType.type == 'Transfer' and destinationType.type == 'Withdrawal' %} + +

+ + If you convert this transfer into a withdrawal, {{ positiveAmount|formatAmount }} + will go from {{ sourceAccount.name }} + to a new destination as an expense, instead of to + {{ destinationAccount.name }} + as a transfer. + +

+ +

+ + Please pick the expense account where the money will go to. + +

+ + {{ ExpandedForm.text('destination_account_expense', destinationAccount.name) }} + + {% endif %} + + {# SIX #} + {% if sourceType.type == 'Transfer' and destinationType.type == 'Deposit' %} + +

+ + If you convert this transfer into a deposit, {{ positiveAmount|formatAmount }} + will be deposited into account {{ destinationAccount.name }} + instead of being transferred there. + +

+ +

+ + Please pick the revenue account where the money will come from. + +

+ + {{ ExpandedForm.text('source_account_revenue', sourceAccount.name) }} + + {% endif %} + +
+ +
+
+
+
+ +{% endblock %} +{% block scripts %} + + +{% endblock %} + +{% block styles %} + +{% endblock %} diff --git a/resources/views/transactions/show.twig b/resources/views/transactions/show.twig index 5a7e7f42d3..5a146f26be 100644 --- a/resources/views/transactions/show.twig +++ b/resources/views/transactions/show.twig @@ -169,8 +169,6 @@
- - {% if journal.attachments|length > 0 %}
@@ -210,10 +208,44 @@
{% endif %} + {% if transactions|length == 1 %} +
+
+

{{ 'transaction_journal_convert_options'|_ }}

+
+
+ {% if journal.transactionType.type != "Withdrawal" %} +

+ + + Convert this {{ journal.transactionType.type }} to a withdrawal. + +

+ {% endif %} + {% if journal.transactionType.type != "Deposit" %} +

+ + + Convert this {{ journal.transactionType.type }} to a deposit. + +

+ {% endif %} + + {% if journal.transactionType.type != "Transfer" %} +

+ + + Convert this {{ journal.transactionType.type }} to a transfer. + +

+ {% endif %} + +
+
+ {% endif %} -
diff --git a/routes/web.php b/routes/web.php index e53e81ccde..89284d06cc 100755 --- a/routes/web.php +++ b/routes/web.php @@ -284,10 +284,8 @@ Route::group( Route::post('/piggy-banks/store', ['uses' => 'PiggyBankController@store', 'as' => 'piggy-banks.store']); Route::post('/piggy-banks/update/{piggyBank}', ['uses' => 'PiggyBankController@update', 'as' => 'piggy-banks.update']); Route::post('/piggy-banks/destroy/{piggyBank}', ['uses' => 'PiggyBankController@destroy', 'as' => 'piggy-banks.destroy']); - Route::post('/piggy-banks/add/{piggyBank}', ['uses' => 'PiggyBankController@postAdd', 'as' => 'piggy-banks.add']); Route::post('/piggy-banks/remove/{piggyBank}', ['uses' => 'PiggyBankController@postRemove', 'as' => 'piggy-banks.remove']); - Route::post('/piggy-banks/sort', ['uses' => 'PiggyBankController@order', 'as' => 'piggy-banks.order']); /** @@ -425,6 +423,10 @@ Route::group( Route::get('/transaction/split/edit/{tj}', ['uses' => 'Transaction\SplitController@edit', 'as' => 'transactions.edit-split']); Route::post('/transaction/split/update/{tj}', ['uses' => 'Transaction\SplitController@update', 'as' => 'split.journal.update']); + // convert controller: + Route::get('transactions/convert/{transaction_type}/{tj}', ['uses' => 'Transaction\ConvertController@convert', 'as' => 'transactions.convert']); + Route::post('transactions/convert/{transaction_type}/{tj}', ['uses' => 'Transaction\ConvertController@submit', 'as' => 'transactions.convert.post']); + /** * POPUP Controllers */ From 35f179625cfd1454e087b415a21cb425c5a742b2 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 29 Oct 2016 16:11:54 +0200 Subject: [PATCH 008/709] New queries for #366 --- app/Http/Controllers/BudgetController.php | 30 +++-- app/Repositories/Budget/BudgetRepository.php | 112 +++++++++---------- 2 files changed, 69 insertions(+), 73 deletions(-) diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index fdbe4bfe7d..b2ed2af41f 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -26,6 +26,7 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Input; +use Log; use Navigation; use Preferences; use Response; @@ -199,10 +200,12 @@ class BudgetController extends Controller $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]); $startAsString = $start->format('Y-m-d'); $endAsString = $end->format('Y-m-d'); + Log::debug('Now at /budgets'); // loop the budgets: /** @var Budget $budget */ foreach ($budgets as $budget) { + Log::debug(sprintf('Now at budget #%d ("%s")', $budget->id, $budget->name)); $budget->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end); $allRepetitions = $repository->getAllBudgetLimitRepetitions($start, $end); $otherRepetitions = new Collection; @@ -290,13 +293,13 @@ class BudgetController extends Controller } /** - * @param BudgetRepositoryInterface $repository - * @param Budget $budget + * @param BudgetRepositoryInterface $repository + * @param AccountRepositoryInterface $accountRepository + * @param Budget $budget * * @return View - * @throws FireflyException */ - public function show(BudgetRepositoryInterface $repository, Budget $budget) + public function show(BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository, Budget $budget) { /** @var Carbon $start */ $start = session('first', Carbon::create()->startOfYear()); @@ -308,6 +311,7 @@ class BudgetController extends Controller $count = $journals->count(); $journals = $journals->slice($offset, $pageSize); $journals = new LengthAwarePaginator($journals, $count, $pageSize); + $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]); $journals->setPath('/budgets/show/' . $budget->id); @@ -318,7 +322,7 @@ class BudgetController extends Controller /** @var LimitRepetition $entry */ foreach ($set as $entry) { - $entry->spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $entry->startdate, $entry->enddate); + $entry->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $entry->startdate, $entry->enddate); $limits->push($entry); } @@ -326,15 +330,17 @@ class BudgetController extends Controller } /** - * @param BudgetRepositoryInterface $repository - * @param Budget $budget - * @param LimitRepetition $repetition + * @param BudgetRepositoryInterface $repository + * @param AccountRepositoryInterface $accountRepository + * @param Budget $budget + * @param LimitRepetition $repetition * * @return View * @throws FireflyException */ - public function showWithRepetition(BudgetRepositoryInterface $repository, Budget $budget, LimitRepetition $repetition) - { + public function showWithRepetition( + BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository, Budget $budget, LimitRepetition $repetition + ) { if ($repetition->budgetLimit->budget->id != $budget->id) { throw new FireflyException('This budget limit is not part of this budget.'); } @@ -348,11 +354,13 @@ class BudgetController extends Controller $journals = $journals->slice($offset, $pageSize); $journals = new LengthAwarePaginator($journals, $count, $pageSize); $subTitle = trans('firefly.budget_in_month', ['name' => $budget->name, 'month' => $repetition->startdate->formatLocalized($this->monthFormat)]); + $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]); + $journals->setPath('/budgets/show/' . $budget->id . '/' . $repetition->id); - $repetition->spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $repetition->startdate, $repetition->enddate); + $repetition->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $repetition->startdate, $repetition->enddate); $limits = new Collection([$repetition]); return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle')); diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index b674435881..3679b922c7 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -19,6 +19,7 @@ use FireflyIII\Events\UpdatedBudgetLimit; use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\LimitRepetition; +use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\User; @@ -347,79 +348,66 @@ class BudgetRepository implements BudgetRepositoryInterface */ public function spentInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end) : string { - // first collect actual transaction journals (fairly easy) - $query = $this->user - ->transactionJournals() + // collect amount of transaction journals, which is easy: + $budgetIds = $budgets->pluck('id')->toArray(); + $accountIds = $accounts->pluck('id')->toArray(); + $fromJournalsQuery = TransactionJournal + ::leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') + ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') ->leftJoin( - 'transactions as source', function (JoinClause $join) { - $join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', 0); + 'transactions', function (JoinClause $join) { + $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', '0'); } ) - ->leftJoin( - 'transactions as destination', function (JoinClause $join) { - $join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')->where('destination.amount', '>', 0); - } - ); - $query->whereNull('source.deleted_at'); - $query->whereNull('destination.deleted_at'); - $query->where('transaction_journals.completed', 1); + ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) + ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) + ->whereNull('transaction_journals.deleted_at') + ->whereNull('transactions.deleted_at') + ->where('transaction_journals.user_id', $this->user->id) + ->where('transaction_types.type', 'Withdrawal'); - if ($end >= $start) { - $query->before($end)->after($start); - } - if ($accounts->count() > 0) { - $accountIds = $accounts->pluck('id')->toArray(); - $query->where( - // source.account_id in accountIds XOR destination.account_id in accountIds - function (Builder $query) use ($accountIds) { - $query->where( - function (Builder $q1) use ($accountIds) { - $q1->whereIn('source.account_id', $accountIds) - ->whereNotIn('destination.account_id', $accountIds); - } - )->orWhere( - function (Builder $q2) use ($accountIds) { - $q2->whereIn('destination.account_id', $accountIds) - ->whereNotIn('source.account_id', $accountIds); - } - ); - } - ); - } + // add budgets: if ($budgets->count() > 0) { - $budgetIds = $budgets->pluck('id')->toArray(); - $query->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'); - $query->whereIn('budget_transaction_journal.budget_id', $budgetIds); - + $fromJournalsQuery->whereIn('budget_transaction_journal.budget_id', $budgetIds); } - // that should do it: - $ids = $query->distinct()->get(['transaction_journals.id'])->pluck('id')->toArray(); - $first = '0'; - if (count($ids) > 0) { - $first = strval( - $this->user->transactions() - ->whereIn('transaction_journal_id', $ids) - ->where('amount', '<', '0') - ->whereNull('transactions.deleted_at') - ->sum('amount') - ); - } - // then collection transactions (harder) - $query = $this->user->transactions() - ->where('transactions.amount', '<', 0) - ->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00')) - ->where('transaction_journals.date', '<=', $end->format('Y-m-d 23:59:59')); + // add accounts: if ($accounts->count() > 0) { - $accountIds = $accounts->pluck('id')->toArray(); - $query->whereIn('transactions.account_id', $accountIds); + $fromJournalsQuery->whereIn('transactions.account_id', $accountIds); } + $first = strval($fromJournalsQuery->sum('transactions.amount')); + unset($fromJournalsQuery); + + // collect amount from transactions: + /** + * select transactions.id, budget_transaction.budget_id , transactions.amount + * + * + * and budget_transaction.budget_id in (1,61) + * and transactions.account_id in (2) + */ + $fromTransactionsQuery = Transaction + ::leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'transactions.id') + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') + ->whereNull('transactions.deleted_at') + ->whereNull('transaction_journals.deleted_at') + ->where('transactions.amount', '<', 0) + ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) + ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) + ->where('transaction_journals.user_id', $this->user->id) + ->where('transaction_types.type', 'Withdrawal'); + + // add budgets: if ($budgets->count() > 0) { - $budgetIds = $budgets->pluck('id')->toArray(); - $query->leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'transactions.id'); - $query->whereIn('budget_transaction.budget_id', $budgetIds); + $fromTransactionsQuery->whereIn('budget_transaction.budget_id', $budgetIds); } - $second = strval($query->sum('transactions.amount')); + + // add accounts: + if ($accounts->count() > 0) { + $fromTransactionsQuery->whereIn('transactions.account_id', $accountIds); + } + $second = strval($fromTransactionsQuery->sum('transactions.amount')); return bcadd($first, $second); } From e9c2446cba35479624c45b913ed8ca9c49946f89 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 29 Oct 2016 16:16:10 +0200 Subject: [PATCH 009/709] Debug log for #366 --- app/Repositories/Budget/BudgetRepository.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 3679b922c7..5210d63ec8 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -27,6 +27,7 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; +use Log; /** * Class BudgetRepository @@ -351,6 +352,11 @@ class BudgetRepository implements BudgetRepositoryInterface // collect amount of transaction journals, which is easy: $budgetIds = $budgets->pluck('id')->toArray(); $accountIds = $accounts->pluck('id')->toArray(); + + Log::debug('spentInPeriod: Now in spentInPeriod for these budgets: ', $budgetIds); + Log::debug('spentInPeriod: and these accounts: ', $accountIds); + Log::debug(sprintf('spentInPeriod: Start date is "%s", end date is "%s"', $start->format('Y-m-d'), $end->format('Y-m-d'))); + $fromJournalsQuery = TransactionJournal ::leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') @@ -376,6 +382,7 @@ class BudgetRepository implements BudgetRepositoryInterface $fromJournalsQuery->whereIn('transactions.account_id', $accountIds); } $first = strval($fromJournalsQuery->sum('transactions.amount')); + Log::debug(sprintf('spentInPeriod: Result from first query: %s', $first)); unset($fromJournalsQuery); // collect amount from transactions: @@ -408,6 +415,9 @@ class BudgetRepository implements BudgetRepositoryInterface $fromTransactionsQuery->whereIn('transactions.account_id', $accountIds); } $second = strval($fromTransactionsQuery->sum('transactions.amount')); + Log::debug(sprintf('spentInPeriod: Result from second query: %s', $second)); + + Log::debug(sprintf('spentInPeriod: FINAL: %s', bcadd($first, $second))); return bcadd($first, $second); } From c9f14da2946eb0e4467a0c76a5e699be14038584 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 29 Oct 2016 17:30:55 +0200 Subject: [PATCH 010/709] Translations. --- .../Transaction/ConvertController.php | 50 ++++++++-- resources/lang/en_US/firefly.php | 19 +++- resources/views/transactions/convert.twig | 91 +++++++++++++------ resources/views/transactions/show.twig | 8 +- 4 files changed, 126 insertions(+), 42 deletions(-) diff --git a/app/Http/Controllers/Transaction/ConvertController.php b/app/Http/Controllers/Transaction/ConvertController.php index 4b47b22714..c66f5421c5 100644 --- a/app/Http/Controllers/Transaction/ConvertController.php +++ b/app/Http/Controllers/Transaction/ConvertController.php @@ -64,30 +64,35 @@ class ConvertController extends Controller $positiveAmount = TransactionJournal::amountPositive($journal); $assetAccounts = ExpandedForm::makeSelectList($this->accounts->getActiveAccountsByType([AccountType::DEFAULT, AccountType::ASSET])); $sourceType = $journal->transactionType; + $subTitle = trans('firefly.convert_to_' . $destinationType->type, ['description' => $journal->description]); + $subTitleIcon = 'fa-exchange'; - $subTitle = trans('firefly.convert_to_' . $destinationType->type, ['description' => $journal->description]); - $subTitleIcon = 'fa-exchange'; - + // cannot convert to its own type. if ($sourceType->type === $destinationType->type) { Session::flash('info', trans('firefly.convert_is_already_type_' . $destinationType->type)); return redirect(route('transactions.show', [$journal->id])); } + + // cannot convert split. if ($journal->transactions()->count() > 2) { Session::flash('error', trans('firefly.cannot_convert_split_journl')); return redirect(route('transactions.show', [$journal->id])); } + + // get source and destination account: $sourceAccount = TransactionJournal::sourceAccountList($journal)->first(); $destinationAccount = TransactionJournal::destinationAccountList($journal)->first(); return view( - 'transactions.convert', compact( - 'sourceType', 'destinationType', 'journal', 'assetAccounts', - 'positiveAmount', 'sourceAccount', 'destinationAccount', 'sourceType', - 'subTitle', 'subTitleIcon' + 'transactions.convert', + compact( + 'sourceType', 'destinationType', 'journal', 'assetAccounts', + 'positiveAmount', 'sourceAccount', 'destinationAccount', 'sourceType', + 'subTitle', 'subTitleIcon' - ) + ) ); @@ -95,8 +100,35 @@ class ConvertController extends Controller // or to transfer requires } - public function submit(Request $request) + /** + * @param Request $request + * @param TransactionType $destinationType + * @param TransactionJournal $journal + */ + public function submit(Request $request, TransactionType $destinationType, TransactionJournal $journal) { + $sourceType = $journal->transactionType; + + // cannot convert to its own type. + if ($sourceType->type === $destinationType->type) { + Session::flash('info', trans('firefly.convert_is_already_type_' . $destinationType->type)); + + return redirect(route('transactions.show', [$journal->id])); + } + + // cannot convert split. + if ($journal->transactions()->count() > 2) { + Session::flash('error', trans('firefly.cannot_convert_split_journl')); + + return redirect(route('transactions.show', [$journal->id])); + } + + // try the conversion with the given data: + + + + + echo '
';
 
         var_dump($request->all());
diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php
index b1d1d18a1a..1db69759c3 100644
--- a/resources/lang/en_US/firefly.php
+++ b/resources/lang/en_US/firefly.php
@@ -366,8 +366,23 @@ return [
     'convert_options_DepositWithdrawal'          => 'Convert a deposit into a withdrawal',
     'convert_options_TransferWithdrawal'         => 'Convert a transfer into a withdrawal',
     'convert_options_TransferDeposit'            => 'Convert a transfer into a deposit',
-
-
+    'transaction_journal_convert_options'        => 'Convert this transaction',
+    'convert_Withdrawal_to_deposit'              => 'Convert this withdrawal to a deposit',
+    'convert_Withdrawal_to_transfer'             => 'Convert this withdrawal to a transfer',
+    'convert_Deposit_to_withdrawal'              => 'Convert this deposit to a withdrawal',
+    'convert_Deposit_to_transfer'                => 'Convert this deposit to a transfer',
+    'convert_Transfer_to_deposit'                => 'Convert this transfer to a deposit',
+    'convert_Transfer_to_withdrawal'             => 'Convert this transfer to a withdrawal',
+    'convert_please_set_revenue_source'          => 'Please pick the revenue account where the money will come from.',
+    'convert_please_set_asset_destination'       => 'Please pick the asset account where the money will go to.',
+    'convert_please_set_expense_destination'     => 'Please pick the expense account where the money will go to.',
+    'convert_please_set_asset_source'            => 'Please pick the asset account where the money will come from.',
+    'convert_explanation_withdrawal_deposit'     => 'If you convert this withdrawal into a deposit, :amount will be deposited into :sourceName instead of taken from it.',
+    'convert_explanation_withdrawal_transfer'    => 'If you convert this withdrawal into a transfer, :amount will be transferred from :sourceName to a new asset account, instead of being paid to :destinationName.',
+    'convert_explanation_deposit_withdrawal'     => 'If you convert this deposit into a withdrawal, :amount will be removed from :destinationName instead of added to it.',
+    'convert_explanation_deposit_transfer'       => 'If you convert this deposit into a transfer, :amount will be transferred from an asset account of your choice into :destinationName.',
+    'convert_explanation_transfer_withdrawal'    => 'If you convert this transfer into a withdrawal, :amount will go from :sourceName to a new destination as an expense, instead of to :destinationName as a transfer.',
+    'convert_explanation_transfer_deposit'       => 'If you convert this transfer into a deposit, :amount will be deposited into account :destinationName instead of being transferred there.',
     // create new stuff:
     'create_new_withdrawal'                      => 'Create new withdrawal',
     'create_new_deposit'                         => 'Create new deposit',
diff --git a/resources/views/transactions/convert.twig b/resources/views/transactions/convert.twig
index c7617480a6..c5c7c95429 100644
--- a/resources/views/transactions/convert.twig
+++ b/resources/views/transactions/convert.twig
@@ -40,13 +40,19 @@
                         {# ONE #}
                         {% if sourceType.type == 'Withdrawal' and destinationType.type == 'Deposit' %}
                             

- If you convert this withdrawal into a deposit, {{ positiveAmount|formatAmount }} - will be deposited into {{ sourceAccount.name }} - instead of taken from it. + {{ trans('firefly.convert_explanation_withdrawal_deposit', + { + amount: positiveAmount|formatAmount, + sourceRoute: route('accounts.show', [sourceAccount.id]), + sourceName: sourceAccount.name, + destinationRoute: route('accounts.show', [destinationAccount.id]), + destinationName: destinationAccount.name, + })|raw + }}

- Please pick the revenue account where the money will come from. + {{ 'convert_please_set_revenue_source'|_ }}

@@ -56,15 +62,21 @@ {# TWO #} {% if sourceType.type == 'Withdrawal' and destinationType.type == 'Transfer' %}

- If you convert this withdrawal into a transfer, {{ positiveAmount|formatAmount }} - will be transferred from {{ sourceAccount.name }} - to a new asset account, instead of being paid to - {{ destinationAccount.name }}. + {{ trans('firefly.convert_explanation_withdrawal_transfer', + { + amount: positiveAmount|formatAmount, + sourceRoute: route('accounts.show', [sourceAccount.id]), + sourceName: sourceAccount.name, + destinationRoute: route('accounts.show', [destinationAccount.id]), + destinationName: destinationAccount.name, + })|raw + }}

- Please pick the asset account where the money will go to. + {{ 'convert_please_set_asset_destination'|_ }} +

{{ ExpandedForm.select('destination_account_asset', assetAccounts) }} @@ -76,14 +88,21 @@ {% if sourceType.type == 'Deposit' and destinationType.type == 'Withdrawal' %}

- If you convert this deposit into a withdrawal, {{ positiveAmount|formatAmount }} - will be removed from {{ destinationAccount.name }} - instead of added to it. + {{ trans('firefly.convert_explanation_deposit_withdrawal', + { + amount: positiveAmount|formatAmount, + sourceRoute: route('accounts.show', [sourceAccount.id]), + sourceName: sourceAccount.name, + destinationRoute: route('accounts.show', [destinationAccount.id]), + destinationName: destinationAccount.name, + })|raw + }}

- Please pick the expense account where the money will go to. + {{ 'convert_please_set_expense_destination'|_ }} +

{{ ExpandedForm.text('destination_account_expense', destinationAccount.name) }} @@ -95,14 +114,21 @@

- If you convert this deposit into a transfer, {{ positiveAmount|formatAmount }} will be transferred - from an asset account of your choice into - {{ destinationAccount.name }}. + {{ trans('firefly.convert_explanation_deposit_transfer', + { + amount: positiveAmount|formatAmount, + sourceRoute: route('accounts.show', [sourceAccount.id]), + sourceName: sourceAccount.name, + destinationRoute: route('accounts.show', [destinationAccount.id]), + destinationName: destinationAccount.name, + })|raw + }}

- Please pick the asset account where the money will come from. + {{ 'convert_please_set_asset_source'|_ }} +

@@ -114,17 +140,21 @@

- If you convert this transfer into a withdrawal, {{ positiveAmount|formatAmount }} - will go from {{ sourceAccount.name }} - to a new destination as an expense, instead of to - {{ destinationAccount.name }} - as a transfer. + {{ trans('firefly.convert_explanation_transfer_withdrawal', + { + amount: positiveAmount|formatAmount, + sourceRoute: route('accounts.show', [sourceAccount.id]), + sourceName: sourceAccount.name, + destinationRoute: route('accounts.show', [destinationAccount.id]), + destinationName: destinationAccount.name, + })|raw + }}

- Please pick the expense account where the money will go to. + {{ 'convert_please_set_expense_destination'|_ }}

@@ -135,17 +165,24 @@ {# SIX #} {% if sourceType.type == 'Transfer' and destinationType.type == 'Deposit' %} +

- If you convert this transfer into a deposit, {{ positiveAmount|formatAmount }} - will be deposited into account {{ destinationAccount.name }} - instead of being transferred there. + {{ trans('firefly.convert_explanation_transfer_deposit', + { + amount: positiveAmount|formatAmount, + sourceRoute: route('accounts.show', [sourceAccount.id]), + sourceName: sourceAccount.name, + destinationRoute: route('accounts.show', [destinationAccount.id]), + destinationName: destinationAccount.name, + })|raw + }}

- Please pick the revenue account where the money will come from. + {{ 'convert_please_set_revenue_source'|_ }}

diff --git a/resources/views/transactions/show.twig b/resources/views/transactions/show.twig index 5a146f26be..f4928d89f4 100644 --- a/resources/views/transactions/show.twig +++ b/resources/views/transactions/show.twig @@ -218,7 +218,7 @@

- Convert this {{ journal.transactionType.type }} to a withdrawal. + {{ ('convert_'~journal.transactionType.type~'_to_withdrawal')|_ }}

{% endif %} @@ -226,7 +226,7 @@

- Convert this {{ journal.transactionType.type }} to a deposit. + {{ ('convert_'~journal.transactionType.type~'_to_deposit')|_ }}

{% endif %} @@ -235,7 +235,7 @@

- Convert this {{ journal.transactionType.type }} to a transfer. + {{ ('convert_'~journal.transactionType.type~'_to_transfer')|_ }}

{% endif %} @@ -251,7 +251,7 @@
-

Transactions

+

{{ 'transactions'|_ }}

From d4995e342f7158df82cf2fc7846ceabe35241c31 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 30 Oct 2016 06:14:07 +0100 Subject: [PATCH 011/709] Fixed #330 --- .../Transaction/ConvertController.php | 123 ++++++++++++++++-- .../Journal/JournalRepository.php | 38 +++++- .../Journal/JournalRepositoryInterface.php | 12 ++ resources/lang/en_US/firefly.php | 5 + 4 files changed, 166 insertions(+), 12 deletions(-) diff --git a/app/Http/Controllers/Transaction/ConvertController.php b/app/Http/Controllers/Transaction/ConvertController.php index c66f5421c5..851fbfe2c5 100644 --- a/app/Http/Controllers/Transaction/ConvertController.php +++ b/app/Http/Controllers/Transaction/ConvertController.php @@ -14,11 +14,14 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers\Transaction; use ExpandedForm; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use Illuminate\Http\Request; use Session; use View; @@ -101,17 +104,20 @@ class ConvertController extends Controller } /** - * @param Request $request - * @param TransactionType $destinationType - * @param TransactionJournal $journal + * @param Request $request + * @param JournalRepositoryInterface $repository + * @param TransactionType $destinationType + * @param TransactionJournal $journal + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ - public function submit(Request $request, TransactionType $destinationType, TransactionJournal $journal) + public function submit(Request $request, JournalRepositoryInterface $repository, TransactionType $destinationType, TransactionJournal $journal) { - $sourceType = $journal->transactionType; + $data = $request->all(); // cannot convert to its own type. - if ($sourceType->type === $destinationType->type) { - Session::flash('info', trans('firefly.convert_is_already_type_' . $destinationType->type)); + if ($journal->transactionType->type === $destinationType->type) { + Session::flash('error', trans('firefly.convert_is_already_type_' . $destinationType->type)); return redirect(route('transactions.show', [$journal->id])); } @@ -123,18 +129,113 @@ class ConvertController extends Controller return redirect(route('transactions.show', [$journal->id])); } - // try the conversion with the given data: + // get the new source and destination account: + $source = $this->getSourceAccount($journal, $destinationType, $data); + $destination = $this->getDestinationAccount($journal, $destinationType, $data); + // update the journal: + $errors = $repository->convert($journal, $destinationType, $source, $destination); + if ($errors->count() > 0) { + return redirect(route('transactions.convert', [strtolower($destinationType->type), $journal->id]))->withErrors($errors)->withInput(); + } + Session::flash('success', trans('firefly.converted_to_' . $destinationType->type)); + return redirect(route('transactions.show', [$journal->id])); + } - echo '
';
+    /**
+     * @param TransactionJournal $journal
+     * @param TransactionType    $destinationType
+     * @param array              $data
+     *
+     * @return Account
+     * @throws FireflyException
+     */
+    private function getDestinationAccount(TransactionJournal $journal, TransactionType $destinationType, array $data): Account
+    {
+        /** @var AccountRepositoryInterface $accountRepository */
+        $accountRepository  = app(AccountRepositoryInterface::class);
+        $sourceAccount      = TransactionJournal::sourceAccountList($journal)->first();
+        $destinationAccount = TransactionJournal::destinationAccountList($journal)->first();
+        $sourceType         = $journal->transactionType;
+        $destination        = null;
+        $joined             = $sourceType->type . '-' . $destinationType->type;
+        switch ($joined) {
+            default:
+                throw new FireflyException('Cannot handle ' . $joined);
+            case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT: # one
+                $destination = $sourceAccount;
+                break;
+            case TransactionType::WITHDRAWAL . '-' . TransactionType::TRANSFER: # two
+                $destination = $accountRepository->find(intval($data['destination_account_asset']));
+                break;
+            case TransactionType::DEPOSIT . '-' . TransactionType::WITHDRAWAL: # three
+            case TransactionType::TRANSFER . '-' . TransactionType::WITHDRAWAL: #five
+                $data        = [
+                    'name'           => $data['destination_account_expense'],
+                    'accountType'    => 'expense',
+                    'virtualBalance' => 0,
+                    'active'         => true,
+                    'iban'           => null,
+                ];
+                $destination = $accountRepository->store($data);
+                break;
+            case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER: # four
+            case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT: #six
+                $destination = $destinationAccount;
+                break;
+        }
 
-        var_dump($request->all());
+        return $destination;
+    }
 
+    /**
+     * @param TransactionJournal $journal
+     * @param TransactionType    $destinationType
+     * @param array              $data
+     *
+     * @return Account
+     * @throws FireflyException
+     */
+    private function getSourceAccount(TransactionJournal $journal, TransactionType $destinationType, array $data): Account
+    {
+        /** @var AccountRepositoryInterface $accountRepository */
+        $accountRepository  = app(AccountRepositoryInterface::class);
+        $sourceAccount      = TransactionJournal::sourceAccountList($journal)->first();
+        $destinationAccount = TransactionJournal::destinationAccountList($journal)->first();
+        $sourceType         = $journal->transactionType;
+        $source             = new Account;
+        $joined             = $sourceType->type . '-' . $destinationType->type;
+        switch ($joined) {
+            default:
+                throw new FireflyException('Cannot handle ' . $joined);
+            case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT: # one
+            case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT: #six
+                $data   = [
+                    'name'           => $data['source_account_revenue'],
+                    'accountType'    => 'revenue',
+                    'virtualBalance' => 0,
+                    'active'         => true,
+                    'iban'           => null,
+                ];
+                $source = $accountRepository->store($data);
+                break;
+            case TransactionType::WITHDRAWAL . '-' . TransactionType::TRANSFER: # two
+            case TransactionType::TRANSFER . '-' . TransactionType::WITHDRAWAL: #five
+                $source = $sourceAccount;
+                break;
+            case TransactionType::DEPOSIT . '-' . TransactionType::WITHDRAWAL: # three
+                $source = $destinationAccount;
+                break;
+            case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER: # four
+                $source = $accountRepository->find(intval($data['source_account_asset']));
+                break;
+        }
+
+        return $source;
 
-        exit;
     }
 
 }
\ No newline at end of file
diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php
index 6399d98f0d..5d81adf1a6 100644
--- a/app/Repositories/Journal/JournalRepository.php
+++ b/app/Repositories/Journal/JournalRepository.php
@@ -26,7 +26,9 @@ use FireflyIII\Models\TransactionType;
 use FireflyIII\Repositories\Tag\TagRepositoryInterface;
 use FireflyIII\User;
 use Illuminate\Support\Collection;
+use Illuminate\Support\MessageBag;
 use Log;
+use Preferences;
 
 /**
  * Class JournalRepository
@@ -51,6 +53,41 @@ class JournalRepository implements JournalRepositoryInterface
         $this->user = $user;
     }
 
+    /**
+     * @param TransactionJournal $journal
+     * @param TransactionType    $type
+     * @param Account            $source
+     * @param Account            $destination
+     *
+     * @return MessageBag
+     */
+    public function convert(TransactionJournal $journal, TransactionType $type, Account $source, Account $destination): MessageBag
+    {
+        // default message bag that shows errors for everything.
+        $messages = new MessageBag;
+        $messages->add('source_account_revenue', trans('firefly.invalid_convert_selection'));
+        $messages->add('destination_account_asset', trans('firefly.invalid_convert_selection'));
+        $messages->add('destination_account_expense', trans('firefly.invalid_convert_selection'));
+        $messages->add('source_account_asset', trans('firefly.invalid_convert_selection'));
+
+        if ($source->id === $destination->id || is_null($source->id) || is_null($destination->id)) {
+            return $messages;
+        }
+
+        $sourceTransaction             = $journal->transactions()->where('amount', '<', 0)->first();
+        $destinationTransaction        = $journal->transactions()->where('amount', '>', 0)->first();
+        $sourceTransaction->account_id = $source->id;
+        $sourceTransaction->save();
+        $destinationTransaction->account_id = $destination->id;
+        $destinationTransaction->save();
+        $journal->transaction_type_id = $type->id;
+        $journal->save();
+        Preferences::mark();
+        $messages = new MessageBag;
+
+        return $messages;
+    }
+
     /**
      * @param TransactionJournal $journal
      *
@@ -95,7 +132,6 @@ class JournalRepository implements JournalRepositoryInterface
         return $entry;
     }
 
-
     /**
      * @param array $data
      *
diff --git a/app/Repositories/Journal/JournalRepositoryInterface.php b/app/Repositories/Journal/JournalRepositoryInterface.php
index 5db74eb818..d1c2825bb5 100644
--- a/app/Repositories/Journal/JournalRepositoryInterface.php
+++ b/app/Repositories/Journal/JournalRepositoryInterface.php
@@ -13,7 +13,10 @@ declare(strict_types = 1);
 
 namespace FireflyIII\Repositories\Journal;
 
+use FireflyIII\Models\Account;
 use FireflyIII\Models\TransactionJournal;
+use FireflyIII\Models\TransactionType;
+use Illuminate\Support\MessageBag;
 
 /**
  * Interface JournalRepositoryInterface
@@ -32,6 +35,15 @@ interface JournalRepositoryInterface
      */
     public function delete(TransactionJournal $journal): bool;
 
+    /**
+     * @param TransactionJournal $journal
+     * @param TransactionType    $type
+     * @param array              $data
+     *
+     * @return MessageBag
+     */
+    public function convert(TransactionJournal $journal, TransactionType $type, Account $source, Account $destination): MessageBag;
+
     /**
      * Find a specific journal
      *
diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php
index 1db69759c3..b15faf48fa 100644
--- a/resources/lang/en_US/firefly.php
+++ b/resources/lang/en_US/firefly.php
@@ -383,6 +383,11 @@ return [
     'convert_explanation_deposit_transfer'       => 'If you convert this deposit into a transfer, :amount will be transferred from an asset account of your choice into :destinationName.',
     'convert_explanation_transfer_withdrawal'    => 'If you convert this transfer into a withdrawal, :amount will go from :sourceName to a new destination as an expense, instead of to :destinationName as a transfer.',
     'convert_explanation_transfer_deposit'       => 'If you convert this transfer into a deposit, :amount will be deposited into account :destinationName instead of being transferred there.',
+    'converted_to_Withdrawal'                    => 'The transaction has been converted to a withdrawal',
+    'converted_to_Deposit'                       => 'The transaction has been converted to a deposit',
+    'converted_to_Transfer'                      => 'The transaction has been converted to a transfer',
+
+
     // create new stuff:
     'create_new_withdrawal'                      => 'Create new withdrawal',
     'create_new_deposit'                         => 'Create new deposit',

From c53da152190f99959d333cac8e506be7a8f30b58 Mon Sep 17 00:00:00 2001
From: James Cole 
Date: Sun, 30 Oct 2016 06:20:18 +0100
Subject: [PATCH 012/709] Update composer.lock in anticipation of new release.

Signed-off-by: James Cole 
---
 composer.lock | 158 +++++++++++++++++++++++++-------------------------
 1 file changed, 79 insertions(+), 79 deletions(-)

diff --git a/composer.lock b/composer.lock
index 0b571ce03d..a87c3c8e96 100644
--- a/composer.lock
+++ b/composer.lock
@@ -410,35 +410,35 @@
         },
         {
             "name": "doctrine/annotations",
-            "version": "v1.2.7",
+            "version": "v1.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/annotations.git",
-                "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535"
+                "reference": "30e07cf03edc3cd3ef579d0dd4dd8c58250799a5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/annotations/zipball/f25c8aab83e0c3e976fd7d19875f198ccf2f7535",
-                "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535",
+                "url": "https://api.github.com/repos/doctrine/annotations/zipball/30e07cf03edc3cd3ef579d0dd4dd8c58250799a5",
+                "reference": "30e07cf03edc3cd3ef579d0dd4dd8c58250799a5",
                 "shasum": ""
             },
             "require": {
                 "doctrine/lexer": "1.*",
-                "php": ">=5.3.2"
+                "php": "^5.6 || ^7.0"
             },
             "require-dev": {
                 "doctrine/cache": "1.*",
-                "phpunit/phpunit": "4.*"
+                "phpunit/phpunit": "^5.6.1"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.3.x-dev"
+                    "dev-master": "1.4.x-dev"
                 }
             },
             "autoload": {
-                "psr-0": {
-                    "Doctrine\\Common\\Annotations\\": "lib/"
+                "psr-4": {
+                    "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
@@ -474,20 +474,20 @@
                 "docblock",
                 "parser"
             ],
-            "time": "2015-08-31 12:32:49"
+            "time": "2016-10-24 11:45:47"
         },
         {
             "name": "doctrine/cache",
-            "version": "v1.6.0",
+            "version": "v1.6.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/cache.git",
-                "reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6"
+                "reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/cache/zipball/f8af318d14bdb0eff0336795b428b547bd39ccb6",
-                "reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6",
+                "url": "https://api.github.com/repos/doctrine/cache/zipball/b6f544a20f4807e81f7044d31e679ccbb1866dc3",
+                "reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3",
                 "shasum": ""
             },
             "require": {
@@ -544,7 +544,7 @@
                 "cache",
                 "caching"
             ],
-            "time": "2015-12-31 16:37:02"
+            "time": "2016-10-29 11:16:17"
         },
         {
             "name": "doctrine/collections",
@@ -1275,16 +1275,16 @@
         },
         {
             "name": "league/csv",
-            "version": "8.1.1",
+            "version": "8.1.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/thephpleague/csv.git",
-                "reference": "3b22a40804aa0bc5224ffb2f5e8248edf0a9a38c"
+                "reference": "33447984f7a7038fefaa5a6177e8407b66bc85b4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/thephpleague/csv/zipball/3b22a40804aa0bc5224ffb2f5e8248edf0a9a38c",
-                "reference": "3b22a40804aa0bc5224ffb2f5e8248edf0a9a38c",
+                "url": "https://api.github.com/repos/thephpleague/csv/zipball/33447984f7a7038fefaa5a6177e8407b66bc85b4",
+                "reference": "33447984f7a7038fefaa5a6177e8407b66bc85b4",
                 "shasum": ""
             },
             "require": {
@@ -1298,7 +1298,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "8.0-dev"
+                    "dev-master": "8.1-dev"
                 }
             },
             "autoload": {
@@ -1328,7 +1328,7 @@
                 "read",
                 "write"
             ],
-            "time": "2016-09-05 08:16:07"
+            "time": "2016-10-27 11:21:24"
         },
         {
             "name": "league/flysystem",
@@ -2170,7 +2170,7 @@
         },
         {
             "name": "symfony/class-loader",
-            "version": "v3.1.5",
+            "version": "v3.1.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/class-loader.git",
@@ -2226,16 +2226,16 @@
         },
         {
             "name": "symfony/console",
-            "version": "v3.1.5",
+            "version": "v3.1.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
-                "reference": "6cb0872fb57b38b3b09ff213c21ed693956b9eb0"
+                "reference": "c99da1119ae61e15de0e4829196b9fba6f73d065"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/console/zipball/6cb0872fb57b38b3b09ff213c21ed693956b9eb0",
-                "reference": "6cb0872fb57b38b3b09ff213c21ed693956b9eb0",
+                "url": "https://api.github.com/repos/symfony/console/zipball/c99da1119ae61e15de0e4829196b9fba6f73d065",
+                "reference": "c99da1119ae61e15de0e4829196b9fba6f73d065",
                 "shasum": ""
             },
             "require": {
@@ -2283,11 +2283,11 @@
             ],
             "description": "Symfony Console Component",
             "homepage": "https://symfony.com",
-            "time": "2016-09-28 00:11:12"
+            "time": "2016-10-06 01:44:51"
         },
         {
             "name": "symfony/debug",
-            "version": "v3.1.5",
+            "version": "v3.1.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/debug.git",
@@ -2344,16 +2344,16 @@
         },
         {
             "name": "symfony/event-dispatcher",
-            "version": "v3.1.5",
+            "version": "v3.1.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/event-dispatcher.git",
-                "reference": "c0c00c80b3a69132c4e55c3e7db32b4a387615e5"
+                "reference": "28b0832b2553ffb80cabef6a7a812ff1e670c0bc"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/c0c00c80b3a69132c4e55c3e7db32b4a387615e5",
-                "reference": "c0c00c80b3a69132c4e55c3e7db32b4a387615e5",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/28b0832b2553ffb80cabef6a7a812ff1e670c0bc",
+                "reference": "28b0832b2553ffb80cabef6a7a812ff1e670c0bc",
                 "shasum": ""
             },
             "require": {
@@ -2400,11 +2400,11 @@
             ],
             "description": "Symfony EventDispatcher Component",
             "homepage": "https://symfony.com",
-            "time": "2016-07-19 10:45:57"
+            "time": "2016-10-13 06:28:43"
         },
         {
             "name": "symfony/finder",
-            "version": "v3.1.5",
+            "version": "v3.1.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/finder.git",
@@ -2453,16 +2453,16 @@
         },
         {
             "name": "symfony/http-foundation",
-            "version": "v3.1.5",
+            "version": "v3.1.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-foundation.git",
-                "reference": "5114f1becca9f29e3af94374f1689c83c1aa3d97"
+                "reference": "f21e5a8b88274b7720779aa88f9c02c6d6ec08d7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/5114f1becca9f29e3af94374f1689c83c1aa3d97",
-                "reference": "5114f1becca9f29e3af94374f1689c83c1aa3d97",
+                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/f21e5a8b88274b7720779aa88f9c02c6d6ec08d7",
+                "reference": "f21e5a8b88274b7720779aa88f9c02c6d6ec08d7",
                 "shasum": ""
             },
             "require": {
@@ -2502,20 +2502,20 @@
             ],
             "description": "Symfony HttpFoundation Component",
             "homepage": "https://symfony.com",
-            "time": "2016-09-21 20:55:10"
+            "time": "2016-10-24 15:52:44"
         },
         {
             "name": "symfony/http-kernel",
-            "version": "v3.1.5",
+            "version": "v3.1.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-kernel.git",
-                "reference": "dc339d6eebadfa6e39c52868b4d4a715eff13c69"
+                "reference": "c235f1b13ba67012e283996a5427f22e2e04be14"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/dc339d6eebadfa6e39c52868b4d4a715eff13c69",
-                "reference": "dc339d6eebadfa6e39c52868b4d4a715eff13c69",
+                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/c235f1b13ba67012e283996a5427f22e2e04be14",
+                "reference": "c235f1b13ba67012e283996a5427f22e2e04be14",
                 "shasum": ""
             },
             "require": {
@@ -2523,7 +2523,7 @@
                 "psr/log": "~1.0",
                 "symfony/debug": "~2.8|~3.0",
                 "symfony/event-dispatcher": "~2.8|~3.0",
-                "symfony/http-foundation": "~2.8.8|~3.0.8|~3.1.2|~3.2"
+                "symfony/http-foundation": "~2.8.13|~3.1.6|~3.2"
             },
             "conflict": {
                 "symfony/config": "<2.8"
@@ -2584,7 +2584,7 @@
             ],
             "description": "Symfony HttpKernel Component",
             "homepage": "https://symfony.com",
-            "time": "2016-10-03 19:01:06"
+            "time": "2016-10-27 02:38:31"
         },
         {
             "name": "symfony/polyfill-mbstring",
@@ -2755,7 +2755,7 @@
         },
         {
             "name": "symfony/process",
-            "version": "v3.1.5",
+            "version": "v3.1.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/process.git",
@@ -2804,7 +2804,7 @@
         },
         {
             "name": "symfony/routing",
-            "version": "v3.1.5",
+            "version": "v3.1.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/routing.git",
@@ -2879,16 +2879,16 @@
         },
         {
             "name": "symfony/translation",
-            "version": "v3.1.5",
+            "version": "v3.1.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation.git",
-                "reference": "93013a18d272e59dab8e67f583155b78c68947eb"
+                "reference": "ff1285087397d2f64041b35e591f3025881c90cd"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation/zipball/93013a18d272e59dab8e67f583155b78c68947eb",
-                "reference": "93013a18d272e59dab8e67f583155b78c68947eb",
+                "url": "https://api.github.com/repos/symfony/translation/zipball/ff1285087397d2f64041b35e591f3025881c90cd",
+                "reference": "ff1285087397d2f64041b35e591f3025881c90cd",
                 "shasum": ""
             },
             "require": {
@@ -2939,20 +2939,20 @@
             ],
             "description": "Symfony Translation Component",
             "homepage": "https://symfony.com",
-            "time": "2016-09-06 11:02:40"
+            "time": "2016-10-18 04:30:12"
         },
         {
             "name": "symfony/var-dumper",
-            "version": "v3.1.5",
+            "version": "v3.1.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/var-dumper.git",
-                "reference": "70bfe927b86ba9999aeebd829715b0bb2cd39a10"
+                "reference": "4dc2f03b480c43f1665d3317d827a04ed6ffd11e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/70bfe927b86ba9999aeebd829715b0bb2cd39a10",
-                "reference": "70bfe927b86ba9999aeebd829715b0bb2cd39a10",
+                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/4dc2f03b480c43f1665d3317d827a04ed6ffd11e",
+                "reference": "4dc2f03b480c43f1665d3317d827a04ed6ffd11e",
                 "shasum": ""
             },
             "require": {
@@ -3002,20 +3002,20 @@
                 "debug",
                 "dump"
             ],
-            "time": "2016-09-29 14:13:09"
+            "time": "2016-10-18 15:46:07"
         },
         {
             "name": "twig/twig",
-            "version": "v1.26.1",
+            "version": "v1.27.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/twigphp/Twig.git",
-                "reference": "a09d8ee17ac1cfea29ed60c83960ad685c6a898d"
+                "reference": "3c6c0033fd3b5679c6e1cb60f4f9766c2b424d97"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/twigphp/Twig/zipball/a09d8ee17ac1cfea29ed60c83960ad685c6a898d",
-                "reference": "a09d8ee17ac1cfea29ed60c83960ad685c6a898d",
+                "url": "https://api.github.com/repos/twigphp/Twig/zipball/3c6c0033fd3b5679c6e1cb60f4f9766c2b424d97",
+                "reference": "3c6c0033fd3b5679c6e1cb60f4f9766c2b424d97",
                 "shasum": ""
             },
             "require": {
@@ -3028,7 +3028,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.26-dev"
+                    "dev-master": "1.27-dev"
                 }
             },
             "autoload": {
@@ -3063,7 +3063,7 @@
             "keywords": [
                 "templating"
             ],
-            "time": "2016-10-05 18:57:41"
+            "time": "2016-10-25 19:17:17"
         },
         {
             "name": "vlucas/phpdotenv",
@@ -3880,16 +3880,16 @@
         },
         {
             "name": "phpunit/phpunit",
-            "version": "5.6.1",
+            "version": "5.6.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "60c32c5b5e79c2248001efa2560f831da11cc2d7"
+                "reference": "cd13b23ac5a519a4708e00736c26ee0bb28b2e01"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/60c32c5b5e79c2248001efa2560f831da11cc2d7",
-                "reference": "60c32c5b5e79c2248001efa2560f831da11cc2d7",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/cd13b23ac5a519a4708e00736c26ee0bb28b2e01",
+                "reference": "cd13b23ac5a519a4708e00736c26ee0bb28b2e01",
                 "shasum": ""
             },
             "require": {
@@ -3958,7 +3958,7 @@
                 "testing",
                 "xunit"
             ],
-            "time": "2016-10-07 13:03:26"
+            "time": "2016-10-25 07:40:25"
         },
         {
             "name": "phpunit/phpunit-mock-objects",
@@ -4534,7 +4534,7 @@
         },
         {
             "name": "symfony/css-selector",
-            "version": "v3.1.5",
+            "version": "v3.1.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/css-selector.git",
@@ -4587,16 +4587,16 @@
         },
         {
             "name": "symfony/dom-crawler",
-            "version": "v3.1.5",
+            "version": "v3.1.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/dom-crawler.git",
-                "reference": "bb7395e8b1db3654de82b9f35d019958276de4d7"
+                "reference": "59eee3c76eb89f21857798620ebdad7a05ad14f4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/bb7395e8b1db3654de82b9f35d019958276de4d7",
-                "reference": "bb7395e8b1db3654de82b9f35d019958276de4d7",
+                "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/59eee3c76eb89f21857798620ebdad7a05ad14f4",
+                "reference": "59eee3c76eb89f21857798620ebdad7a05ad14f4",
                 "shasum": ""
             },
             "require": {
@@ -4639,20 +4639,20 @@
             ],
             "description": "Symfony DomCrawler Component",
             "homepage": "https://symfony.com",
-            "time": "2016-08-05 08:37:39"
+            "time": "2016-10-18 15:46:07"
         },
         {
             "name": "symfony/yaml",
-            "version": "v3.1.5",
+            "version": "v3.1.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/yaml.git",
-                "reference": "368b9738d4033c8b93454cb0dbd45d305135a6d3"
+                "reference": "7ff51b06c6c3d5cc6686df69004a42c69df09e27"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/yaml/zipball/368b9738d4033c8b93454cb0dbd45d305135a6d3",
-                "reference": "368b9738d4033c8b93454cb0dbd45d305135a6d3",
+                "url": "https://api.github.com/repos/symfony/yaml/zipball/7ff51b06c6c3d5cc6686df69004a42c69df09e27",
+                "reference": "7ff51b06c6c3d5cc6686df69004a42c69df09e27",
                 "shasum": ""
             },
             "require": {
@@ -4688,7 +4688,7 @@
             ],
             "description": "Symfony Yaml Component",
             "homepage": "https://symfony.com",
-            "time": "2016-09-25 08:27:07"
+            "time": "2016-10-24 18:41:13"
         },
         {
             "name": "webmozart/assert",

From 18ee20e680505a9dbabd9ee17881d0e24f8a3193 Mon Sep 17 00:00:00 2001
From: James Cole 
Date: Sun, 30 Oct 2016 06:37:00 +0100
Subject: [PATCH 013/709] Update crowdin file [skip ci]

Signed-off-by: James Cole 
---
 crowdin.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crowdin.yaml b/crowdin.yaml
index 85cd4661c0..2ebe1dbabf 100644
--- a/crowdin.yaml
+++ b/crowdin.yaml
@@ -1,4 +1,4 @@
 files:
   - 
     source: /resources/lang/en_US/*.php
-    translation: /resources/lang/%locale_with_underscore%/
+    translation: /resources/lang/%locale_with_underscore%/%original_file_name%

From 73f87e30c219c58e40a497854f724fefad6444fb Mon Sep 17 00:00:00 2001
From: James Cole 
Date: Sun, 30 Oct 2016 08:52:09 +0100
Subject: [PATCH 014/709] Changelog.

Signed-off-by: James Cole 
---
 CHANGELOG.md | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9181780696..4aad0bb631 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,7 +2,37 @@
 All notable changes to this project will be documented in this file.
 This project adheres to [Semantic Versioning](http://semver.org/).
 
+## [4.1.4] - 2016-10-30
+### Added
+- New Dockerfile thanks to @schoentoon
+- Added changing the destination account as rule action.
+- Added changing the source account as rule action.
+- Can convert transactions into different types.
+
+### Changed
+- Changed the export routine to be more future-proof.
+- Improved help routine.
+- Integrated CrowdIn translations.
+- Simplified reports
+- Change error message to refer to solution.
+
+### Fixed
+- #367 thanks to @HungryFeline
+- #366 thanks to @3mz3t
+- #362 and #341 thanks to @bnw
+- #355 thanks to @roberthorlings
+
+## [4.1.3] - 2016-10-22
+### Fixed
+- Some event handlers called the wrong method.
+
+## [4.1.2] - 2016-10-22
+
+### Fixed
+- A bug is fixed in the journal event handler that prevented Firefly III from actually storing journals.
+
 ## [4.1.1] - 2016-10-22
+
 ### Added
 - Option to show deposit accounts on the front page.
 - Script to upgrade split transactions

From 7821c52842ca6c2133df490a9440f6c355fa5944 Mon Sep 17 00:00:00 2001
From: James Cole 
Date: Sun, 30 Oct 2016 18:29:26 +0100
Subject: [PATCH 015/709] Ajax some report parts.

---
 .../Controllers/Report/BudgetController.php   |  91 ++++++++++
 app/Http/Controllers/ReportController.php     |  22 +--
 app/Import/Setup/CsvSetup.php                 |   1 +
 public/js/ff/reports/default/month.js         |  23 ++-
 public/js/ff/reports/default/year.js          |  25 ++-
 resources/views/reports/default/month.twig    |  14 +-
 resources/views/reports/default/year.twig     |  28 +--
 .../partials/budget-year-overview.twig        |  25 +++
 resources/views/reports/partials/budgets.twig | 162 +++++++++---------
 routes/web.php                                |  24 ++-
 10 files changed, 282 insertions(+), 133 deletions(-)
 create mode 100644 app/Http/Controllers/Report/BudgetController.php
 create mode 100644 resources/views/reports/partials/budget-year-overview.twig

diff --git a/app/Http/Controllers/Report/BudgetController.php b/app/Http/Controllers/Report/BudgetController.php
new file mode 100644
index 0000000000..1d139c23ca
--- /dev/null
+++ b/app/Http/Controllers/Report/BudgetController.php
@@ -0,0 +1,91 @@
+addProperty($start);
+        $cache->addProperty($end);
+        $cache->addProperty('budget-report');
+        $cache->addProperty($accounts->pluck('id')->toArray());
+        if ($cache->has()) {
+            return $cache->get();
+        }
+
+        $budgets = $helper->getBudgetReport($start, $end, $accounts);
+
+        $result = view('reports.partials.budgets', compact('budgets'))->render();
+        $cache->store($result);
+
+        return $result;
+
+    }
+
+    /**
+     * @param BudgetReportHelperInterface $helper
+     * @param Carbon                      $start
+     * @param Carbon                      $end
+     * @param Collection                  $accounts
+     *
+     * @return string
+     */
+    public function budgetYearOverview(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts)
+    {
+
+        // chart properties for cache:
+        $cache = new CacheProperties;
+        $cache->addProperty($start);
+        $cache->addProperty($end);
+        $cache->addProperty('budget-year-overview');
+        $cache->addProperty($accounts->pluck('id')->toArray());
+        if ($cache->has()) {
+            return $cache->get();
+        }
+
+        $budgets = $helper->budgetYearOverview($start, $end, $accounts);
+
+        $result = view('reports.partials.budget-year-overview', compact('budgets'))->render();
+        $cache->store($result);
+
+        return $result;
+
+    }
+
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php
index ab3833555a..77a50d7218 100644
--- a/app/Http/Controllers/ReportController.php
+++ b/app/Http/Controllers/ReportController.php
@@ -15,7 +15,6 @@ namespace FireflyIII\Http\Controllers;
 
 use Carbon\Carbon;
 use FireflyIII\Exceptions\FireflyException;
-use FireflyIII\Helpers\Report\BudgetReportHelperInterface;
 use FireflyIII\Helpers\Report\ReportHelperInterface;
 use FireflyIII\Models\Account;
 use FireflyIII\Models\AccountType;
@@ -37,8 +36,6 @@ use View;
  */
 class ReportController extends Controller
 {
-    /** @var BudgetReportHelperInterface */
-    protected $budgetHelper;
     /** @var ReportHelperInterface */
     protected $helper;
 
@@ -55,8 +52,7 @@ class ReportController extends Controller
                 View::share('title', trans('firefly.reports'));
                 View::share('mainTitleIcon', 'fa-line-chart');
 
-                $this->helper       = app(ReportHelperInterface::class);
-                $this->budgetHelper = app(BudgetReportHelperInterface::class);
+                $this->helper = app(ReportHelperInterface::class);
 
                 return $next($request);
             }
@@ -219,10 +215,8 @@ class ReportController extends Controller
      */
     private function defaultMonth(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
     {
-        // get report stuff!
-        $budgets = $this->budgetHelper->getBudgetReport($start, $end, $accounts);
-        $bills   = $this->helper->getBillReport($start, $end, $accounts);
-        $tags    = $this->helper->tagReport($start, $end, $accounts);
+        $bills = $this->helper->getBillReport($start, $end, $accounts);
+        $tags  = $this->helper->tagReport($start, $end, $accounts);
 
         // and some id's, joined:
         $accountIds = join(',', $accounts->pluck('id')->toArray());
@@ -233,9 +227,9 @@ class ReportController extends Controller
             compact(
                 'start', 'end',
                 'tags',
-                'budgets',
                 'bills',
-                'accountIds', 'reportType'
+                'accountIds',
+                'reportType'
             )
         );
     }
@@ -281,8 +275,7 @@ class ReportController extends Controller
      */
     private function defaultYear(string $reportType, Carbon $start, Carbon $end, Collection $accounts)
     {
-        $tags    = $this->helper->tagReport($start, $end, $accounts);
-        $budgets = $this->budgetHelper->budgetYearOverview($start, $end, $accounts);
+        $tags = $this->helper->tagReport($start, $end, $accounts);
 
         Session::flash('gaEventCategory', 'report');
         Session::flash('gaEventAction', 'year');
@@ -299,7 +292,8 @@ class ReportController extends Controller
         return view(
             'reports.default.year',
             compact(
-                'start', 'reportType', 'accountIds', 'end', 'tags', 'budgets'
+                'start', 'reportType',
+                'accountIds', 'end', 'tags'
             )
         );
     }
diff --git a/app/Import/Setup/CsvSetup.php b/app/Import/Setup/CsvSetup.php
index a49c5fe070..cefeebe275 100644
--- a/app/Import/Setup/CsvSetup.php
+++ b/app/Import/Setup/CsvSetup.php
@@ -323,6 +323,7 @@ class CsvSetup implements SetupInterface
 
         foreach ($config['column-do-mapping'] as $index => $mustBeMapped) {
             if ($mustBeMapped) {
+
                 $column = $config['column-roles'][$index] ?? '_ignore';
 
                 // is valid column?
diff --git a/public/js/ff/reports/default/month.js b/public/js/ff/reports/default/month.js
index d9f30b1f8c..7c15f922b6 100644
--- a/public/js/ff/reports/default/month.js
+++ b/public/js/ff/reports/default/month.js
@@ -1,4 +1,4 @@
-/* globals google,  startDate ,reportURL, endDate , reportType ,accountIds, lineChart, categoryReportUrl, balanceReportUrl */
+/* globals google, budgetReportUrl, startDate ,reportURL, endDate , reportType ,accountIds, lineChart, categoryReportUrl, balanceReportUrl */
 
 
 $(function () {
@@ -7,6 +7,7 @@ $(function () {
 
     loadCategoryReport();
     loadBalanceReport();
+    loadBudgetReport();
 });
 
 function loadCategoryReport() {
@@ -15,12 +16,26 @@ function loadCategoryReport() {
     $.get(categoryReportUrl).done(placeCategoryReport).fail(failCategoryReport);
 }
 
+function loadBudgetReport() {
+    "use strict";
+    console.log('Going to grab ' + budgetReportUrl);
+    $.get(budgetReportUrl).done(placeBudgetReport).fail(failBudgetReport);
+}
+
+
 function loadBalanceReport() {
     "use strict";
     console.log('Going to grab ' + categoryReportUrl);
     $.get(balanceReportUrl).done(placeBalanceReport).fail(failBalanceReport);
 }
 
+function placeBudgetReport(data) {
+    "use strict";
+    $('#budgetReport').removeClass('loading').html(data);
+    listLengthInitial();
+    triggerInfoClick();
+}
+
 function placeBalanceReport(data) {
     "use strict";
     $('#balanceReport').removeClass('loading').html(data);
@@ -35,6 +50,12 @@ function placeCategoryReport(data) {
     triggerInfoClick();
 }
 
+function failBudgetReport() {
+    "use strict";
+    console.log('Fail budget report data!');
+    $('#budgetReport').removeClass('loading').addClass('general-chart-error');
+}
+
 function failBalanceReport() {
     "use strict";
     console.log('Fail balance report data!');
diff --git a/public/js/ff/reports/default/year.js b/public/js/ff/reports/default/year.js
index 3afa11d242..e0cb494772 100644
--- a/public/js/ff/reports/default/year.js
+++ b/public/js/ff/reports/default/year.js
@@ -1,4 +1,4 @@
-/* globals google,  startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, year, month, columnChart, lineChart, stackedColumnChart */
+/* globals google, accountIds, budgetYearOverviewUrl */
 
 var chartDrawn;
 var budgetChart;
@@ -7,8 +7,29 @@ $(function () {
     chartDrawn = false;
     drawChart();
 
+    //
+    loadBudgetOverview();
 });
 
+function loadBudgetOverview() {
+    "use strict";
+    console.log('Going to grab ' + budgetYearOverviewUrl);
+    $.get(budgetYearOverviewUrl).done(placeBudgetOverview).fail(failBudgetOverview);
+}
+
+function placeBudgetOverview(data) {
+    "use strict";
+    $('#budgetOverview').removeClass('loading').html(data);
+    $('.budget-chart-activate').on('click', clickBudgetChart);
+}
+
+function failBudgetOverview() {
+    "use strict";
+    console.log('Fail budget overview data!');
+    $('#budgetOverview').removeClass('loading').addClass('general-chart-error');
+}
+
+
 
 function drawChart() {
     "use strict";
@@ -17,7 +38,7 @@ function drawChart() {
     columnChart('chart/report/in-out/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-chart');
     columnChart('chart/report/in-out-sum/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-sum-chart');
 
-    $('.budget-chart-activate').on('click', clickBudgetChart);
+
 }
 
 function clickBudgetChart(e) {
diff --git a/resources/views/reports/default/month.twig b/resources/views/reports/default/month.twig
index 6e27d82518..bd4f6789b8 100644
--- a/resources/views/reports/default/month.twig
+++ b/resources/views/reports/default/month.twig
@@ -67,8 +67,17 @@
     
     
- - {% include 'reports/partials/budgets.twig' %} +
+ +
+

{{ 'budgets'|_ }}

+
+
+
+
+ + +
@@ -127,6 +136,7 @@ var inOutReportUrl = '{{ route('reports.data.inOutReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; var categoryReportUrl = '{{ route('reports.data.categoryReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; var balanceReportUrl = '{{ route('reports.data.balanceReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var budgetReportUrl = '{{ route('reports.data.budgetReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; diff --git a/resources/views/reports/default/year.twig b/resources/views/reports/default/year.twig index 1db568296a..41e6ef0ebb 100644 --- a/resources/views/reports/default/year.twig +++ b/resources/views/reports/default/year.twig @@ -90,32 +90,7 @@

{{ 'budgets'|_ }}

-
-
- - - - {% for date, header in budgets.get('headers') %} - - {% endfor %} - - - - {% set spentData = budgets.get('spent') %} - {% for budgetId, budgetName in budgets.get('budgets') %} - - - {% for date, header in budgets.get('headers') %} - - {% endfor %} - - - {% endfor %} - - -
 {{ header }}
- {{ budgetName }} - {{ spentData[budgetId][date]|formatAmount }}
+
@@ -150,6 +125,7 @@ var accountReportUrl = '{{ route('reports.data.accountReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; var inOutReportUrl = '{{ route('reports.data.inOutReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var budgetYearOverviewUrl = '{{ route('reports.data.budgetYearOverview', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; diff --git a/resources/views/reports/partials/budget-year-overview.twig b/resources/views/reports/partials/budget-year-overview.twig new file mode 100644 index 0000000000..6836d0d25b --- /dev/null +++ b/resources/views/reports/partials/budget-year-overview.twig @@ -0,0 +1,25 @@ + + + + + {% for date, header in budgets.get('headers') %} + + {% endfor %} + + + + {% set spentData = budgets.get('spent') %} + {% for budgetId, budgetName in budgets.get('budgets') %} + + + {% for date, header in budgets.get('headers') %} + + {% endfor %} + + + {% endfor %} + + +
 {{ header }}
+ {{ budgetName }} + {{ spentData[budgetId][date]|formatAmount }}
\ No newline at end of file diff --git a/resources/views/reports/partials/budgets.twig b/resources/views/reports/partials/budgets.twig index c48dfc87ce..24880e9e46 100644 --- a/resources/views/reports/partials/budgets.twig +++ b/resources/views/reports/partials/budgets.twig @@ -1,87 +1,79 @@ -
+ + + + + + + + + + + + + {% for budgetLine in budgets.getBudgetLines %} + + + + + + + + + {% endfor %} + + + + + + + + + + +
{{ 'budget'|_ }}{{ 'date'|_ }}{{ 'budgeted'|_ }}{{ 'spent'|_ }}{{ 'left'|_ }}{{ 'overspent'|_ }}
+ {% if budgetLine.getBudget.id %} + {{ budgetLine.getBudget.name }} + {% else %} + {{ 'no_budget'|_ }} + {% endif %} + + {% if budgetLine.getRepetition.id %} + + {{ budgetLine.getRepetition.startdate.formatLocalized(monthAndDayFormat) }} + — + {{ budgetLine.getRepetition.enddate.formatLocalized(monthAndDayFormat) }} + + {% endif %} + + {% if budgetLine.getRepetition.id %} + {{ budgetLine.getRepetition.amount|formatAmount }} + {% else %} + {{ 0|formatAmount }} + {% endif %} + + {% if budgetLine.getSpent != 0 %} + {{ budgetLine.getSpent|formatAmount }} + {% endif %} -
-

{{ 'budgets'|_ }}

-
-
- - - - - - - - - - - - - {% for budgetLine in budgets.getBudgetLines %} - - - - - - - - - {% endfor %} - - - - - - - - - - -
{{ 'budget'|_ }}{{ 'date'|_ }}{{ 'budgeted'|_ }}{{ 'spent'|_ }}{{ 'left'|_ }}{{ 'overspent'|_ }}
- {% if budgetLine.getBudget.id %} - {{ budgetLine.getBudget.name }} - {% else %} - {{ 'no_budget'|_ }} - {% endif %} - - {% if budgetLine.getRepetition.id %} - - {{ budgetLine.getRepetition.startdate.formatLocalized(monthAndDayFormat) }} - — - {{ budgetLine.getRepetition.enddate.formatLocalized(monthAndDayFormat) }} - - {% endif %} - - {% if budgetLine.getRepetition.id %} - {{ budgetLine.getRepetition.amount|formatAmount }} - {% else %} - {{ 0|formatAmount }} - {% endif %} - - {% if budgetLine.getSpent != 0 %} - {{ budgetLine.getSpent|formatAmount }} - {% endif %} + {% if budgetLine.getSpent == 0 %} + {{ budgetLine.getSpent|formatAmount }} + {% endif %} - {% if budgetLine.getSpent == 0 %} - {{ budgetLine.getSpent|formatAmount }} - {% endif %} - - - {% if(budgetLine.getOverspent == 0) %} - {{ budgetLine.getLeft|formatAmount }} - {% endif %} - - {% if budgetLine.getOverspent != 0 %} - {{ budgetLine.getOverspent|formatAmount }} - {% endif %} -
{{ 'sum'|_ }}{{ budgets.getBudgeted|formatAmount }} - {% if budgets.getSpent != 0 %} - {{ budgets.getSpent|formatAmountPlain }} - {% endif %} - {% if budgets.getSpent == 0 %} - {{ budgets.getSpent|formatAmount }} - {% endif %} - {{ budgets.getLeft|formatAmount }}{{ budgets.getOverspent|formatAmountPlain }}
-
- +
+ {% if(budgetLine.getOverspent == 0) %} + {{ budgetLine.getLeft|formatAmount }} + {% endif %} + + {% if budgetLine.getOverspent != 0 %} + {{ budgetLine.getOverspent|formatAmount }} + {% endif %} +
{{ 'sum'|_ }}{{ budgets.getBudgeted|formatAmount }} + {% if budgets.getSpent != 0 %} + {{ budgets.getSpent|formatAmountPlain }} + {% endif %} + {% if budgets.getSpent == 0 %} + {{ budgets.getSpent|formatAmount }} + {% endif %} + {{ budgets.getLeft|formatAmount }}{{ budgets.getOverspent|formatAmountPlain }}
diff --git a/routes/web.php b/routes/web.php index 89284d06cc..69fd01df55 100755 --- a/routes/web.php +++ b/routes/web.php @@ -339,6 +339,18 @@ Route::group( ['uses' => 'Report\BalanceController@balanceReport', 'as' => 'reports.data.balanceReport'] ); + // budget report: + Route::get( + '/reports/data/budget-report/{start_date}/{end_date}/{accountList}', + ['uses' => 'Report\BudgetController@budgetReport', 'as' => 'reports.data.budgetReport'] + ); + // budget year overview + Route::get( + '/reports/data/budget-year-overview/{start_date}/{end_date}/{accountList}', + ['uses' => 'Report\BudgetController@budgetYearOverview', 'as' => 'reports.data.budgetYearOverview'] + ); + + /** * Rules Controller */ @@ -401,15 +413,21 @@ Route::group( */ // normal controller - Route::get('/transactions/{what}', ['uses' => 'TransactionController@index', 'as' => 'transactions.index'])->where(['what' => 'expenses|revenue|withdrawal|deposit|transfer|transfers']); + Route::get('/transactions/{what}', ['uses' => 'TransactionController@index', 'as' => 'transactions.index'])->where( + ['what' => 'expenses|revenue|withdrawal|deposit|transfer|transfers'] + ); Route::get('/transaction/show/{tj}', ['uses' => 'TransactionController@show', 'as' => 'transactions.show']); Route::post('/transaction/reorder', ['uses' => 'TransactionController@reorder', 'as' => 'transactions.reorder']); // single controller - Route::get('/transactions/create/{what}', ['uses' => 'Transaction\SingleController@create', 'as' => 'transactions.create'])->where(['what' => 'expenses|revenue|withdrawal|deposit|transfer|transfers']); + Route::get('/transactions/create/{what}', ['uses' => 'Transaction\SingleController@create', 'as' => 'transactions.create'])->where( + ['what' => 'expenses|revenue|withdrawal|deposit|transfer|transfers'] + ); Route::get('/transaction/edit/{tj}', ['uses' => 'Transaction\SingleController@edit', 'as' => 'transactions.edit']); Route::get('/transaction/delete/{tj}', ['uses' => 'Transaction\SingleController@delete', 'as' => 'transactions.delete']); - Route::post('/transactions/store/{what}', ['uses' => 'Transaction\SingleController@store', 'as' => 'transactions.store'])->where(['what' => 'expenses|revenue|withdrawal|deposit|transfer|transfers']); + Route::post('/transactions/store/{what}', ['uses' => 'Transaction\SingleController@store', 'as' => 'transactions.store'])->where( + ['what' => 'expenses|revenue|withdrawal|deposit|transfer|transfers'] + ); Route::post('/transaction/update/{tj}', ['uses' => 'Transaction\SingleController@update', 'as' => 'transactions.update']); Route::post('/transaction/destroy/{tj}', ['uses' => 'Transaction\SingleController@destroy', 'as' => 'transactions.destroy']); From 4e3e0159126d45b52e2bf15bcb156c7975c601ae Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 30 Oct 2016 20:13:49 +0100 Subject: [PATCH 016/709] Fix multi year account report [skip ci] --- resources/views/reports/default/multi-year.twig | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/resources/views/reports/default/multi-year.twig b/resources/views/reports/default/multi-year.twig index e6975889c8..9eeff7cafe 100644 --- a/resources/views/reports/default/multi-year.twig +++ b/resources/views/reports/default/multi-year.twig @@ -31,7 +31,14 @@
-
+
+
+
+

{{ 'accountBalances'|_ }}

+
+
+
+
From ed33a054ad20741b49b8e8f1847d4a34c3d6ffda Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 31 Oct 2016 18:31:52 +0100 Subject: [PATCH 017/709] This update will make the help method fall back to the English content, if it is available. --- app/Helpers/Help/Help.php | 45 ++++++++++++++++++------- app/Http/Controllers/HelpController.php | 20 +++++++++-- resources/lang/en_US/firefly.php | 3 +- 3 files changed, 53 insertions(+), 15 deletions(-) diff --git a/app/Helpers/Help/Help.php b/app/Helpers/Help/Help.php index 2933b2a0ca..4b19963bef 100644 --- a/app/Helpers/Help/Help.php +++ b/app/Helpers/Help/Help.php @@ -14,7 +14,9 @@ namespace FireflyIII\Helpers\Help; use Cache; use League\CommonMark\CommonMarkConverter; +use Log; use Requests; +use Requests_Exception; use Route; /** @@ -45,20 +47,28 @@ class Help implements HelpInterface public function getFromGithub(string $language, string $route): string { - $uri = sprintf('https://raw.githubusercontent.com/firefly-iii/help/master/%s/%s.md', $language, $route); - $content = '

' . strval(trans('firefly.route_has_no_help')) . '

'; - $result = Requests::get($uri); + $uri = sprintf('https://raw.githubusercontent.com/firefly-iii/help/master/%s/%s.md', $language, $route); + Log::debug(sprintf('Trying to get %s...', $uri)); + $content = ''; + try { + $result = Requests::get($uri); + } catch (Requests_Exception $e) { + Log::error($e); + + return ''; + } + + + Log::debug(sprintf('Status code is %d', $result->status_code)); if ($result->status_code === 200) { - $content = $result->body; + $content = trim($result->body); } - - - if (strlen(trim($content)) == 0) { - $content = '

' . strval(trans('firefly.route_has_no_help')) . '

'; + if (strlen($content) > 0) { + Log::debug('Content is longer than zero. Expect something.'); + $converter = new CommonMarkConverter(); + $content = $converter->convertToHtml($content); } - $converter = new CommonMarkConverter(); - $content = $converter->convertToHtml($content); return $content; @@ -83,7 +93,16 @@ class Help implements HelpInterface */ public function inCache(string $route, string $language):bool { - return Cache::has('help.' . $route . '.' . $language); + $result = Cache::has('help.' . $route . '.' . $language); + if ($result) { + Log::debug(sprintf('Cache has this entry: %s', 'help.' . $route . '.' . $language)); + } + if (!$result) { + Log::debug(sprintf('Cache does not have this entry: %s', 'help.' . $route . '.' . $language)); + } + + return $result; + } /** @@ -96,6 +115,8 @@ class Help implements HelpInterface */ public function putInCache(string $route, string $language, string $content) { - Cache::put('help.' . $route . '.' . $language, $content, 10080); // a week. + $key = 'help.' . $route . '.' . $language; + Log::debug(sprintf('Will store entry in cache: %s', $key)); + Cache::put($key, $content, 10080); // a week. } } diff --git a/app/Http/Controllers/HelpController.php b/app/Http/Controllers/HelpController.php index 4b56c6e89e..bd1f34c581 100644 --- a/app/Http/Controllers/HelpController.php +++ b/app/Http/Controllers/HelpController.php @@ -42,8 +42,9 @@ class HelpController extends Controller public function show(HelpInterface $help, string $route) { - $language = Preferences::get('language', config('firefly.default_language', 'en_US'))->data; - $content = '

' . strval(trans('firefly.route_has_no_help')) . '

'; + $language = Preferences::get('language', config('firefly.default_language', 'en_US'))->data; + $content = '

' . strval(trans('firefly.route_has_no_help')) . '

'; + $alternative = false; if (!$help->hasRoute($route)) { Log::error('No such route: ' . $route); @@ -60,6 +61,21 @@ class HelpController extends Controller $content = $help->getFromGithub($language, $route); + // get backup language content (try English): + if (strlen($content) === 0) { + $language = 'en_US'; + $content = $help->getFromGithub($language, $route); + $alternative = true; + } + + if ($alternative && strlen($content) > 0) { + $content = '

' . strval(trans('firefly.help_may_not_be_your_language')) . '

' . $content; + } + + if (strlen($content) === 0) { + $content = '

' . strval(trans('firefly.route_has_no_help')) . '

'; + } + $help->putInCache($route, $language, $content); return Response::json($content); diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index b15faf48fa..435ab3f18b 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -51,7 +51,8 @@ return [ 'flash_info_multiple' => 'There is one message|There are :count messages', 'flash_error_multiple' => 'There is one error|There are :count errors', 'net_worth' => 'Net worth', - 'route_has_no_help' => 'There is no help for this route, or there is no help available in your language.', + 'route_has_no_help' => 'There is no help for this route.', + 'help_may_not_be_your_language' => 'This help text is in English. It is not yet available in your language', 'two_factor_welcome' => 'Hello, :user!', 'two_factor_enter_code' => 'To continue, please enter your two factor authentication code. Your application can generate it for you.', 'two_factor_code_here' => 'Enter code here', From 16570481818ea43f736bc858672bd5fbeed44b47 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 1 Nov 2016 18:40:35 +0100 Subject: [PATCH 018/709] Fixed bug #370 --- app/Models/TransactionJournal.php | 4 +++- app/Repositories/Category/CategoryRepository.php | 7 +++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index f6f17b9429..3c763b7302 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -408,7 +408,9 @@ class TransactionJournal extends TransactionJournalSupport if (!self::isJoined($query, 'transaction_types')) { $query->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'); } - $query->whereIn('transaction_types.type', $types); + if (count($types) > 0) { + $query->whereIn('transaction_types.type', $types); + } } /** diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 6880813805..cc0ec6b192 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -260,10 +260,9 @@ class CategoryRepository implements CategoryRepositoryInterface // then collection transactions (harder) $query = $this->user->transactionJournals()->distinct() ->leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->leftJoin('category_transaction', 'category_transaction.transaction_id', '=', 'transactions.id'); - + ->leftJoin('category_transaction', 'category_transaction.transaction_id', '=', 'transactions.id') + ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'); if (count($types) > 0) { - $query->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'); $query->whereIn('transaction_types.type', $types); } if ($accounts->count() > 0) { @@ -276,7 +275,7 @@ class CategoryRepository implements CategoryRepositoryInterface } - $second = $query->get(['transaction_journals.*','transaction_types.type as transaction_type_type']); + $second = $query->get(['transaction_journals.*', 'transaction_types.type as transaction_type_type']); $complete = $complete->merge($first); $complete = $complete->merge($second); From fecbdc7fbf40d2a56085208d7822f83ba8f6f5ec Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 1 Nov 2016 18:44:26 +0100 Subject: [PATCH 019/709] New version. Signed-off-by: James Cole --- CHANGELOG.md | 9 +++++++++ composer.lock | 35 +++++++++++++++-------------------- config/firefly.php | 2 +- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4aad0bb631..ac1c4e09df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [4.1.5] - 2016-11-01 +### Changed +- Report parts are loaded using AJAX, making a lot of code more simple. +- Help content will fall back to English. +- Help content is translated through Crowdin. + +### Fixed +- Issue #370 + ## [4.1.4] - 2016-10-30 ### Added - New Dockerfile thanks to @schoentoon diff --git a/composer.lock b/composer.lock index a87c3c8e96..2e8a6bc01f 100644 --- a/composer.lock +++ b/composer.lock @@ -3117,16 +3117,16 @@ }, { "name": "watson/validating", - "version": "3.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/dwightwatson/validating.git", - "reference": "8ae5915976ddc152da54efcc03240d17370a60c3" + "reference": "3cef5b4cd0af2dc26d2c7ca668bd12f4d4ab421b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dwightwatson/validating/zipball/8ae5915976ddc152da54efcc03240d17370a60c3", - "reference": "8ae5915976ddc152da54efcc03240d17370a60c3", + "url": "https://api.github.com/repos/dwightwatson/validating/zipball/3cef5b4cd0af2dc26d2c7ca668bd12f4d4ab421b", + "reference": "3cef5b4cd0af2dc26d2c7ca668bd12f4d4ab421b", "shasum": "" }, "require": { @@ -3142,11 +3142,6 @@ "phpunit/phpunit": "~4.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, "autoload": { "psr-4": { "Watson\\Validating\\": "src/" @@ -3168,7 +3163,7 @@ "laravel", "validation" ], - "time": "2016-08-28 13:39:22" + "time": "2016-10-31 21:53:17" } ], "packages-dev": [ @@ -3386,16 +3381,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.5.4", + "version": "1.5.5", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "ea74994a3dc7f8d2f65a06009348f2d63c81e61f" + "reference": "399c1f9781e222f6eb6cc238796f5200d1b7f108" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/ea74994a3dc7f8d2f65a06009348f2d63c81e61f", - "reference": "ea74994a3dc7f8d2f65a06009348f2d63c81e61f", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/399c1f9781e222f6eb6cc238796f5200d1b7f108", + "reference": "399c1f9781e222f6eb6cc238796f5200d1b7f108", "shasum": "" }, "require": { @@ -3424,7 +3419,7 @@ "object", "object graph" ], - "time": "2016-09-16 13:37:59" + "time": "2016-10-31 17:19:45" }, { "name": "phpdocumentor/reflection-common", @@ -3636,16 +3631,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "4.0.1", + "version": "4.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "5f3f7e736d6319d5f1fc402aff8b026da26709a3" + "reference": "6cba06ff75a1a63a71033e1a01b89056f3af1e8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5f3f7e736d6319d5f1fc402aff8b026da26709a3", - "reference": "5f3f7e736d6319d5f1fc402aff8b026da26709a3", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6cba06ff75a1a63a71033e1a01b89056f3af1e8d", + "reference": "6cba06ff75a1a63a71033e1a01b89056f3af1e8d", "shasum": "" }, "require": { @@ -3695,7 +3690,7 @@ "testing", "xunit" ], - "time": "2016-07-26 14:39:29" + "time": "2016-11-01 05:06:24" }, { "name": "phpunit/php-file-iterator", diff --git a/config/firefly.php b/config/firefly.php index 69b5e13029..97b843110a 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -22,7 +22,7 @@ return [ 'single_user_mode' => true, ], 'chart' => 'chartjs', - 'version' => '4.1.4', + 'version' => '4.1.5', 'csv_import_enabled' => true, 'maxUploadSize' => 5242880, 'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'], From 33c0c1bea668a09b48b989560388700312a8e482 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 1 Nov 2016 19:06:35 +0100 Subject: [PATCH 020/709] Removed everything pointless from multi year report. Signed-off-by: James Cole --- .../Budget/BudgetChartGeneratorInterface.php | 7 - .../Budget/ChartJsBudgetChartGenerator.php | 34 ----- .../CategoryChartGeneratorInterface.php | 7 - .../ChartJsCategoryChartGenerator.php | 28 ---- .../Controllers/Chart/BudgetController.php | 74 ---------- .../Controllers/Chart/CategoryController.php | 72 --------- app/Http/Controllers/ReportController.php | 10 +- public/js/ff/reports/default/multi-year.js | 138 ------------------ .../views/reports/default/multi-year.twig | 82 ----------- routes/web.php | 5 - 10 files changed, 2 insertions(+), 455 deletions(-) diff --git a/app/Generator/Chart/Budget/BudgetChartGeneratorInterface.php b/app/Generator/Chart/Budget/BudgetChartGeneratorInterface.php index fc466aa418..4983d9635a 100644 --- a/app/Generator/Chart/Budget/BudgetChartGeneratorInterface.php +++ b/app/Generator/Chart/Budget/BudgetChartGeneratorInterface.php @@ -38,13 +38,6 @@ interface BudgetChartGeneratorInterface */ public function frontpage(Collection $entries): array; - /** - * @param Collection $entries - * - * @return array - */ - public function multiYear(Collection $entries): array; - /** * @param Collection $entries * @param string $viewRange diff --git a/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php b/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php index f82fb807c5..cf78936c4d 100644 --- a/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php +++ b/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php @@ -100,40 +100,6 @@ class ChartJsBudgetChartGenerator implements BudgetChartGeneratorInterface return $data; } - /** - * @param Collection $entries - * - * @return array - */ - public function multiYear(Collection $entries): array - { - // dataset: - $data = [ - 'count' => 0, - 'labels' => [], - 'datasets' => [], - ]; - // get labels from one of the budgets (assuming there's at least one): - $first = $entries->first(); - $keys = array_keys($first['budgeted']); - foreach ($keys as $year) { - $data['labels'][] = strval($year); - } - - // then, loop all entries and create datasets: - foreach ($entries as $entry) { - $name = $entry['name']; - $spent = $entry['spent']; - $budgeted = $entry['budgeted']; - $data['datasets'][] = ['label' => 'Spent on ' . $name, 'data' => array_values($spent)]; - $data['datasets'][] = ['label' => 'Budgeted for ' . $name, 'data' => array_values($budgeted)]; - } - $data['count'] = count($data['datasets']); - - return $data; - - } - /** * @param Collection $entries * @param string $viewRange diff --git a/app/Generator/Chart/Category/CategoryChartGeneratorInterface.php b/app/Generator/Chart/Category/CategoryChartGeneratorInterface.php index 56d57f6966..e17022d9b6 100644 --- a/app/Generator/Chart/Category/CategoryChartGeneratorInterface.php +++ b/app/Generator/Chart/Category/CategoryChartGeneratorInterface.php @@ -45,13 +45,6 @@ interface CategoryChartGeneratorInterface */ public function frontpage(Collection $entries): array; - /** - * @param Collection $entries - * - * @return array - */ - public function multiYear(Collection $entries): array; - /** * @param Collection $entries * diff --git a/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php b/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php index 631b867cd1..560b4f95bb 100644 --- a/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php +++ b/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php @@ -117,34 +117,6 @@ class ChartJsCategoryChartGenerator implements CategoryChartGeneratorInterface return $data; } - /** - * @param Collection $entries - * - * @return array - */ - public function multiYear(Collection $entries): array - { - // get labels from one of the categories (assuming there's at least one): - $first = $entries->first(); - $data = ['count' => 0, 'labels' => array_keys($first['spent']), 'datasets' => [],]; - - // then, loop all entries and create datasets: - foreach ($entries as $entry) { - $name = $entry['name']; - $spent = $entry['spent']; - $earned = $entry['earned']; - if (array_sum(array_values($spent)) != 0) { - $data['datasets'][] = ['label' => 'Spent in category ' . $name, 'data' => array_values($spent)]; - } - if (array_sum(array_values($earned)) != 0) { - $data['datasets'][] = ['label' => 'Earned in category ' . $name, 'data' => array_values($earned)]; - } - } - $data['count'] = count($data['datasets']); - - return $data; - } - /** * * @param Collection $entries diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index 56eeb966ef..3e6c5f1c83 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -183,80 +183,6 @@ class BudgetController extends Controller return Response::json($data); } - /** - * - * @param BudgetRepositoryInterface $repository - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * @param Collection $budgets - * - * - * @return \Illuminate\Http\JsonResponse - */ - public function multiYear(BudgetRepositoryInterface $repository, Carbon $start, Carbon $end, Collection $accounts, Collection $budgets) - { - - $cache = new CacheProperties(); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty($accounts); - $cache->addProperty($budgets); - $cache->addProperty('multiYearBudget'); - - if ($cache->has()) { - return Response::json($cache->get()); - } - $budgetIds = $budgets->pluck('id')->toArray(); - $repetitions = $repository->getAllBudgetLimitRepetitions($start, $end); - $budgeted = []; - $entries = new Collection; - // filter budgets once: - $repetitions = $repetitions->filter( - function (LimitRepetition $repetition) use ($budgetIds) { - if (in_array(strval($repetition->budget_id), $budgetIds)) { - return true; - } - - return false; - } - ); - /** @var LimitRepetition $repetition */ - foreach ($repetitions as $repetition) { - $year = $repetition->startdate->year; - if (isset($budgeted[$repetition->budget_id][$year])) { - $budgeted[$repetition->budget_id][$year] = bcadd($budgeted[$repetition->budget_id][$year], $repetition->amount); - continue; - } - $budgeted[$repetition->budget_id][$year] = $repetition->amount; - } - - foreach ($budgets as $budget) { - $currentStart = clone $start; - $entry = ['name' => $budget->name, 'spent' => [], 'budgeted' => []]; - while ($currentStart < $end) { - // fix the date: - $currentEnd = clone $currentStart; - $year = $currentStart->year; - $currentEnd->endOfYear(); - - $spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $currentStart, $currentEnd); - - // jump to next year. - $currentStart = clone $currentEnd; - $currentStart->addDay(); - - $entry['spent'][$year] = round($spent * -1, 2); - $entry['budgeted'][$year] = isset($budgeted[$budget->id][$year]) ? round($budgeted[$budget->id][$year], 2) : 0; - } - $entries->push($entry); - } - $data = $this->generator->multiYear($entries); - $cache->store($data); - - return Response::json($data); - } - /** * @param BudgetRepositoryInterface $repository * @param Budget $budget diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 98a8392ca4..f8d9882e28 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -153,78 +153,6 @@ class CategoryController extends Controller } - /** - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * @param Collection $categories - * - * @return \Illuminate\Http\JsonResponse - */ - public function multiYear(Carbon $start, Carbon $end, Collection $accounts, Collection $categories) - { - - /** @var CRI $repository */ - $repository = app(CRI::class); - - // chart properties for cache: - $cache = new CacheProperties(); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty($accounts); - $cache->addProperty($categories); - $cache->addProperty('multiYearCategory'); - - if ($cache->has()) { - return Response::json($cache->get()); - } - - $entries = new Collection; - - /** @var Category $category */ - foreach ($categories as $category) { - $entry = ['name' => '', 'spent' => [], 'earned' => []]; - - $currentStart = clone $start; - while ($currentStart < $end) { - // fix the date: - $year = $currentStart->year; - $currentEnd = clone $currentStart; - $currentEnd->endOfYear(); - - // get data: - if (is_null($category->id)) { - $entry['name'] = trans('firefly.noCategory'); - $entry['spent'][$year] = ($repository->spentInPeriodWithoutCategory($accounts, $currentStart, $currentEnd) * -1); - $entry['earned'][$year] = $repository->earnedInPeriodWithoutCategory($accounts, $currentStart, $currentEnd); - - // jump to next year. - $currentStart = clone $currentEnd; - $currentStart->addDay(); - continue; - - } - // alternative is a normal category: - $entry['name'] = $category->name; - $entry['spent'][$year] = ($repository->spentInPeriod(new Collection([$category]), $accounts, $currentStart, $currentEnd) * -1); - $entry['earned'][$year] = $repository->earnedInPeriod(new Collection([$category]), $accounts, $currentStart, $currentEnd); - - // jump to next year. - $currentStart = clone $currentEnd; - $currentStart->addDay(); - } - $entries->push($entry); - } - - // generate chart with data: - $data = $this->generator->multiYear($entries); - $cache->store($data); - - return Response::json($data); - - - } - /** * @param CRI $repository * @param Category $category diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index 77a50d7218..9c0d213260 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -245,10 +245,6 @@ class ReportController extends Controller private function defaultMultiYear(string $reportType, Carbon $start, Carbon $end, Collection $accounts) { - $budgets = app(BudgetRepositoryInterface::class)->getActiveBudgets(); - $categories = app(CategoryRepositoryInterface::class)->getCategories(); - $tags = $this->helper->tagReport($start, $end, $accounts); - // and some id's, joined: $accountIds = []; /** @var Account $account */ @@ -260,7 +256,7 @@ class ReportController extends Controller return view( 'reports.default.multi-year', compact( - 'budgets', 'accounts', 'categories', 'start', 'end', 'accountIds', 'reportType', 'tags' + 'accounts', 'start', 'end', 'accountIds', 'reportType' ) ); } @@ -275,8 +271,6 @@ class ReportController extends Controller */ private function defaultYear(string $reportType, Carbon $start, Carbon $end, Collection $accounts) { - $tags = $this->helper->tagReport($start, $end, $accounts); - Session::flash('gaEventCategory', 'report'); Session::flash('gaEventAction', 'year'); Session::flash('gaEventLabel', $start->format('Y')); @@ -293,7 +287,7 @@ class ReportController extends Controller 'reports.default.year', compact( 'start', 'reportType', - 'accountIds', 'end', 'tags' + 'accountIds', 'end' ) ); } diff --git a/public/js/ff/reports/default/multi-year.js b/public/js/ff/reports/default/multi-year.js index 78845f5100..04b9ab3ddb 100644 --- a/public/js/ff/reports/default/multi-year.js +++ b/public/js/ff/reports/default/multi-year.js @@ -15,142 +15,4 @@ function drawChart() { lineChart('chart/report/net-worth/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'net-worth'); columnChart('chart/report/in-out/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-chart'); columnChart('chart/report/in-out-sum/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-sum-chart'); - - - $.each($('.account-chart'), function (i, v) { - var holder = $(v); - console.log('Will draw chart for account #' + holder.data('id')); - }); - - // draw budget chart based on selected budgets: - $('.budget-checkbox').on('change', updateBudgetChart); - selectBudgetsByCookie(); - updateBudgetChart(); - - // draw category chart based on selected budgets: - $('.category-checkbox').on('change', updateCategoryChart); - selectCategoriesByCookie(); - updateCategoryChart(); } - -function selectBudgetsByCookie() { - "use strict"; - var cookie = readCookie('multi-year-budgets'); - if (cookie !== null) { - var cookieArray = cookie.split(','); - for (var x in cookieArray) { - var budgetId = cookieArray[x]; - $('.budget-checkbox[value="' + budgetId + '"').prop('checked', true); - } - } -} - -function selectCategoriesByCookie() { - "use strict"; - var cookie = readCookie('multi-year-categories'); - if (cookie !== null) { - var cookieArray = cookie.split(','); - for (var x in cookieArray) { - var categoryId = cookieArray[x]; - $('.category-checkbox[value="' + categoryId + '"').prop('checked', true); - } - } -} - -function updateBudgetChart() { - "use strict"; - console.log('will update budget chart.'); - // get all budget ids: - var budgets = []; - $.each($('.budget-checkbox'), function (i, v) { - var current = $(v); - if (current.prop('checked')) { - budgets.push(current.val()); - } - }); - - if (budgets.length > 0) { - - var budgetIds = budgets.join(','); - - // remove old chart: - $('#budgets-chart').replaceWith(''); - - // hide message: - $('#budgets-chart-message').hide(); - - // draw chart. Redraw when exists? Not sure if we support that. - columnChart('chart/budget/multi-year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds + '/' + budgetIds, 'budgets-chart'); - createCookie('multi-year-budgets', budgets, 365); - } else { - // hide canvas, show message: - $('#budgets-chart-message').show(); - $('#budgets-chart').hide(); - - } - -} - -function updateCategoryChart() { - "use strict"; - console.log('will update category chart.'); - // get all category ids: - var categories = []; - $.each($('.category-checkbox'), function (i, v) { - var current = $(v); - if (current.prop('checked')) { - categories.push(current.val()); - } - }); - - if (categories.length > 0) { - - var categoryIds = categories.join(','); - - // remove old chart: - $('#categories-chart').replaceWith(''); - - // hide message: - $('#categories-chart-message').hide(); - - // draw chart. Redraw when exists? Not sure if we support that. - columnChart('chart/category/multi-year/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds + '/' + categoryIds, 'categories-chart'); - createCookie('multi-year-categories', categories, 365); - } else { - // hide canvas, show message: - $('#categories-chart-message').show(); - $('#categories-chart').hide(); - - } -} - - -function createCookie(name, value, days) { - "use strict"; - var expires; - - if (days) { - var date = new Date(); - date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); - expires = "; expires=" + date.toGMTString(); - } else { - expires = ""; - } - document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/"; -} - -function readCookie(name) { - "use strict"; - var nameEQ = encodeURIComponent(name) + "="; - var ca = document.cookie.split(';'); - for (var i = 0; i < ca.length; i++) { - var c = ca[i]; - while (c.charAt(0) === ' ') c = c.substring(1, c.length); - if (c.indexOf(nameEQ) === 0) return decodeURIComponent(c.substring(nameEQ.length, c.length)); - } - return null; -} - -function eraseCookie(name) { - createCookie(name, "", -1); -} \ No newline at end of file diff --git a/resources/views/reports/default/multi-year.twig b/resources/views/reports/default/multi-year.twig index 9eeff7cafe..121784617c 100644 --- a/resources/views/reports/default/multi-year.twig +++ b/resources/views/reports/default/multi-year.twig @@ -74,23 +74,6 @@
- - {% for account in accounts %} - - {% endfor %} -
@@ -104,71 +87,6 @@
-
-
-
-
-

Selected budgets

-
-
-
-
- - -
-
-
-
- - {% for budget in budgets %} - - {% endfor %} -
-
-
-
-
-
- -
-
-
-
-

Selected categories

-
-
-
-
- - -
-
-
-
- - {% for category in categories %} - - {% endfor %} -
-
-
-
-
-
- - {% endblock %} {% block styles %} diff --git a/routes/web.php b/routes/web.php index 69fd01df55..7a5072cf29 100755 --- a/routes/web.php +++ b/routes/web.php @@ -198,17 +198,12 @@ Route::group( Route::get('/chart/budget/frontpage', ['uses' => 'Chart\BudgetController@frontpage']); // this chart is used in reports: - Route::get('/chart/budget/multi-year/default/{start_date}/{end_date}/{accountList}/{budgetList}', ['uses' => 'Chart\BudgetController@multiYear']); - Route::get('/chart/budget/period/{budget}/default/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\BudgetController@period']); Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'Chart\BudgetController@budgetLimit']); Route::get('/chart/budget/{budget}', ['uses' => 'Chart\BudgetController@budget']); // categories: Route::get('/chart/category/frontpage', ['uses' => 'Chart\CategoryController@frontpage']); - // these three charts are for reports: - Route::get('/chart/category/multi-year/default/{start_date}/{end_date}/{accountList}/{categoryList}', ['uses' => 'Chart\CategoryController@multiYear']); - Route::get('/chart/category/{category}/period', ['uses' => 'Chart\CategoryController@currentPeriod']); Route::get('/chart/category/{category}/period/{date}', ['uses' => 'Chart\CategoryController@specificPeriod']); Route::get('/chart/category/{category}/all', ['uses' => 'Chart\CategoryController@all']); From 124ecb1372607a62735478ccff1cb979994eb0a2 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 2 Nov 2016 04:55:44 +0100 Subject: [PATCH 021/709] New budget table for multi year report. Signed-off-by: James Cole --- app/Helpers/Report/ReportHelper.php | 107 ++++++++++++++++++ app/Helpers/Report/ReportHelperInterface.php | 18 +++ app/Http/Controllers/ReportController.php | 10 +- .../views/reports/default/multi-year.twig | 41 ++++++- 4 files changed, 170 insertions(+), 6 deletions(-) diff --git a/app/Helpers/Report/ReportHelper.php b/app/Helpers/Report/ReportHelper.php index f9df70c397..cd8520e29d 100644 --- a/app/Helpers/Report/ReportHelper.php +++ b/app/Helpers/Report/ReportHelper.php @@ -14,6 +14,7 @@ declare(strict_types = 1); namespace FireflyIII\Helpers\Report; use Carbon\Carbon; +use DB; use FireflyIII\Helpers\Collection\Bill as BillCollection; use FireflyIII\Helpers\Collection\BillLine; use FireflyIII\Helpers\Collection\Category as CategoryCollection; @@ -21,6 +22,7 @@ use FireflyIII\Helpers\Collection\Expense; use FireflyIII\Helpers\Collection\Income; use FireflyIII\Helpers\FiscalHelperInterface; use FireflyIII\Models\Bill; +use FireflyIII\Models\Budget; use FireflyIII\Models\Category; use FireflyIII\Models\Tag; use FireflyIII\Models\TransactionJournal; @@ -111,6 +113,67 @@ class ReportHelper implements ReportHelperInterface return $collection; } + /** + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return array + */ + public function getBudgetMultiYear(Carbon $start, Carbon $end, Collection $accounts): array + { + $accountIds = $accounts->pluck('id')->toArray(); + $query = TransactionJournal + ::leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') + ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') + ->leftJoin( + 'transactions', function (JoinClause $join) { + $join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0); + } + ) + ->whereNull('transaction_journals.deleted_at') + ->whereNull('transactions.deleted_at') + ->where('transaction_types.type', 'Withdrawal') + ->where('transaction_journals.user_id', auth()->user()->id); + + if (count($accountIds) > 0) { + $query->whereIn('transactions.account_id', $accountIds); + } + $query->groupBy(['budget_transaction_journal.budget_id', 'the_year']); + $queryResult = $query->get( + [ + 'budget_transaction_journal.budget_id', + DB::raw('DATE_FORMAT(transaction_journals.date,"%Y") AS the_year'), + DB::raw('SUM(transactions.amount) as sum_of_period'), + ] + ); + + $data = []; + $budgets = $this->budgetRepository->getBudgets(); + $years = $this->listOfYears($start, $end); + + // do budget "zero" + $emptyBudget = new Budget; + $emptyBudget->id = 0; + $emptyBudget->name = strval(trans('firefly.no_budget')); + $budgets->push($emptyBudget); + + + // get all budgets and years. + foreach ($budgets as $budget) { + $data[$budget->id] = [ + 'name' => $budget->name, + 'entries' => [], + ]; + foreach ($years as $year) { + // filter query result here! + $data[$budget->id]['entries'][$year] = $this->filterAmount($queryResult, $budget->id, $year); + } + } + + return $data; + } + /** * @param Carbon $start * @param Carbon $end @@ -231,6 +294,24 @@ class ReportHelper implements ReportHelperInterface return $months; } + /** + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + public function listOfYears(Carbon $start, Carbon $end): array + { + $begin = clone $start; + $years = []; + while ($begin < $end) { + $years[] = $begin->year; + $begin->addYear(); + } + + return $years; + } + /** * Returns an array of tags and their comparitive size with amounts bla bla. * @@ -305,6 +386,30 @@ class ReportHelper implements ReportHelperInterface return $collection; } + /** + * @param Collection $set + * @param int $budgetId + * @param int $year + * + * @return string + */ + protected function filterAmount(Collection $set, int $budgetId, int $year): string + { + /** @var stdClass $object */ + $result = $set->filter( + function (TransactionJournal $object) use ($budgetId, $year) { + return intval($object->the_year) === $year && $budgetId === intval($object->budget_id); + } + ); + $amount = '0'; + if (!is_null($result->first())) { + $amount = $result->first()->sum_of_period; + } + + return $amount; + + } + /** * Take the array as returned by CategoryRepositoryInterface::spentPerDay and CategoryRepositoryInterface::earnedByDay * and sum up everything in the array in the given range. @@ -331,4 +436,6 @@ class ReportHelper implements ReportHelperInterface return $sum; } + + } diff --git a/app/Helpers/Report/ReportHelperInterface.php b/app/Helpers/Report/ReportHelperInterface.php index 34ffcc9e7b..6dec1b9c0e 100644 --- a/app/Helpers/Report/ReportHelperInterface.php +++ b/app/Helpers/Report/ReportHelperInterface.php @@ -28,6 +28,16 @@ use Illuminate\Support\Collection; interface ReportHelperInterface { + /** + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return array + */ + public function getBudgetMultiYear(Carbon $start, Carbon $end, Collection $accounts): array; + + /** * This method generates a full report for the given period on all * the users bills and their payments. @@ -80,6 +90,14 @@ interface ReportHelperInterface */ public function listOfMonths(Carbon $date): array; + /** + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + public function listOfYears(Carbon $start, Carbon $end): array; + /** * Returns an array of tags and their comparitive size with amounts bla bla. * diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index 9c0d213260..7878830f15 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -21,8 +21,6 @@ use FireflyIII\Models\AccountType; use FireflyIII\Models\Transaction; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountTaskerInterface; -use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; -use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use Illuminate\Support\Collection; use Preferences; use Session; @@ -244,6 +242,11 @@ class ReportController extends Controller */ private function defaultMultiYear(string $reportType, Carbon $start, Carbon $end, Collection $accounts) { + // need all budgets + // need all years. + $years = $this->helper->listOfYears($start, $end); + $budgetMultiYear = $this->helper->getBudgetMultiYear($start, $end, $accounts); + // and some id's, joined: $accountIds = []; @@ -256,7 +259,8 @@ class ReportController extends Controller return view( 'reports.default.multi-year', compact( - 'accounts', 'start', 'end', 'accountIds', 'reportType' + 'accounts', 'start', 'end', 'accountIds', 'reportType', + 'years', 'budgetMultiYear' ) ); } diff --git a/resources/views/reports/default/multi-year.twig b/resources/views/reports/default/multi-year.twig index 121784617c..314415d2f8 100644 --- a/resources/views/reports/default/multi-year.twig +++ b/resources/views/reports/default/multi-year.twig @@ -69,9 +69,6 @@
-
- {% include 'reports/partials/tags.twig' %} -
@@ -87,6 +84,44 @@
+ +
+
+
+
+

{{ 'budgets'|_ }}

+
+
+ + + + + {% for year in years %} + + {% endfor %} + + + + + + {% for id, info in budgetMultiYear %} + + + {% for amount in info.entries %} + + {% endfor %} + + + {% endfor %} + +
Budget{{ year }}
{{ info.name }} + {{ amount|formatAmount }} +
+
+
+
+
+ {% endblock %} {% block styles %} From 39917b77c153e8257e2dfad794a22722758acb4b Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 2 Nov 2016 07:02:22 +0100 Subject: [PATCH 022/709] New GitHub move repository instructions Signed-off-by: James Cole --- app/Console/Commands/MoveRepository.php | 86 +++++++++++++++++++ .../Commands/UpgradeFireflyInstructions.php | 12 +-- app/Console/Kernel.php | 3 +- 3 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 app/Console/Commands/MoveRepository.php diff --git a/app/Console/Commands/MoveRepository.php b/app/Console/Commands/MoveRepository.php new file mode 100644 index 0000000000..51d170d7c3 --- /dev/null +++ b/app/Console/Commands/MoveRepository.php @@ -0,0 +1,86 @@ + $now) { + $this->line('+------------------------------------------------------------------------------+'); + $this->line(''); + $this->line('The Github repository for Firefly III will MOVE'); + $this->line('This move will be on January 1st 2017'); + $this->line(''); + $this->error('READ THIS WIKI PAGE FOR MORE INFORMATION'); + $this->line(''); + $this->info('https://github.com/firefly-iii/help/wiki/New-Github-repository'); + $this->line(''); + $this->line('+------------------------------------------------------------------------------+'); + } + + // display message after 2017-01-01 but before 2017-03-01 + if ($moveDate <= $now && $now <= $final) { + $this->line('+------------------------------------------------------------------------------+'); + $this->line(''); + $this->line('The Github repository for Firefly III has MOVED'); + $this->line('This move was on January 1st 2017!'); + $this->line(''); + $this->error('READ THIS WIKI PAGE FOR MORE INFORMATION'); + $this->line(''); + $this->info('https://github.com/firefly-iii/help/wiki/New-Github-repository'); + $this->line(''); + $this->line('+------------------------------------------------------------------------------+'); + } + + } +} diff --git a/app/Console/Commands/UpgradeFireflyInstructions.php b/app/Console/Commands/UpgradeFireflyInstructions.php index 2a4a801ade..234b075f7a 100644 --- a/app/Console/Commands/UpgradeFireflyInstructions.php +++ b/app/Console/Commands/UpgradeFireflyInstructions.php @@ -63,21 +63,21 @@ class UpgradeFireflyInstructions extends Command } - $this->line('+------------------------------------------------------------------------------+'); - $this->line(''); + if (is_null($text)) { $this->line('Thank you for installing Firefly III, v' . $version); $this->info('There are no extra upgrade instructions.'); $this->line('Firefly III should be ready for use.'); } else { + $this->line('+------------------------------------------------------------------------------+'); + $this->line(''); $this->line('Thank you for installing Firefly III, v' . $version); - $this->line('If you are upgrading from a previous version,'); - $this->line('please follow these upgrade instructions carefully:'); $this->info(wordwrap($text)); + $this->line(''); + $this->line('+------------------------------------------------------------------------------+'); } - $this->line(''); - $this->line('+------------------------------------------------------------------------------+'); + } } diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index d62f5371ca..7c9c52fc01 100755 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -16,6 +16,7 @@ namespace FireflyIII\Console; use FireflyIII\Console\Commands\CreateImport; use FireflyIII\Console\Commands\EncryptFile; use FireflyIII\Console\Commands\Import; +use FireflyIII\Console\Commands\MoveRepository; use FireflyIII\Console\Commands\ScanAttachments; use FireflyIII\Console\Commands\UpgradeDatabase; use FireflyIII\Console\Commands\UpgradeFireflyInstructions; @@ -63,7 +64,7 @@ class Kernel extends ConsoleKernel EncryptFile::class, ScanAttachments::class, UpgradeDatabase::class, - + MoveRepository::class, ]; /** From 5be317d73c9613df5b9760cc6eedb77a96e94802 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 2 Nov 2016 07:04:14 +0100 Subject: [PATCH 023/709] sprintf ALL THE THINGS Signed-off-by: James Cole --- app/Console/Commands/Import.php | 2 +- app/Console/Commands/VerifyDatabase.php | 7 +++++-- app/Helpers/Help/Help.php | 9 ++++++--- app/Http/Controllers/HelpController.php | 2 +- app/Support/Amount.php | 12 ++++++------ app/Support/Twig/Journal.php | 21 ++++++++++----------- app/Support/Twig/Transaction.php | 12 ++++++------ app/Support/Twig/Translation.php | 2 +- 8 files changed, 36 insertions(+), 31 deletions(-) diff --git a/app/Console/Commands/Import.php b/app/Console/Commands/Import.php index 0459e1c75b..09a1f5bec9 100644 --- a/app/Console/Commands/Import.php +++ b/app/Console/Commands/Import.php @@ -66,7 +66,7 @@ class Import extends Command return; } - $this->line('Going to import job with key "' . $job->key . '" of type ' . $job->file_type); + $this->line(sprintf('Going to import job with key "%s" of type "%s"', $job->key, $job->file_type)); $monolog = Log::getMonolog(); $handler = new CommandHandler($this); diff --git a/app/Console/Commands/VerifyDatabase.php b/app/Console/Commands/VerifyDatabase.php index 8571d599af..6050902ecd 100644 --- a/app/Console/Commands/VerifyDatabase.php +++ b/app/Console/Commands/VerifyDatabase.php @@ -127,8 +127,11 @@ class VerifyDatabase extends Command /** @var stdClass $entry */ foreach ($set as $entry) { - $line = 'Notice: User #' . $entry->user_id . ' (' . $entry->email . ') has budget #' . $entry->id . ' ("' . Crypt::decrypt($entry->name) - . '") which has no budget limits.'; + + $line = sprintf( + 'Notice: User #%d (%s) has budget #%d ("%s") which has no budget limits.', + $entry->user_id, $entry->email, $entry->id, Crypt::decrypt($entry->name) + ); $this->line($line); } } diff --git a/app/Helpers/Help/Help.php b/app/Helpers/Help/Help.php index 4b19963bef..a4546e49af 100644 --- a/app/Helpers/Help/Help.php +++ b/app/Helpers/Help/Help.php @@ -35,7 +35,9 @@ class Help implements HelpInterface */ public function getFromCache(string $route, string $language): string { - return Cache::get('help.' . $route . '.' . $language); + $line = sprintf('help.%s.%s', $route, $language); + + return Cache::get($line); } /** @@ -93,7 +95,8 @@ class Help implements HelpInterface */ public function inCache(string $route, string $language):bool { - $result = Cache::has('help.' . $route . '.' . $language); + $line = sprintf('help.%s.%s', $route, $language); + $result = Cache::has($line); if ($result) { Log::debug(sprintf('Cache has this entry: %s', 'help.' . $route . '.' . $language)); } @@ -115,7 +118,7 @@ class Help implements HelpInterface */ public function putInCache(string $route, string $language, string $content) { - $key = 'help.' . $route . '.' . $language; + $key = sprintf('help.%s.%s', $route, $language); Log::debug(sprintf('Will store entry in cache: %s', $key)); Cache::put($key, $content, 10080); // a week. } diff --git a/app/Http/Controllers/HelpController.php b/app/Http/Controllers/HelpController.php index bd1f34c581..3096ac15eb 100644 --- a/app/Http/Controllers/HelpController.php +++ b/app/Http/Controllers/HelpController.php @@ -75,7 +75,7 @@ class HelpController extends Controller if (strlen($content) === 0) { $content = '

' . strval(trans('firefly.route_has_no_help')) . '

'; } - + $help->putInCache($route, $language, $content); return Response::json($content); diff --git a/app/Support/Amount.php b/app/Support/Amount.php index 7937ae148a..c283289933 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -59,14 +59,14 @@ class Amount if ($coloured === true) { if ($amount > 0) { - return '' . $result . ''; + return sprintf('%s', $result); } else { if ($amount < 0) { - return '' . $result . ''; + return sprintf('%s', $result); } } - return '' . $result . ''; + return sprintf('%s', $result); } @@ -139,14 +139,14 @@ class Amount if ($coloured === true) { if ($amount > 0) { - return '' . $result . ''; + return sprintf('%s', $result); } else { if ($amount < 0) { - return '' . $result . ''; + return sprintf('%s', $result); } } - return '' . $result . ''; + return sprintf('%s', $result); } diff --git a/app/Support/Twig/Journal.php b/app/Support/Twig/Journal.php index 62f99d89a6..e98f0ee070 100644 --- a/app/Support/Twig/Journal.php +++ b/app/Support/Twig/Journal.php @@ -146,7 +146,7 @@ class Journal extends Twig_Extension $array[] = '(cash)'; continue; } - $array[] = '' . e($entry->name) . ''; + $array[] = sprintf('%1$s', e($entry->name), route('accounts.show', $entry->id)); } $array = array_unique($array); $result = join(', ', $array); @@ -221,7 +221,7 @@ class Journal extends Twig_Extension $array[] = '(cash)'; continue; } - $array[] = '' . e($entry->name) . ''; + $array[] = sprintf('%1$s', e($entry->name), route('accounts.show', $entry->id)); } $array = array_unique($array); $result = join(', ', $array); @@ -253,12 +253,12 @@ class Journal extends Twig_Extension $budgets = []; // get all budgets: foreach ($journal->budgets as $budget) { - $budgets[] = '' . e($budget->name) . ''; + $budgets[] = sprintf('%1$s', e($budget->name), route('accounts.show', $budget->id)); } // and more! foreach ($journal->transactions as $transaction) { foreach ($transaction->budgets as $budget) { - $budgets[] = '' . e($budget->name) . ''; + $budgets[] = sprintf('%1$s', e($budget->name), route('accounts.show', $budget->id)); } } $string = join(', ', array_unique($budgets)); @@ -288,7 +288,7 @@ class Journal extends Twig_Extension $categories = []; // get all categories for the journal itself (easy): foreach ($journal->categories as $category) { - $categories[] = '' . e($category->name) . ''; + $categories[] = sprintf('%1$s', e($category->name), route('accounts.show', $category->id)); } if (count($categories) === 0) { $set = Category::distinct()->leftJoin('category_transaction', 'categories.id', '=', 'category_transaction.category_id') @@ -299,8 +299,7 @@ class Journal extends Twig_Extension ->get(['categories.*']); /** @var Category $category */ foreach ($set as $category) { - $categories[] = '' . e($category->name) - . ''; + $categories[] = sprintf('%1$s', e($category->name), route('accounts.show', $category->id)); } } @@ -324,16 +323,16 @@ class Journal extends Twig_Extension switch (true) { case $journal->isWithdrawal(): - $txt = ''; + $txt = sprintf('', trans('firefly.withdrawal')); break; case $journal->isDeposit(): - $txt = ''; + $txt = sprintf('', trans('firefly.deposit')); break; case $journal->isTransfer(): - $txt = ''; + $txt = sprintf('', trans('firefly.transfer')); break; case $journal->isOpeningBalance(): - $txt = ''; + $txt = sprintf('', trans('firefly.openingBalance')); break; default: $txt = ''; diff --git a/app/Support/Twig/Transaction.php b/app/Support/Twig/Transaction.php index ceb5e7695b..ef8c97da73 100644 --- a/app/Support/Twig/Transaction.php +++ b/app/Support/Twig/Transaction.php @@ -210,7 +210,7 @@ class Transaction extends Twig_Extension return '(cash)'; } - return '' . e($name) . ''; + return sprintf('%1$s', e($name), route('accounts.show', [$id])); }, ['is_safe' => ['html']] ); @@ -276,7 +276,7 @@ class Transaction extends Twig_Extension return '(cash)'; } - return '' . e($name) . ''; + return sprintf('%1$s', e($name), route('accounts.show', [$id])); }, ['is_safe' => ['html']] ); @@ -294,16 +294,16 @@ class Transaction extends Twig_Extension switch ($transaction->transaction_type_type) { case TransactionType::WITHDRAWAL: - $txt = ''; + $txt = sprintf('' . trans('firefly.withdrawal')); break; case TransactionType::DEPOSIT: - $txt = ''; + $txt = sprintf('', trans('firefly.deposit')); break; case TransactionType::TRANSFER: - $txt = ''; + $txt = sprintf('', trans('firefly.transfer')); break; case TransactionType::OPENING_BALANCE: - $txt = ''; + $txt = sprintf('', trans('firefly.openingBalance')); break; default: $txt = ''; diff --git a/app/Support/Twig/Translation.php b/app/Support/Twig/Translation.php index f9401eee52..cb84f85c8e 100644 --- a/app/Support/Twig/Translation.php +++ b/app/Support/Twig/Translation.php @@ -35,7 +35,7 @@ class Translation extends Twig_Extension $filters[] = new Twig_SimpleFilter( '_', function ($name) { - return trans('firefly.' . $name); + return strval(trans(sprintf('firefly.%s', $name))); }, ['is_safe' => ['html']] ); From 4ba34ab511fba6f58e179f3451589622bc22afac Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 2 Nov 2016 07:16:46 +0100 Subject: [PATCH 024/709] Show sum [skip ci] Signed-off-by: James Cole --- resources/views/budgets/index.twig | 4 ++-- .../views/reports/default/multi-year.twig | 22 ++++++++++++++----- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/resources/views/budgets/index.twig b/resources/views/budgets/index.twig index e3b0f4ba0c..e9524e6acf 100644 --- a/resources/views/budgets/index.twig +++ b/resources/views/budgets/index.twig @@ -185,8 +185,8 @@

{{ 'inactiveBudgets'|_ }}

- {% for index,budget in inactive %} - {% if index != inactive|length-1 %} + {% for budget in inactive %} + {% if loop.index == inactive.count() %} {{ budget.name }} {% else %} {{ budget.name }}, diff --git a/resources/views/reports/default/multi-year.twig b/resources/views/reports/default/multi-year.twig index 314415d2f8..88474b6029 100644 --- a/resources/views/reports/default/multi-year.twig +++ b/resources/views/reports/default/multi-year.twig @@ -95,24 +95,34 @@ - + {% for year in years %} {% endfor %} - + - {% for id, info in budgetMultiYear %} - + + {% set sum = 0 %} {% for amount in info.entries %} - + {% set sum = sum + amount %} {% endfor %} - + {% endfor %} From b980b5baead4602eca1f1110bf4c3df1a5607443 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 2 Nov 2016 07:23:11 +0100 Subject: [PATCH 025/709] Small optimisations. Signed-off-by: James Cole --- app/Helpers/Report/ReportHelper.php | 68 +++++++++++++------ .../views/reports/default/multi-year.twig | 6 +- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/app/Helpers/Report/ReportHelper.php b/app/Helpers/Report/ReportHelper.php index cd8520e29d..ce0ab0cfaf 100644 --- a/app/Helpers/Report/ReportHelper.php +++ b/app/Helpers/Report/ReportHelper.php @@ -163,13 +163,12 @@ class ReportHelper implements ReportHelperInterface foreach ($budgets as $budget) { $data[$budget->id] = [ 'name' => $budget->name, - 'entries' => [], + 'entries' => $this->filterAmounts($queryResult, $budget->id, $years), + 'sum' => '0', ]; - foreach ($years as $year) { - // filter query result here! - $data[$budget->id]['entries'][$year] = $this->filterAmount($queryResult, $budget->id, $year); - } } + // filter out empty ones and fill sum: + $data = $this->getBudgetMultiYearMeta($data); return $data; } @@ -387,27 +386,28 @@ class ReportHelper implements ReportHelperInterface } /** - * @param Collection $set - * @param int $budgetId - * @param int $year + * @param array $data * - * @return string + * @return array */ - protected function filterAmount(Collection $set, int $budgetId, int $year): string + protected function getBudgetMultiYearMeta(array $data): array { - /** @var stdClass $object */ - $result = $set->filter( - function (TransactionJournal $object) use ($budgetId, $year) { - return intval($object->the_year) === $year && $budgetId === intval($object->budget_id); + /** + * @var int $budgetId + * @var array $set + */ + foreach ($data as $budgetId => $set) { + $sum = '0'; + foreach ($set['entries'] as $amount) { + $sum = bcadd($amount, $sum); + } + $data[$budgetId]['sum'] = $sum; + if (bccomp('0', $sum) === 0) { + unset($data[$budgetId]); } - ); - $amount = '0'; - if (!is_null($result->first())) { - $amount = $result->first()->sum_of_period; } - return $amount; - + return $data; } /** @@ -437,5 +437,33 @@ class ReportHelper implements ReportHelperInterface return $sum; } + /** + * @param Collection $set + * @param int $budgetId + * @param array $years + * + * @return array + */ + private function filterAmounts(Collection $set, int $budgetId, array $years):array + { + $arr = []; + foreach ($years as $year) { + /** @var stdClass $object */ + $result = $set->filter( + function (TransactionJournal $object) use ($budgetId, $year) { + return intval($object->the_year) === $year && $budgetId === intval($object->budget_id); + } + ); + $amount = '0'; + if (!is_null($result->first())) { + $amount = $result->first()->sum_of_period; + } + + $arr[$year] = $amount; + } + + return $arr; + } + } diff --git a/resources/views/reports/default/multi-year.twig b/resources/views/reports/default/multi-year.twig index 88474b6029..0754163e1e 100644 --- a/resources/views/reports/default/multi-year.twig +++ b/resources/views/reports/default/multi-year.twig @@ -113,15 +113,13 @@ {{ info.name }} {% endif %} - {% set sum = 0 %} {% for amount in info.entries %} - {% set sum = sum + amount %} {% endfor %} - {% endfor %} From 2ddd4314f1b0328b2f454fddc5d509507807bf6b Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 2 Nov 2016 14:33:57 +0100 Subject: [PATCH 026/709] Extend help pages. --- app/Helpers/Help/Help.php | 18 ++++++++++++++---- app/Http/Controllers/HelpController.php | 19 ++++++++++--------- resources/lang/en_US/firefly.php | 2 ++ resources/views/layout/default.twig | 3 +++ 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/app/Helpers/Help/Help.php b/app/Helpers/Help/Help.php index a4546e49af..79e3e7e721 100644 --- a/app/Helpers/Help/Help.php +++ b/app/Helpers/Help/Help.php @@ -26,6 +26,8 @@ use Route; */ class Help implements HelpInterface { + /** @var string */ + protected $userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36'; /** * @param string $route @@ -51,9 +53,10 @@ class Help implements HelpInterface $uri = sprintf('https://raw.githubusercontent.com/firefly-iii/help/master/%s/%s.md', $language, $route); Log::debug(sprintf('Trying to get %s...', $uri)); + $opt = ['useragent' => $this->userAgent]; $content = ''; try { - $result = Requests::get($uri); + $result = Requests::get($uri, [], $opt); } catch (Requests_Exception $e) { Log::error($e); @@ -71,6 +74,9 @@ class Help implements HelpInterface $converter = new CommonMarkConverter(); $content = $converter->convertToHtml($content); } + if (strlen($content) === 0) { + Log::warning('Raw content length is zero.'); + } return $content; @@ -95,7 +101,7 @@ class Help implements HelpInterface */ public function inCache(string $route, string $language):bool { - $line = sprintf('help.%s.%s', $route, $language); + $line = sprintf('help.%s.%s', $route, $language); $result = Cache::has($line); if ($result) { Log::debug(sprintf('Cache has this entry: %s', 'help.' . $route . '.' . $language)); @@ -119,7 +125,11 @@ class Help implements HelpInterface public function putInCache(string $route, string $language, string $content) { $key = sprintf('help.%s.%s', $route, $language); - Log::debug(sprintf('Will store entry in cache: %s', $key)); - Cache::put($key, $content, 10080); // a week. + if (strlen($content) > 0) { + Log::debug(sprintf('Will store entry in cache: %s', $key)); + Cache::put($key, $content, 10080); // a week. + return; + } + Log::info(sprintf('Will not cache %s because content is empty.', $key)); } } diff --git a/app/Http/Controllers/HelpController.php b/app/Http/Controllers/HelpController.php index 3096ac15eb..2d4299818e 100644 --- a/app/Http/Controllers/HelpController.php +++ b/app/Http/Controllers/HelpController.php @@ -44,7 +44,6 @@ class HelpController extends Controller $language = Preferences::get('language', config('firefly.default_language', 'en_US'))->data; $content = '

' . strval(trans('firefly.route_has_no_help')) . '

'; - $alternative = false; if (!$help->hasRoute($route)) { Log::error('No such route: ' . $route); @@ -54,7 +53,7 @@ class HelpController extends Controller if ($help->inCache($route, $language)) { $content = $help->getFromCache($route, $language); - Log::debug('Help text was in cache.'); + Log::debug(sprintf('Help text %s was in cache.', $language)); return Response::json($content); } @@ -63,13 +62,15 @@ class HelpController extends Controller // get backup language content (try English): if (strlen($content) === 0) { - $language = 'en_US'; - $content = $help->getFromGithub($language, $route); - $alternative = true; - } - - if ($alternative && strlen($content) > 0) { - $content = '

' . strval(trans('firefly.help_may_not_be_your_language')) . '

' . $content; + $language = 'en_US'; + if ($help->inCache($route, $language)) { + Log::debug(sprintf('Help text %s was in cache.', $language)); + $content = $help->getFromCache($route, $language); + } + if (!$help->inCache($route, $language)) { + $content = $help->getFromGithub($language, $route); + $content = '

' . strval(trans('firefly.help_may_not_be_your_language')) . '

' . $content; + } } if (strlen($content) === 0) { diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 435ab3f18b..2fa1eb4e59 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -72,6 +72,8 @@ return [ 'destination_accounts' => 'Destination account(s)', 'user_id_is' => 'Your user id is :user', 'field_supports_markdown' => 'This field supports Markdown.', + 'need_more_help' => 'If you need more help using Firefly III, please open a ticker on Github.', + // repeat frequencies: 'repeat_freq_yearly' => 'yearly', diff --git a/resources/views/layout/default.twig b/resources/views/layout/default.twig index 921138dd81..cd63a16aa3 100644 --- a/resources/views/layout/default.twig +++ b/resources/views/layout/default.twig @@ -148,6 +148,9 @@ From e24f5ec9f335202c6cbd642d01fe36e6a3090894 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 2 Nov 2016 20:08:11 +0100 Subject: [PATCH 027/709] Multi year report move to AJAX. Signed-off-by: James Cole --- app/Helpers/Report/BudgetReportHelper.php | 142 ++++++++++++++-- .../Report/BudgetReportHelperInterface.php | 17 ++ app/Helpers/Report/ReportHelper.php | 159 ------------------ app/Helpers/Report/ReportHelperInterface.php | 18 -- app/Http/Controllers/Controller.php | 27 --- .../Controllers/Report/BudgetController.php | 27 +++ app/Http/Controllers/ReportController.php | 6 +- app/Support/Twig/Transaction.php | 2 +- public/js/ff/reports/default/all.js | 29 ++++ public/js/ff/reports/default/multi-year.js | 4 +- resources/views/admin/domains/index.twig | 10 +- resources/views/admin/users/index.twig | 24 ++- .../views/reports/default/multi-year.twig | 35 +--- .../reports/partials/budget-multi-year.twig | 33 ++++ routes/web.php | 6 + 15 files changed, 267 insertions(+), 272 deletions(-) create mode 100644 resources/views/reports/partials/budget-multi-year.twig diff --git a/app/Helpers/Report/BudgetReportHelper.php b/app/Helpers/Report/BudgetReportHelper.php index 9c250e8560..4f370f89bb 100644 --- a/app/Helpers/Report/BudgetReportHelper.php +++ b/app/Helpers/Report/BudgetReportHelper.php @@ -15,13 +15,17 @@ namespace FireflyIII\Helpers\Report; use Carbon\Carbon; +use DB; use FireflyIII\Helpers\Collection\Budget as BudgetCollection; use FireflyIII\Helpers\Collection\BudgetLine; use FireflyIII\Models\Budget; use FireflyIII\Models\LimitRepetition; +use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Support\CacheProperties; +use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; +use stdClass; /** * Class BudgetReportHelper @@ -97,6 +101,66 @@ class BudgetReportHelper implements BudgetReportHelperInterface return $return; } + /** + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return array + */ + public function getBudgetMultiYear(Carbon $start, Carbon $end, Collection $accounts): array + { + $accountIds = $accounts->pluck('id')->toArray(); + $query = TransactionJournal + ::leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') + ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') + ->leftJoin( + 'transactions', function (JoinClause $join) { + $join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0); + } + ) + ->whereNull('transaction_journals.deleted_at') + ->whereNull('transactions.deleted_at') + ->where('transaction_types.type', 'Withdrawal') + ->where('transaction_journals.user_id', auth()->user()->id); + + if (count($accountIds) > 0) { + $query->whereIn('transactions.account_id', $accountIds); + } + $query->groupBy(['budget_transaction_journal.budget_id', 'the_year']); + $queryResult = $query->get( + [ + 'budget_transaction_journal.budget_id', + DB::raw('DATE_FORMAT(transaction_journals.date,"%Y") AS the_year'), + DB::raw('SUM(transactions.amount) as sum_of_period'), + ] + ); + + $data = []; + $budgets = $this->repository->getBudgets(); + $years = $this->listOfYears($start, $end); + + // do budget "zero" + $emptyBudget = new Budget; + $emptyBudget->id = 0; + $emptyBudget->name = strval(trans('firefly.no_budget')); + $budgets->push($emptyBudget); + + + // get all budgets and years. + foreach ($budgets as $budget) { + $data[$budget->id] = [ + 'name' => $budget->name, + 'entries' => $this->filterAmounts($queryResult, $budget->id, $years), + 'sum' => '0', + ]; + } + // filter out empty ones and fill sum: + $data = $this->getBudgetMultiYearMeta($data); + + return $data; + } + /** * @param Carbon $start * @param Carbon $end @@ -183,30 +247,21 @@ class BudgetReportHelper implements BudgetReportHelperInterface } /** - * Take the array as returned by CategoryRepositoryInterface::spentPerDay and CategoryRepositoryInterface::earnedByDay - * and sum up everything in the array in the given range. - * * @param Carbon $start * @param Carbon $end - * @param array $array * - * @return string + * @return array */ - protected function getSumOfRange(Carbon $start, Carbon $end, array $array) + public function listOfYears(Carbon $start, Carbon $end): array { - $sum = '0'; - $currentStart = clone $start; // to not mess with the original one - $currentEnd = clone $end; // to not mess with the original one - - while ($currentStart <= $currentEnd) { - $date = $currentStart->format('Y-m-d'); - if (isset($array[$date])) { - $sum = bcadd($sum, $array[$date]); - } - $currentStart->addDay(); + $begin = clone $start; + $years = []; + while ($begin < $end) { + $years[] = $begin->year; + $begin->addYear(); } - return $sum; + return $years; } /** @@ -247,6 +302,59 @@ class BudgetReportHelper implements BudgetReportHelperInterface return $headers; } + /** + * @param Collection $set + * @param int $budgetId + * @param array $years + * + * @return array + */ + private function filterAmounts(Collection $set, int $budgetId, array $years):array + { + $arr = []; + foreach ($years as $year) { + /** @var stdClass $object */ + $result = $set->filter( + function (TransactionJournal $object) use ($budgetId, $year) { + return intval($object->the_year) === $year && $budgetId === intval($object->budget_id); + } + ); + $amount = '0'; + if (!is_null($result->first())) { + $amount = $result->first()->sum_of_period; + } + + $arr[$year] = $amount; + } + + return $arr; + } + + /** + * @param array $data + * + * @return array + */ + private function getBudgetMultiYearMeta(array $data): array + { + /** + * @var int $budgetId + * @var array $set + */ + foreach ($data as $budgetId => $set) { + $sum = '0'; + foreach ($set['entries'] as $amount) { + $sum = bcadd($amount, $sum); + } + $data[$budgetId]['sum'] = $sum; + if (bccomp('0', $sum) === 0) { + unset($data[$budgetId]); + } + } + + return $data; + } + /** * @param Carbon $current * @param Carbon $end diff --git a/app/Helpers/Report/BudgetReportHelperInterface.php b/app/Helpers/Report/BudgetReportHelperInterface.php index 3660447c99..89c785b0a8 100644 --- a/app/Helpers/Report/BudgetReportHelperInterface.php +++ b/app/Helpers/Report/BudgetReportHelperInterface.php @@ -34,6 +34,15 @@ interface BudgetReportHelperInterface */ public function budgetYearOverview(Carbon $start, Carbon $end, Collection $accounts): Collection; + /** + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return array + */ + public function getBudgetMultiYear(Carbon $start, Carbon $end, Collection $accounts): array; + /** * @param Carbon $start * @param Carbon $end @@ -52,4 +61,12 @@ interface BudgetReportHelperInterface */ public function getBudgetsWithExpenses(Carbon $start, Carbon $end, Collection $accounts): Collection; + /** + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + public function listOfYears(Carbon $start, Carbon $end): array; + } diff --git a/app/Helpers/Report/ReportHelper.php b/app/Helpers/Report/ReportHelper.php index ce0ab0cfaf..b95ac24315 100644 --- a/app/Helpers/Report/ReportHelper.php +++ b/app/Helpers/Report/ReportHelper.php @@ -113,66 +113,6 @@ class ReportHelper implements ReportHelperInterface return $collection; } - /** - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return array - */ - public function getBudgetMultiYear(Carbon $start, Carbon $end, Collection $accounts): array - { - $accountIds = $accounts->pluck('id')->toArray(); - $query = TransactionJournal - ::leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') - ->leftJoin( - 'transactions', function (JoinClause $join) { - $join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0); - } - ) - ->whereNull('transaction_journals.deleted_at') - ->whereNull('transactions.deleted_at') - ->where('transaction_types.type', 'Withdrawal') - ->where('transaction_journals.user_id', auth()->user()->id); - - if (count($accountIds) > 0) { - $query->whereIn('transactions.account_id', $accountIds); - } - $query->groupBy(['budget_transaction_journal.budget_id', 'the_year']); - $queryResult = $query->get( - [ - 'budget_transaction_journal.budget_id', - DB::raw('DATE_FORMAT(transaction_journals.date,"%Y") AS the_year'), - DB::raw('SUM(transactions.amount) as sum_of_period'), - ] - ); - - $data = []; - $budgets = $this->budgetRepository->getBudgets(); - $years = $this->listOfYears($start, $end); - - // do budget "zero" - $emptyBudget = new Budget; - $emptyBudget->id = 0; - $emptyBudget->name = strval(trans('firefly.no_budget')); - $budgets->push($emptyBudget); - - - // get all budgets and years. - foreach ($budgets as $budget) { - $data[$budget->id] = [ - 'name' => $budget->name, - 'entries' => $this->filterAmounts($queryResult, $budget->id, $years), - 'sum' => '0', - ]; - } - // filter out empty ones and fill sum: - $data = $this->getBudgetMultiYearMeta($data); - - return $data; - } - /** * @param Carbon $start * @param Carbon $end @@ -293,24 +233,6 @@ class ReportHelper implements ReportHelperInterface return $months; } - /** - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function listOfYears(Carbon $start, Carbon $end): array - { - $begin = clone $start; - $years = []; - while ($begin < $end) { - $years[] = $begin->year; - $begin->addYear(); - } - - return $years; - } - /** * Returns an array of tags and their comparitive size with amounts bla bla. * @@ -385,85 +307,4 @@ class ReportHelper implements ReportHelperInterface return $collection; } - /** - * @param array $data - * - * @return array - */ - protected function getBudgetMultiYearMeta(array $data): array - { - /** - * @var int $budgetId - * @var array $set - */ - foreach ($data as $budgetId => $set) { - $sum = '0'; - foreach ($set['entries'] as $amount) { - $sum = bcadd($amount, $sum); - } - $data[$budgetId]['sum'] = $sum; - if (bccomp('0', $sum) === 0) { - unset($data[$budgetId]); - } - } - - return $data; - } - - /** - * Take the array as returned by CategoryRepositoryInterface::spentPerDay and CategoryRepositoryInterface::earnedByDay - * and sum up everything in the array in the given range. - * - * @param Carbon $start - * @param Carbon $end - * @param array $array - * - * @return string - */ - protected function getSumOfRange(Carbon $start, Carbon $end, array $array) - { - $sum = '0'; - $currentStart = clone $start; // to not mess with the original one - $currentEnd = clone $end; // to not mess with the original one - - while ($currentStart <= $currentEnd) { - $date = $currentStart->format('Y-m-d'); - if (isset($array[$date])) { - $sum = bcadd($sum, $array[$date]); - } - $currentStart->addDay(); - } - - return $sum; - } - - /** - * @param Collection $set - * @param int $budgetId - * @param array $years - * - * @return array - */ - private function filterAmounts(Collection $set, int $budgetId, array $years):array - { - $arr = []; - foreach ($years as $year) { - /** @var stdClass $object */ - $result = $set->filter( - function (TransactionJournal $object) use ($budgetId, $year) { - return intval($object->the_year) === $year && $budgetId === intval($object->budget_id); - } - ); - $amount = '0'; - if (!is_null($result->first())) { - $amount = $result->first()->sum_of_period; - } - - $arr[$year] = $amount; - } - - return $arr; - } - - } diff --git a/app/Helpers/Report/ReportHelperInterface.php b/app/Helpers/Report/ReportHelperInterface.php index 6dec1b9c0e..34ffcc9e7b 100644 --- a/app/Helpers/Report/ReportHelperInterface.php +++ b/app/Helpers/Report/ReportHelperInterface.php @@ -28,16 +28,6 @@ use Illuminate\Support\Collection; interface ReportHelperInterface { - /** - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return array - */ - public function getBudgetMultiYear(Carbon $start, Carbon $end, Collection $accounts): array; - - /** * This method generates a full report for the given period on all * the users bills and their payments. @@ -90,14 +80,6 @@ interface ReportHelperInterface */ public function listOfMonths(Carbon $date): array; - /** - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function listOfYears(Carbon $start, Carbon $end): array; - /** * Returns an array of tags and their comparitive size with amounts bla bla. * diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 58917455dc..727ef34f7a 100755 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -61,31 +61,4 @@ class Controller extends BaseController } - /** - * Take the array as returned by CategoryRepositoryInterface::spentPerDay and CategoryRepositoryInterface::earnedByDay - * and sum up everything in the array in the given range. - * - * @param Carbon $start - * @param Carbon $end - * @param array $array - * - * @return string - */ - protected function getSumOfRange(Carbon $start, Carbon $end, array $array) - { - $sum = '0'; - $currentStart = clone $start; // to not mess with the original one - $currentEnd = clone $end; // to not mess with the original one - - while ($currentStart <= $currentEnd) { - $date = $currentStart->format('Y-m-d'); - if (isset($array[$date])) { - $sum = bcadd($sum, $array[$date]); - } - $currentStart->addDay(); - } - - return $sum; - } - } diff --git a/app/Http/Controllers/Report/BudgetController.php b/app/Http/Controllers/Report/BudgetController.php index 1d139c23ca..b18f1b8d43 100644 --- a/app/Http/Controllers/Report/BudgetController.php +++ b/app/Http/Controllers/Report/BudgetController.php @@ -28,6 +28,33 @@ use Illuminate\Support\Collection; class BudgetController extends Controller { + /** + * @param BudgetReportHelperInterface $helper + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + */ + public function budgetMultiYear(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts) + { + $cache = new CacheProperties; + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('budget-mult-year-report'); + $cache->addProperty($accounts->pluck('id')->toArray()); + if ($cache->has()) { + return $cache->get(); + } + + + $years = $helper->listOfYears($start, $end); + $budgetMultiYear = $helper->getBudgetMultiYear($start, $end, $accounts); + + $result = view('reports.partials.budget-multi-year', compact('budgetMultiYear', 'years'))->render(); + $cache->store($result); + + return $result; + } + /** * @param BudgetReportHelperInterface $helper * @param Carbon $start diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index 7878830f15..6ac94f0883 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -244,8 +244,7 @@ class ReportController extends Controller { // need all budgets // need all years. - $years = $this->helper->listOfYears($start, $end); - $budgetMultiYear = $this->helper->getBudgetMultiYear($start, $end, $accounts); + // and some id's, joined: @@ -259,8 +258,7 @@ class ReportController extends Controller return view( 'reports.default.multi-year', compact( - 'accounts', 'start', 'end', 'accountIds', 'reportType', - 'years', 'budgetMultiYear' + 'accounts', 'start', 'end', 'accountIds', 'reportType' ) ); } diff --git a/app/Support/Twig/Transaction.php b/app/Support/Twig/Transaction.php index ef8c97da73..a5b98f34d1 100644 --- a/app/Support/Twig/Transaction.php +++ b/app/Support/Twig/Transaction.php @@ -294,7 +294,7 @@ class Transaction extends Twig_Extension switch ($transaction->transaction_type_type) { case TransactionType::WITHDRAWAL: - $txt = sprintf('' . trans('firefly.withdrawal')); + $txt = sprintf('', trans('firefly.withdrawal')); break; case TransactionType::DEPOSIT: $txt = sprintf('', trans('firefly.deposit')); diff --git a/public/js/ff/reports/default/all.js b/public/js/ff/reports/default/all.js index a76d6be0ec..5d47620714 100644 --- a/public/js/ff/reports/default/all.js +++ b/public/js/ff/reports/default/all.js @@ -128,4 +128,33 @@ function respondInfoButton(data) { $('#defaultModal').empty().html(data.html); $('#defaultModal').modal('show'); +} + +function loadAjaxPartial(holder, uri) { + "use strict"; + console.log('Going to grab URI ' + uri); + $.get(uri).done(function (data) { + displayAjaxPartial(data, holder); + }).fail(function () { + failAjaxPartial(uri, holder); + }); +} + +function displayAjaxPartial(data, holder) { + "use strict"; + console.log('Display stuff in ' + holder); + var obj = $('#' + holder); + obj.removeClass('loading').html(data); + + // call some often needed recalculations and what-not: + + // find a sortable table and make it sortable: + $.bootstrapSortable(true); +} + +function failAjaxPartial(uri, holder) { + "use strict"; + console.log('Failed to load' + uri); + $('#' + holder).removeClass('loading').addClass('general-chart-error'); + } \ No newline at end of file diff --git a/public/js/ff/reports/default/multi-year.js b/public/js/ff/reports/default/multi-year.js index 04b9ab3ddb..9274a86ac1 100644 --- a/public/js/ff/reports/default/multi-year.js +++ b/public/js/ff/reports/default/multi-year.js @@ -1,13 +1,13 @@ -/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, year, month, columnChart, lineChart, stackedColumnChart */ +/* globals budgetMultiUrl, accountIds */ $(function () { "use strict"; drawChart(); + loadAjaxPartial('budgetMultiYear', budgetMultiUrl); }); - function drawChart() { "use strict"; diff --git a/resources/views/admin/domains/index.twig b/resources/views/admin/domains/index.twig index 2da0fe9ad2..ad854f3c52 100644 --- a/resources/views/admin/domains/index.twig +++ b/resources/views/admin/domains/index.twig @@ -12,7 +12,7 @@
{% if domains|length > 0 %} -
Budget{{ 'budget'|_ }}{{ year }}{{ 'sum'|_ }}
{{ info.name }} + {% if id == 0 %} + {{ info.name }} + + {% else %} + {{ info.name }} + {% endif %} + + {{ amount|formatAmount }} + {{ sum|formatAmount }} +
{{ amount|formatAmount }} - {{ sum|formatAmount }} + + {{ info.sum|formatAmount }}
+
@@ -59,7 +59,7 @@ {{ 'all_domains_is_filtered'|_ }}

-
 
+
@@ -118,3 +118,9 @@ {% endblock %} +{% block styles %} + +{% endblock %} +{% block scripts %} + +{% endblock %} diff --git a/resources/views/admin/users/index.twig b/resources/views/admin/users/index.twig index 9b47a66dd8..60708d175c 100644 --- a/resources/views/admin/users/index.twig +++ b/resources/views/admin/users/index.twig @@ -11,7 +11,7 @@

{{ 'all_users'|_ }}

-
 
+
@@ -29,15 +29,15 @@ {% for user in users %} - - - + - @@ -47,28 +47,28 @@ - - - - - - diff --git a/resources/views/transactions/edit.twig b/resources/views/transactions/edit.twig index 5046d67512..bd8a39cf54 100644 --- a/resources/views/transactions/edit.twig +++ b/resources/views/transactions/edit.twig @@ -83,11 +83,7 @@ {{ ExpandedForm.text('tags') }} - - {% if what == 'transfer' and piggyBankList|length > 0 %} - {{ ExpandedForm.select('piggy_bank_id',piggyBankList,data['piggy_bank_id']) }} - {% endif %} - + {# NO PIGGY BANK #} From 47bebb614e588059f20dcf3047629662ec59300a Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 5 Nov 2016 07:43:23 +0100 Subject: [PATCH 037/709] Only withdrawal can have a budget. --- app/Repositories/Journal/JournalRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php index 5d81adf1a6..6b3e476b50 100644 --- a/app/Repositories/Journal/JournalRepository.php +++ b/app/Repositories/Journal/JournalRepository.php @@ -458,7 +458,7 @@ class JournalRepository implements JournalRepositoryInterface */ private function storeBudgetWithJournal(TransactionJournal $journal, int $budgetId) { - if (intval($budgetId) > 0 && $journal->transactionType->type !== TransactionType::TRANSFER) { + if (intval($budgetId) > 0 && $journal->transactionType->type === TransactionType::WITHDRAWAL) { /** @var \FireflyIII\Models\Budget $budget */ $budget = Budget::find($budgetId); $journal->budgets()->save($budget); From 9c5d192d90255f9b0935ac35b471dd1c5dedb1f7 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 5 Nov 2016 08:27:25 +0100 Subject: [PATCH 038/709] Journal collector may not have been a bad idea after all! --- app/Helpers/Collector/JournalCollector.php | 360 ++++++++++++++++++ app/Http/Controllers/JsonController.php | 14 +- .../Controllers/TransactionController.php | 37 +- resources/views/list/journals.twig | 8 + resources/views/transactions/index.twig | 2 +- 5 files changed, 405 insertions(+), 16 deletions(-) create mode 100644 app/Helpers/Collector/JournalCollector.php diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php new file mode 100644 index 0000000000..467197d8ca --- /dev/null +++ b/app/Helpers/Collector/JournalCollector.php @@ -0,0 +1,360 @@ +user = $user; + $this->query = $this->startQuery(); + } + + /** + * @return int + * @throws FireflyException + */ + public function count(): int + { + if ($this->run === true) { + throw new FireflyException('Cannot count after run in JournalCollector.'); + } + + $countQuery = clone $this->query; + + // dont need some fields: + $countQuery->getQuery()->limit = null; + $countQuery->getQuery()->offset = null; + $countQuery->getQuery()->unionLimit = null; + $countQuery->getQuery()->groups = null; + $countQuery->groupBy('accounts.user_id'); + $this->count = $countQuery->count(); + + return $this->count; + } + + /** + * @return Collection + */ + public function getJournals(): Collection + { + $this->run = true; + $set = $this->query->get($this->fields); + + // loop for decryption. + $set->each( + function (Transaction $transaction) { + $transaction->date = new Carbon($transaction->date); + $transaction->description = intval($transaction->encrypted) === 1 ? Crypt::decrypt($transaction->description) : $transaction->description; + $transaction->bill_name = !is_null($transaction->bill_name) ? Crypt::decrypt($transaction->bill_name) : ''; + } + ); + + return $set; + } + + /** + * It might be worth it to expand this query to include all account information required. + * + * @param Collection $accounts + * @param array $types + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + public function getJournalsInPeriod(Collection $accounts, array $types, Carbon $start, Carbon $end): Collection + { + $accountIds = $accounts->pluck('id')->toArray(); + $query = Transaction + ::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transaction_journals.transaction_currency_id') + ->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id') + ->leftJoin('bills', 'bills.id', 'transaction_journals.bill_id') + ->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id') + ->leftJoin('account_types', 'accounts.account_type_id', 'account_types.id') + ->whereIn('transactions.account_id', $accountIds) + ->whereNull('transactions.deleted_at') + ->whereNull('transaction_journals.deleted_at') + ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) + ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) + ->where('transaction_journals.user_id', $this->user->id) + ->orderBy('transaction_journals.date', 'DESC') + ->orderBy('transaction_journals.order', 'ASC') + ->orderBy('transaction_journals.id', 'DESC'); + + if (count($types) > 0) { + $query->whereIn('transaction_types.type', $types); + } + + $set = $query->get( + [ + 'transaction_journals.id as journal_id', + 'transaction_journals.description', + 'transaction_journals.date', + 'transaction_journals.encrypted', + //'transaction_journals.transaction_currency_id', + 'transaction_currencies.code as transaction_currency_code', + //'transaction_currencies.symbol as transaction_currency_symbol', + 'transaction_types.type as transaction_type_type', + 'transaction_journals.bill_id', + 'bills.name as bill_name', + 'transactions.id as id', + 'transactions.amount as transaction_amount', + 'transactions.description as transaction_description', + 'transactions.account_id', + 'transactions.identifier', + 'transactions.transaction_journal_id', + 'accounts.name as account_name', + 'accounts.encrypted as account_encrypted', + 'account_types.type as account_type', + + ] + ); + + // loop for decryption. + $set->each( + function (Transaction $transaction) { + $transaction->date = new Carbon($transaction->date); + $transaction->description = intval($transaction->encrypted) === 1 ? Crypt::decrypt($transaction->description) : $transaction->description; + $transaction->bill_name = !is_null($transaction->bill_name) ? Crypt::decrypt($transaction->bill_name) : ''; + } + ); + + return $set; + } + + /** + * @return LengthAwarePaginator + * @throws FireflyException + */ + public function getPaginatedJournals():LengthAwarePaginator + { + if ($this->run === true) { + throw new FireflyException('Cannot getPaginatedJournals after run in JournalCollector.'); + } + $this->count(); + $set = $this->getJournals(); + $journals = new LengthAwarePaginator($set, $this->count, $this->limit, $this->page); + + return $journals; + } + + /** + * @param Collection $accounts + * + * @return JournalCollector + */ + public function setAccounts(Collection $accounts): JournalCollector + { + if ($accounts->count() > 0) { + $accountIds = $accounts->pluck('id')->toArray(); + $this->query->whereIn('transactions.account_id', $accountIds); + } + + return $this; + } + + /** + * @param int $limit + * + * @return JournalCollector + */ + public function setLimit(int $limit): JournalCollector + { + $this->limit = $limit; + $this->query->limit($limit); + Log::debug(sprintf('Set limit to %d', $limit)); + + return $this; + } + + /** + * @param int $offset + * + * @return JournalCollector + */ + public function setOffset(int $offset): JournalCollector + { + $this->offset = $offset; + } + + /** + * @param int $page + * + * @return JournalCollector + */ + public function setPage(int $page): JournalCollector + { + $this->page = $page; + + if ($page > 0) { + $page--; + } + Log::debug(sprintf('Page is %d', $page)); + + if (!is_null($this->limit)) { + $offset = ($this->limit * $page); + $this->offset = $offset; + $this->query->skip($offset); + Log::debug(sprintf('Changed offset to %d', $offset)); + } + if (is_null($this->limit)) { + Log::debug('The limit is zero, cannot set the page.'); + } + + return $this; + } + + /** + * @param Carbon $start + * @param Carbon $end + * + * @return JournalCollector + */ + public function setRange(Carbon $start, Carbon $end): JournalCollector + { + if ($start <= $end) { + $this->query->where('transaction_journals.date', '>=', $start->format('Y-m-d')); + $this->query->where('transaction_journals.date', '<=', $end->format('Y-m-d')); + } + + return $this; + } + + /** + * @param Collection $accounts + * + * @return JournalCollector + */ + public function setDestinationAccounts(Collection $accounts): JournalCollector + { + if ($accounts->count() > 0) { + $accountIds = $accounts->pluck('id')->toArray(); + $this->query->whereIn('transactions.account_id', $accountIds); + $this->query->where('transactions.amount', '>', 0); + } + + return $this; + } + + /** + * @param Collection $accounts + * + * @return JournalCollector + */ + public function setSourceAccounts(Collection $accounts): JournalCollector + { + if ($accounts->count() > 0) { + $accountIds = $accounts->pluck('id')->toArray(); + $this->query->whereIn('transactions.account_id', $accountIds); + $this->query->where('transactions.amount', '<', 0); + } + + return $this; + } + + /** + * @param array $types + * + * @return JournalCollector + */ + public function setTypes(array $types): JournalCollector + { + if (count($types) > 0) { + $this->query->whereIn('transaction_types.type', $types); + } + + return $this; + } + + /** + * @return EloquentBuilder + */ + private function startQuery(): EloquentBuilder + { + + $query = Transaction + ::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transaction_journals.transaction_currency_id') + ->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id') + ->leftJoin('bills', 'bills.id', 'transaction_journals.bill_id') + ->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id') + ->leftJoin('account_types', 'accounts.account_type_id', 'account_types.id') + ->whereNull('transactions.deleted_at') + ->whereNull('transaction_journals.deleted_at') + ->where('transaction_journals.user_id', $this->user->id) + ->orderBy('transaction_journals.date', 'DESC') + ->orderBy('transaction_journals.order', 'ASC') + ->orderBy('transaction_journals.id', 'DESC'); + + return $query; + + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/JsonController.php b/app/Http/Controllers/JsonController.php index 5eea913966..197fd7a326 100644 --- a/app/Http/Controllers/JsonController.php +++ b/app/Http/Controllers/JsonController.php @@ -15,6 +15,7 @@ namespace FireflyIII\Http\Controllers; use Amount; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Models\AccountType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountTaskerInterface; @@ -270,17 +271,20 @@ class JsonController extends Controller } /** - * @param JournalTaskerInterface $tasker - * @param $what + * @param $what * - * @return \Symfony\Component\HttpFoundation\Response + * @return \Illuminate\Http\JsonResponse */ - public function transactionJournals(JournalTaskerInterface $tasker, $what) + public function transactionJournals($what) { $descriptions = []; $type = config('firefly.transactionTypesByWhat.' . $what); $types = [$type]; - $journals = $tasker->getJournals($types, 1, 50); + + // use journal collector instead: + $collector = new JournalCollector(auth()->user()); + $collector->setTypes($types)->setLimit(100)->setPage(1); + $journals = $collector->getJournals(); foreach ($journals as $j) { $descriptions[] = $j->description; } diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index bd2a3d4c1e..a92c70d130 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -14,7 +14,10 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; use Carbon\Carbon; +use FireflyIII\Helpers\Collector\JournalCollector; +use FireflyIII\Models\AccountType; use FireflyIII\Models\TransactionJournal; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalTaskerInterface; use Illuminate\Http\Request; @@ -49,21 +52,35 @@ class TransactionController extends Controller } /** - * @param Request $request - * @param JournalTaskerInterface $tasker - * @param string $what + * @param Request $request + * @param AccountRepositoryInterface $repository + * @param string $what * * @return View */ - public function index(Request $request, JournalTaskerInterface $tasker, string $what) + public function index(Request $request, AccountRepositoryInterface $repository, string $what) { - $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); - $subTitleIcon = config('firefly.transactionIconsByWhat.' . $what); - $types = config('firefly.transactionTypesByWhat.' . $what); - $subTitle = trans('firefly.title_' . $what); - $page = intval($request->get('page')); - $journals = $tasker->getJournals($types, $page, $pageSize); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); + $subTitleIcon = config('firefly.transactionIconsByWhat.' . $what); + $types = config('firefly.transactionTypesByWhat.' . $what); + $subTitle = trans('firefly.title_' . $what); + $page = intval($request->get('page')); + $assetAccounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); + $collector = new JournalCollector(auth()->user()); + $collector->setTypes($types)->setLimit($pageSize)->setPage($page); + // depending on the view, we filter the collector to grab the right stuff. + switch ($what) { + default: + $collector->setAccounts($assetAccounts); + break; + case 'transfer': + case 'transfers': + $collector->setDestinationAccounts($assetAccounts); + break; + } + + $journals = $collector->getPaginatedJournals(); $journals->setPath('transactions/' . $what); return view('transactions.index', compact('subTitle', 'what', 'subTitleIcon', 'journals')); diff --git a/resources/views/list/journals.twig b/resources/views/list/journals.twig index ddadd0aa60..86fa131105 100644 --- a/resources/views/list/journals.twig +++ b/resources/views/list/journals.twig @@ -1,5 +1,12 @@ {{ journals.render|raw }} +{% if journals.count == 0 %} +

+ {{ 'nothing_to_display'|_ }} +

+{% endif %} + +{% if journals.count > 0 %}
 
+
#{{ user.id }} + #{{ user.id }} {{ user.email }} + {{ user.created_at.formatLocalized(monthAndDayFormat) }} {{ user.created_at.format('H:i') }} {{ Preferences.getForUser(user,"confirmation_ip_address").data }} + {% if user.isAdmin %} {% else %} {% endif %} + {% if user.has2FA %} {% else %} {% endif %} + {% if user.activated %} {% else %} {% endif %} + {% if user.blocked == 1 %} {% else %} @@ -95,3 +95,9 @@ {% endblock %} +{% block styles %} + +{% endblock %} +{% block scripts %} + +{% endblock %} diff --git a/resources/views/reports/default/multi-year.twig b/resources/views/reports/default/multi-year.twig index 0754163e1e..d4a059577a 100644 --- a/resources/views/reports/default/multi-year.twig +++ b/resources/views/reports/default/multi-year.twig @@ -91,40 +91,8 @@

{{ 'budgets'|_ }}

-
- - - - - {% for year in years %} - - {% endfor %} - - - - - {% for id, info in budgetMultiYear %} - - - {% for amount in info.entries %} - - {% endfor %} - - - {% endfor %} - -
{{ 'budget'|_ }}{{ year }}{{ 'sum'|_ }}
- {% if id == 0 %} - {{ info.name }} +
- {% else %} - {{ info.name }} - {% endif %} -
- {{ amount|formatAmount }} - - {{ info.sum|formatAmount }} -
@@ -152,6 +120,7 @@ var accountReportUrl = '{{ route('reports.data.accountReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; var inOutReportUrl = '{{ route('reports.data.inOutReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var budgetMultiUrl = '{{ route('reports.data.budgetMultiYear', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; diff --git a/resources/views/reports/partials/budget-multi-year.twig b/resources/views/reports/partials/budget-multi-year.twig new file mode 100644 index 0000000000..7fc30ea9f3 --- /dev/null +++ b/resources/views/reports/partials/budget-multi-year.twig @@ -0,0 +1,33 @@ + + + + + {% for year in years %} + + {% endfor %} + + + + + {% for id, info in budgetMultiYear %} + + + {% for amount in info.entries %} + + {% endfor %} + + + {% endfor %} + +
{{ 'budget'|_ }}{{ year }}{{ 'sum'|_ }}
+ {% if id == 0 %} + {{ info.name }} + + {% else %} + {{ info.name }} + {% endif %} + + {{ amount|formatAmount }} + + {{ info.sum|formatAmount }} +
\ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 7a5072cf29..d7a6f64b5d 100755 --- a/routes/web.php +++ b/routes/web.php @@ -345,6 +345,12 @@ Route::group( ['uses' => 'Report\BudgetController@budgetYearOverview', 'as' => 'reports.data.budgetYearOverview'] ); + // budget multi year overview + Route::get( + '/reports/data/budget-multi-year/{start_date}/{end_date}/{accountList}', + ['uses' => 'Report\BudgetController@budgetMultiYear', 'as' => 'reports.data.budgetMultiYear'] + ); + /** * Rules Controller From 5e480eca36826c451945bf88a3a5058f9b80cbfe Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 2 Nov 2016 20:45:11 +0100 Subject: [PATCH 028/709] Clean up some report code. Signed-off-by: James Cole --- .../Controllers/Report/InOutController.php | 73 ++++++++++++++++--- public/js/ff/reports/default/all.js | 65 ++++------------- public/js/ff/reports/default/month.js | 67 +---------------- public/js/ff/reports/default/year.js | 25 +------ resources/views/reports/default/month.twig | 13 ++-- resources/views/reports/default/year.twig | 9 ++- routes/web.php | 16 +++- 7 files changed, 110 insertions(+), 158 deletions(-) diff --git a/app/Http/Controllers/Report/InOutController.php b/app/Http/Controllers/Report/InOutController.php index abc23c55fb..83ce241de1 100644 --- a/app/Http/Controllers/Report/InOutController.php +++ b/app/Http/Controllers/Report/InOutController.php @@ -19,7 +19,6 @@ use FireflyIII\Helpers\Report\ReportHelperInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; -use Response; /** * Class InOutController @@ -37,29 +36,83 @@ class InOutController extends Controller * * @return \Illuminate\Http\JsonResponse */ - public function inOutReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts) + public function expenseReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts) { // chart properties for cache: $cache = new CacheProperties; $cache->addProperty($start); $cache->addProperty($end); - $cache->addProperty('in-out-report'); + $cache->addProperty('expense-report'); $cache->addProperty($accounts->pluck('id')->toArray()); if ($cache->has()) { - return Response::json($cache->get()); + return $cache->get(); + } + + $expenses = $helper->getExpenseReport($start, $end, $accounts); + + $result = view('reports.partials.expenses', compact('expenses'))->render(); + $cache->store($result); + + return $result; + + } + + /** + * @param ReportHelperInterface $helper + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return \Illuminate\Http\JsonResponse + */ + public function incExpReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts) + { + // chart properties for cache: + $cache = new CacheProperties; + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('inc-exp-report'); + $cache->addProperty($accounts->pluck('id')->toArray()); + if ($cache->has()) { + return $cache->get(); } $incomes = $helper->getIncomeReport($start, $end, $accounts); $expenses = $helper->getExpenseReport($start, $end, $accounts); - $result = [ - 'income' => view('reports.partials.income', compact('incomes'))->render(), - 'expenses' => view('reports.partials.expenses', compact('expenses'))->render(), - 'incomes_expenses' => view('reports.partials.income-vs-expenses', compact('expenses', 'incomes'))->render(), - ]; + $result = view('reports.partials.income-vs-expenses', compact('expenses', 'incomes'))->render(); $cache->store($result); - return Response::json($result); + return $result; + + } + + /** + * @param ReportHelperInterface $helper + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return \Illuminate\Http\JsonResponse + */ + public function incomeReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts) + { + // chart properties for cache: + $cache = new CacheProperties; + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('income-report'); + $cache->addProperty($accounts->pluck('id')->toArray()); + if ($cache->has()) { + return $cache->get(); + } + + $incomes = $helper->getIncomeReport($start, $end, $accounts); + + $result = view('reports.partials.income', compact('incomes'))->render(); + $cache->store($result); + + return $result; } diff --git a/public/js/ff/reports/default/all.js b/public/js/ff/reports/default/all.js index 5d47620714..0c534045ca 100644 --- a/public/js/ff/reports/default/all.js +++ b/public/js/ff/reports/default/all.js @@ -1,4 +1,4 @@ -/* globals startDate, showOnlyTop, showFullList, endDate, reportType, accountIds, inOutReportUrl, accountReportUrl */ +/* globals startDate, showOnlyTop, showFullList, endDate, reportType, expenseReportUri, accountIds, incExpReportUri,accountReportUri, incomeReportUri */ /* * all.js * Copyright (C) 2016 thegrumpydictator@gmail.com @@ -13,23 +13,19 @@ $(function () { // load the account report, which this report shows: - loadAccountReport(); + loadAjaxPartial('accountReport', accountReportUri); - // load income / expense / difference: - loadInOutReport(); - - // trigger info click - triggerInfoClick(); - - // trigger list length things: - listLengthInitial(); + // load income and expense reports: + loadAjaxPartial('incomeReport',incomeReportUri); + loadAjaxPartial('expenseReport',expenseReportUri); + loadAjaxPartial('incomeVsExpenseReport',incExpReportUri); }); function triggerInfoClick() { "use strict"; // find the little info buttons and respond to them. - $('.firefly-info-button').unbind('clicl').click(clickInfoButton); + $('.firefly-info-button').unbind('click').click(clickInfoButton); } function listLengthInitial() { @@ -58,44 +54,6 @@ function triggerList(e) { return false; } -function loadInOutReport() { - "use strict"; - console.log('Going to grab ' + inOutReportUrl); - $.get(inOutReportUrl).done(placeInOutReport).fail(failInOutReport); -} - -function placeInOutReport(data) { - "use strict"; - $('#incomeReport').removeClass('loading').html(data.income); - $('#expenseReport').removeClass('loading').html(data.expenses); - $('#incomeVsExpenseReport').removeClass('loading').html(data.incomes_expenses); - listLengthInitial(); - triggerInfoClick(); -} - -function failInOutReport() { - "use strict"; - console.log('Fail in/out report data!'); - $('#incomeReport').removeClass('loading').addClass('general-chart-error'); - $('#expenseReport').removeClass('loading').addClass('general-chart-error'); - $('#incomeVsExpenseReport').removeClass('loading').addClass('general-chart-error'); -} - -function loadAccountReport() { - "use strict"; - $.get(accountReportUrl).done(placeAccountReport).fail(failAccountReport); -} - -function placeAccountReport(data) { - "use strict"; - $('#accountReport').removeClass('loading').html(data); -} - -function failAccountReport(data) { - "use strict"; - $('#accountReport').removeClass('loading').addClass('general-chart-error'); -} - function clickInfoButton(e) { "use strict"; // find all data tags, regardless of what they are: @@ -125,8 +83,7 @@ function respondInfoButton(data) { "use strict"; // remove wait cursor $('body').removeClass('waiting'); - $('#defaultModal').empty().html(data.html); - $('#defaultModal').modal('show'); + $('#defaultModal').empty().html(data.html).modal('show'); } @@ -150,6 +107,12 @@ function displayAjaxPartial(data, holder) { // find a sortable table and make it sortable: $.bootstrapSortable(true); + + // find the info click things and respond to them: + triggerInfoClick(); + + // trigger list thing + listLengthInitial(); } function failAjaxPartial(uri, holder) { diff --git a/public/js/ff/reports/default/month.js b/public/js/ff/reports/default/month.js index 7c15f922b6..4e34d95032 100644 --- a/public/js/ff/reports/default/month.js +++ b/public/js/ff/reports/default/month.js @@ -1,74 +1,15 @@ -/* globals google, budgetReportUrl, startDate ,reportURL, endDate , reportType ,accountIds, lineChart, categoryReportUrl, balanceReportUrl */ +/* globals google, categoryReportUri, budgetReportUri, balanceReportUri */ $(function () { "use strict"; drawChart(); - loadCategoryReport(); - loadBalanceReport(); - loadBudgetReport(); + loadAjaxPartial('categoryReport', categoryReportUri); + loadAjaxPartial('budgetReport', budgetReportUri); + loadAjaxPartial('balanceReport',balanceReportUri); }); -function loadCategoryReport() { - "use strict"; - console.log('Going to grab ' + categoryReportUrl); - $.get(categoryReportUrl).done(placeCategoryReport).fail(failCategoryReport); -} - -function loadBudgetReport() { - "use strict"; - console.log('Going to grab ' + budgetReportUrl); - $.get(budgetReportUrl).done(placeBudgetReport).fail(failBudgetReport); -} - - -function loadBalanceReport() { - "use strict"; - console.log('Going to grab ' + categoryReportUrl); - $.get(balanceReportUrl).done(placeBalanceReport).fail(failBalanceReport); -} - -function placeBudgetReport(data) { - "use strict"; - $('#budgetReport').removeClass('loading').html(data); - listLengthInitial(); - triggerInfoClick(); -} - -function placeBalanceReport(data) { - "use strict"; - $('#balanceReport').removeClass('loading').html(data); - listLengthInitial(); - triggerInfoClick(); -} - -function placeCategoryReport(data) { - "use strict"; - $('#categoryReport').removeClass('loading').html(data); - listLengthInitial(); - triggerInfoClick(); -} - -function failBudgetReport() { - "use strict"; - console.log('Fail budget report data!'); - $('#budgetReport').removeClass('loading').addClass('general-chart-error'); -} - -function failBalanceReport() { - "use strict"; - console.log('Fail balance report data!'); - $('#balanceReport').removeClass('loading').addClass('general-chart-error'); -} - -function failCategoryReport() { - "use strict"; - console.log('Fail category report data!'); - $('#categoryReport').removeClass('loading').addClass('general-chart-error'); -} - - function drawChart() { "use strict"; diff --git a/public/js/ff/reports/default/year.js b/public/js/ff/reports/default/year.js index e0cb494772..cea6c09c03 100644 --- a/public/js/ff/reports/default/year.js +++ b/public/js/ff/reports/default/year.js @@ -1,4 +1,4 @@ -/* globals google, accountIds, budgetYearOverviewUrl */ +/* globals google, accountIds, budgetYearOverviewUri */ var chartDrawn; var budgetChart; @@ -7,30 +7,9 @@ $(function () { chartDrawn = false; drawChart(); - // - loadBudgetOverview(); + loadAjaxPartial('budgetOverview',budgetYearOverviewUri); }); -function loadBudgetOverview() { - "use strict"; - console.log('Going to grab ' + budgetYearOverviewUrl); - $.get(budgetYearOverviewUrl).done(placeBudgetOverview).fail(failBudgetOverview); -} - -function placeBudgetOverview(data) { - "use strict"; - $('#budgetOverview').removeClass('loading').html(data); - $('.budget-chart-activate').on('click', clickBudgetChart); -} - -function failBudgetOverview() { - "use strict"; - console.log('Fail budget overview data!'); - $('#budgetOverview').removeClass('loading').addClass('general-chart-error'); -} - - - function drawChart() { "use strict"; diff --git a/resources/views/reports/default/month.twig b/resources/views/reports/default/month.twig index bd4f6789b8..69285bff64 100644 --- a/resources/views/reports/default/month.twig +++ b/resources/views/reports/default/month.twig @@ -132,11 +132,14 @@ var accountIds = '{{ accountIds }}'; - var accountReportUrl = '{{ route('reports.data.accountReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; - var inOutReportUrl = '{{ route('reports.data.inOutReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; - var categoryReportUrl = '{{ route('reports.data.categoryReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; - var balanceReportUrl = '{{ route('reports.data.balanceReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; - var budgetReportUrl = '{{ route('reports.data.budgetReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var accountReportUri = '{{ route('reports.data.accountReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var incomeReportUri = '{{ route('reports.data.incomeReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var expenseReportUri = '{{ route('reports.data.expenseReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var incExpReportUri = '{{ route('reports.data.incExpReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var categoryReportUri = '{{ route('reports.data.categoryReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var budgetReportUri = '{{ route('reports.data.budgetReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var balanceReportUri = '{{ route('reports.data.balanceReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + diff --git a/resources/views/reports/default/year.twig b/resources/views/reports/default/year.twig index 41e6ef0ebb..99a9a75d11 100644 --- a/resources/views/reports/default/year.twig +++ b/resources/views/reports/default/year.twig @@ -123,9 +123,12 @@ var accountIds = '{{ accountIds }}'; - var accountReportUrl = '{{ route('reports.data.accountReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; - var inOutReportUrl = '{{ route('reports.data.inOutReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; - var budgetYearOverviewUrl = '{{ route('reports.data.budgetYearOverview', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var accountReportUri = '{{ route('reports.data.accountReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var incomeReportUri = '{{ route('reports.data.incomeReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var expenseReportUri = '{{ route('reports.data.expenseReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var incExpReportUri = '{{ route('reports.data.incExpReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + + var budgetYearOverviewUri = '{{ route('reports.data.budgetYearOverview', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; diff --git a/routes/web.php b/routes/web.php index d7a6f64b5d..793d8e769a 100755 --- a/routes/web.php +++ b/routes/web.php @@ -316,10 +316,20 @@ Route::group( ['uses' => 'Report\AccountController@accountReport', 'as' => 'reports.data.accountReport'] ); - // income report + // income and expenses report Route::get( - '/reports/data/in-out-report/{start_date}/{end_date}/{accountList}', - ['uses' => 'Report\InOutController@inOutReport', 'as' => 'reports.data.inOutReport'] + '/reports/data/inc-exp-report/{start_date}/{end_date}/{accountList}', + ['uses' => 'Report\InOutController@incExpReport', 'as' => 'reports.data.incExpReport'] + ); + // (income report): + Route::get( + '/reports/data/income-report/{start_date}/{end_date}/{accountList}', + ['uses' => 'Report\InOutController@incomeReport', 'as' => 'reports.data.incomeReport'] + ); + // (expense report): + Route::get( + '/reports/data/expense-report/{start_date}/{end_date}/{accountList}', + ['uses' => 'Report\InOutController@expenseReport', 'as' => 'reports.data.expenseReport'] ); // category report: From a7e0e3fc154ad36fbb903a96a239dd6f1d8a489e Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 2 Nov 2016 20:52:40 +0100 Subject: [PATCH 029/709] Small additions and bug fixes. Signed-off-by: James Cole --- public/js/ff/reports/default/all.js | 15 ++++++++++----- routes/web.php | 2 ++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/public/js/ff/reports/default/all.js b/public/js/ff/reports/default/all.js index 0c534045ca..9b0e686d58 100644 --- a/public/js/ff/reports/default/all.js +++ b/public/js/ff/reports/default/all.js @@ -11,14 +11,13 @@ $(function () { "use strict"; - // load the account report, which this report shows: loadAjaxPartial('accountReport', accountReportUri); // load income and expense reports: - loadAjaxPartial('incomeReport',incomeReportUri); - loadAjaxPartial('expenseReport',expenseReportUri); - loadAjaxPartial('incomeVsExpenseReport',incExpReportUri); + loadAjaxPartial('incomeReport', incomeReportUri); + loadAjaxPartial('expenseReport', expenseReportUri); + loadAjaxPartial('incomeVsExpenseReport', incExpReportUri); }); @@ -106,13 +105,19 @@ function displayAjaxPartial(data, holder) { // call some often needed recalculations and what-not: // find a sortable table and make it sortable: - $.bootstrapSortable(true); + if (typeof $.bootstrapSortable === "function") { + $.bootstrapSortable(true); + } // find the info click things and respond to them: triggerInfoClick(); // trigger list thing listLengthInitial(); + + // trigger thing for budgets: + $('.budget-chart-activate').unbind('click').on('click', clickBudgetChart); + } function failAjaxPartial(uri, holder) { diff --git a/routes/web.php b/routes/web.php index 793d8e769a..32cda245ba 100755 --- a/routes/web.php +++ b/routes/web.php @@ -196,6 +196,7 @@ Route::group( // budgets: Route::get('/chart/budget/frontpage', ['uses' => 'Chart\BudgetController@frontpage']); + Route::get('/chart/budget/period/{budget}/default/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\BudgetController@period']); // this chart is used in reports: Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'Chart\BudgetController@budgetLimit']); @@ -216,6 +217,7 @@ Route::group( Route::get('/chart/report/in-out-sum/{reportType}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@yearInOutSummarized']); Route::get('/chart/report/net-worth/{reportType}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@netWorth']); + /** * IMPORT CONTROLLER */ From e1be4909b98f7e43354ae760c377acde37d80fa8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 2 Nov 2016 21:24:16 +0100 Subject: [PATCH 030/709] Redirect when 0 accounts. Signed-off-by: James Cole --- app/Http/Controllers/PiggyBankController.php | 6 ++++++ resources/lang/en_US/firefly.php | 10 ++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/PiggyBankController.php b/app/Http/Controllers/PiggyBankController.php index 15c98f6e5e..7239ba309a 100644 --- a/app/Http/Controllers/PiggyBankController.php +++ b/app/Http/Controllers/PiggyBankController.php @@ -107,6 +107,12 @@ class PiggyBankController extends Controller $subTitle = trans('firefly.new_piggy_bank'); $subTitleIcon = 'fa-plus'; + if (count($accounts) === 0) { + Session::flash('error', strval(trans('firefly.need_at_least_one_account'))); + + return redirect(route('new-user.index')); + } + // put previous url in session if not redirect from store (not "create another"). if (session('piggy-banks.create.fromStore') !== true) { Session::put('piggy-banks.create.url', URL::previous()); diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 2fa1eb4e59..0ec1a957b4 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -867,11 +867,9 @@ return [ 'import_double' => 'Row #:row: This row has been imported before, and is stored in :description.', 'import_finished_all' => 'The import has finished. Please check out the results below.', 'import_with_key' => 'Import with key \':key\'', - - 'import_share_configuration' => 'Please consider downloading your configuration and sharing it at the import configuration center. This will allow other users of Firefly III to import their files more easily.', - - 'import_finished_report' => 'The import has finished. Please note any errors in the block above this line. All transactions imported during this particular session have been tagged, and you can check them out below. ', - 'import_finished_link' => 'The transactions imported can be found in tag :tag.', - + 'import_share_configuration' => 'Please consider downloading your configuration and sharing it at the import configuration center. This will allow other users of Firefly III to import their files more easily.', + 'import_finished_report' => 'The import has finished. Please note any errors in the block above this line. All transactions imported during this particular session have been tagged, and you can check them out below. ', + 'import_finished_link' => 'The transactions imported can be found in tag :tag.', + 'need_at_least_one_account' => 'You need at least one asset account to be able to create piggy banks', ]; From 4106b2e4c099033dc7a99e122687eae5c949ca6b Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 2 Nov 2016 22:23:40 +0100 Subject: [PATCH 031/709] Remove some help entries in favour of help pages in the top right corner. --- resources/lang/en_US/csv.php | 3 +-- resources/lang/en_US/firefly.php | 7 +------ resources/views/import/csv/roles.twig | 4 +++- resources/views/import/index.twig | 15 +-------------- 4 files changed, 6 insertions(+), 23 deletions(-) diff --git a/resources/lang/en_US/csv.php b/resources/lang/en_US/csv.php index fe83034aa2..6b5abd3431 100644 --- a/resources/lang/en_US/csv.php +++ b/resources/lang/en_US/csv.php @@ -15,7 +15,7 @@ return [ 'import_configure_title' => 'Configure your import', 'import_configure_intro' => 'There are some options for your CSV import. Please indicate if your CSV file contains headers on the first column, and what the date format of your date-fields is. That might require some experimentation. The field delimiter is usually a ",", but could also be a ";". Check this carefully.', - 'import_configure_form' => 'Form', + 'import_configure_form' => 'Basic CSV import options', 'header_help' => 'Check this if the first row of your CSV file are the column titles', 'date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', 'delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', @@ -24,7 +24,6 @@ return [ // roles 'column_roles_title' => 'Define column roles', - 'column_roles_text' => '

Firefly III cannot guess what data each column contains. You must tell Firefly which kinds of data to expect. The example data can guide you into picking the correct type from the dropdown. If a column cannot be matched to a useful data type, please let me know by creating an issue.

Some values in your CSV file, such as account names or categories, may already exist in your Firefly III database. If you select "map these values" Firefly will not attempt to search for matching values itself but allow you to match the CSV values against the values in your database. This allows you to fine-tune the import.

', 'column_roles_table' => 'Table', 'column_name' => 'Name of column', 'column_example' => 'Column example data', diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 0ec1a957b4..22c235368b 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -844,14 +844,9 @@ return [ 'import_complete_text' => 'The import is ready to start. All the configuration you needed to do has been done. Please download the configuration file. It will help you with the import should it not go as planned. To actually run the import, you can either execute the following command in your console, or run the web-based import. Depending on your configuration, the console import will give you more feedback.', 'import_download_config' => 'Download configuration', 'import_start_import' => 'Start import', - 'import_intro_beta' => 'The import function of Firefly III is in beta. Many users of Firefly III have tried many different files. Although each individual compontent of this import routine works (really), the combination might break. If your file cannot be imported by Firefly, please read this wiki page so I can fix the problem you have run into.', 'import_data' => 'Import data', 'import_data_full' => 'Import data into Firefly III', 'import' => 'Import', - 'import_intro_what_it_does' => 'This page allows you to import data into Firefly III. To do so, export data from your bank, or from another financial management system. Upload that file here. Firefly III will convert the data. You need to give it some directions. Please select a file and follow the instructions.', - 'import_intro_import_conf_title' => 'Import "configuration"', - 'import_intro_beta_warning' => 'Warning', - 'import_intro_import_conf_text' => 'As you will discover over the next few pages, this import routine has a lot of settings. These settings are mainly dependent on the bank (or financial management software) your file comes from. There is a good chance somebody else already imported such a file and has shared their configuration file. Please visit the import configuration center to see if there already is a configuration available for your bank or system. If there is, you should download this configuration file and upload it here as well. It will save you a lot of time!', 'import_file_help' => 'Select your file', 'import_status_settings_complete' => 'The import is ready to start.', 'import_status_import_complete' => 'The import has completed.', @@ -871,5 +866,5 @@ return [ 'import_finished_report' => 'The import has finished. Please note any errors in the block above this line. All transactions imported during this particular session have been tagged, and you can check them out below. ', 'import_finished_link' => 'The transactions imported can be found in tag :tag.', 'need_at_least_one_account' => 'You need at least one asset account to be able to create piggy banks', - + 'see_help_top_right' => 'For more information, please check out the help pages using the icon in the top right corner of the page.', ]; diff --git a/resources/views/import/csv/roles.twig b/resources/views/import/csv/roles.twig index c18b7e8c03..f10122ea1e 100644 --- a/resources/views/import/csv/roles.twig +++ b/resources/views/import/csv/roles.twig @@ -13,7 +13,9 @@

{{ trans('csv.column_roles_title') }}

-

{{ trans('csv.column_roles_text')|raw }}

+

+ {{ 'see_help_top_right'|_ }} +

diff --git a/resources/views/import/index.twig b/resources/views/import/index.twig index 137d87bf9d..d3188651b3 100644 --- a/resources/views/import/index.twig +++ b/resources/views/import/index.twig @@ -17,20 +17,7 @@ {{ 'import_data_full'|_ }}

- {{ 'import_intro_what_it_does'|_ }} -

-

- {{ 'import_intro_import_conf_title'|_ }} -

-

- {{ 'import_intro_import_conf_text'|_ }} -

-

- {{ 'import_intro_beta_warning'|_ }} -

- -

-  {{ 'import_intro_beta'|_ }} + {{ 'see_help_top_right'|_ }}

From 72f7b5f3eaba52568b8a6f75b0130e80df64f4b6 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 3 Nov 2016 21:07:12 +0100 Subject: [PATCH 032/709] This fixes #375 --- app/Http/Controllers/CurrencyController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/CurrencyController.php b/app/Http/Controllers/CurrencyController.php index e1abc73e27..19ccf48434 100644 --- a/app/Http/Controllers/CurrencyController.php +++ b/app/Http/Controllers/CurrencyController.php @@ -129,7 +129,7 @@ class CurrencyController extends Controller Session::flash('success', trans('firefly.deleted_currency', ['name' => $currency->name])); if (auth()->user()->hasRole('owner')) { - $currency->delete(); + $currency->forceDelete(); } return redirect(session('currency.delete.url')); From 8e5e3de8b057134e27688ee4ce68e14a5029cb56 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 3 Nov 2016 21:45:35 +0100 Subject: [PATCH 033/709] Update change log (prematurely). [skip ci] --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac1c4e09df..b9de810a31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,20 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [4.1.6] - 2016-11-05 +### Added + +### Changed + +### Deprecated + +### Removed + +### Fixed + +### Security + + ## [4.1.5] - 2016-11-01 ### Changed - Report parts are loaded using AJAX, making a lot of code more simple. From 7bc4c6d115515da38e940df37ff83e1c8ec926fb Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 3 Nov 2016 21:49:08 +0100 Subject: [PATCH 034/709] Update change log [skip ci] --- CHANGELOG.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9de810a31..83e339a29b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,16 +4,14 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [4.1.6] - 2016-11-05 ### Added +- New budget table for multi year report. ### Changed - -### Deprecated - -### Removed +- Greatly expanded help pages and their function. ### Fixed - -### Security +- #375, thanks to @schoentoon which made it impossible to resurrect currencies. +- #370 thanks to @ksmolder ## [4.1.5] - 2016-11-01 From 1d15bc0b10a20110fbdf36ce8230fa0b34a4d3ea Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 3 Nov 2016 21:54:07 +0100 Subject: [PATCH 035/709] I am changing some string concatenations to sprintf() routines because they are more readable and safer. [skip ci] --- app/Console/Commands/UpgradeDatabase.php | 2 +- .../Commands/UpgradeFireflyInstructions.php | 5 ++--- app/Support/Navigation.php | 14 +++++++------- app/Support/Preferences.php | 6 +++--- app/Support/Twig/Transaction.php | 2 +- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/app/Console/Commands/UpgradeDatabase.php b/app/Console/Commands/UpgradeDatabase.php index 5b0ed46a5f..2c1a4dee63 100644 --- a/app/Console/Commands/UpgradeDatabase.php +++ b/app/Console/Commands/UpgradeDatabase.php @@ -99,7 +99,7 @@ class UpgradeDatabase extends Command } catch (QueryException $e) { Log::error($e->getMessage()); $this->error('Firefly III could not find the "identifier" field in the "transactions" table.'); - $this->error('This field is required for Firefly III version ' . config('firefly.version') . ' to run.'); + $this->error(sprintf('This field is required for Firefly III version %s to run.', config('firefly.version'))); $this->error('Please run "php artisan migrate" to add this field to the table.'); $this->info('Then, run "php artisan firefly:upgrade-database" to try again.'); break 2; diff --git a/app/Console/Commands/UpgradeFireflyInstructions.php b/app/Console/Commands/UpgradeFireflyInstructions.php index 234b075f7a..df007ab40f 100644 --- a/app/Console/Commands/UpgradeFireflyInstructions.php +++ b/app/Console/Commands/UpgradeFireflyInstructions.php @@ -64,15 +64,14 @@ class UpgradeFireflyInstructions extends Command } - if (is_null($text)) { - $this->line('Thank you for installing Firefly III, v' . $version); + $this->line(sprintf('Thank you for installing Firefly III, v%s', $version)); $this->info('There are no extra upgrade instructions.'); $this->line('Firefly III should be ready for use.'); } else { $this->line('+------------------------------------------------------------------------------+'); $this->line(''); - $this->line('Thank you for installing Firefly III, v' . $version); + $this->line(sprintf('Thank you for installing Firefly III, v%s', $version)); $this->info(wordwrap($text)); $this->line(''); $this->line('+------------------------------------------------------------------------------+'); diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php index 4ea2318acb..665873596e 100644 --- a/app/Support/Navigation.php +++ b/app/Support/Navigation.php @@ -54,7 +54,7 @@ class Navigation ]; if (!isset($functionMap[$repeatFreq])) { - throw new FireflyException('Cannot do addPeriod for $repeat_freq "' . $repeatFreq . '"'); + throw new FireflyException(sprintf('Cannot do addPeriod for $repeat_freq "%s"', $repeatFreq)); } if (isset($modifierMap[$repeatFreq])) { $add = $add * $modifierMap[$repeatFreq]; @@ -108,7 +108,7 @@ class Navigation } if (!isset($functionMap[$repeatFreq])) { - throw new FireflyException('Cannot do endOfPeriod for $repeat_freq "' . $repeatFreq . '"'); + throw new FireflyException(sprintf('Cannot do endOfPeriod for $repeat_freq "%s"', $repeatFreq)); } $function = $functionMap[$repeatFreq]; if (isset($modifierMap[$repeatFreq])) { @@ -202,7 +202,7 @@ class Navigation if (isset($formatMap[$repeatFrequency])) { return $date->formatLocalized(strval($formatMap[$repeatFrequency])); } - throw new FireflyException('No date formats for frequency "' . $repeatFrequency . '"!'); + throw new FireflyException(sprintf('No date formats for frequency "%s"!', $repeatFrequency)); } /** @@ -252,7 +252,7 @@ class Navigation } - throw new FireflyException('Cannot do startOfPeriod for $repeat_freq "' . $repeatFreq . '"'); + throw new FireflyException(sprintf('Cannot do startOfPeriod for $repeat_freq "%s"', $repeatFreq)); } /** @@ -313,7 +313,7 @@ class Navigation return $date; } - throw new FireflyException('Cannot do subtractPeriod for $repeat_freq "' . $repeatFreq . '"'); + throw new FireflyException(sprintf('Cannot do subtractPeriod for $repeat_freq "%s"', $repeatFreq)); } /** @@ -350,7 +350,7 @@ class Navigation return $end; } - throw new FireflyException('updateEndDate cannot handle $range "' . $range . '"'); + throw new FireflyException(sprintf('updateEndDate cannot handle range "%s"', $range)); } /** @@ -387,7 +387,7 @@ class Navigation } - throw new FireflyException('updateStartDate cannot handle $range "' . $range . '"'); + throw new FireflyException(sprintf('updateStartDate cannot handle range "%s"', $range)); } diff --git a/app/Support/Preferences.php b/app/Support/Preferences.php index 8471745dbc..55ed08c6dc 100644 --- a/app/Support/Preferences.php +++ b/app/Support/Preferences.php @@ -32,7 +32,7 @@ class Preferences */ public function delete($name): bool { - $fullName = 'preference' . auth()->user()->id . $name; + $fullName = sprintf('preference%s%s', auth()->user()->id, $name); if (Cache::has($fullName)) { Cache::forget($fullName); } @@ -66,7 +66,7 @@ class Preferences */ public function getForUser(User $user, $name, $default = null) { - $fullName = 'preference' . $user->id . $name; + $fullName = sprintf('preference%s%s', $user->id, $name); if (Cache::has($fullName)) { return Cache::get($fullName); } @@ -138,7 +138,7 @@ class Preferences */ public function setForUser(User $user, $name, $value): Preference { - $fullName = 'preference' . $user->id . $name; + $fullName = sprintf('preference%s%s', $user->id, $name); Cache::forget($fullName); $pref = Preference::where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data']); diff --git a/app/Support/Twig/Transaction.php b/app/Support/Twig/Transaction.php index a5b98f34d1..696464932c 100644 --- a/app/Support/Twig/Transaction.php +++ b/app/Support/Twig/Transaction.php @@ -125,7 +125,7 @@ class Transaction extends Twig_Extension && bccomp($amount, bcmul($transactionAmount, '-1')) !== 0 ) { // not equal? - return ' (' . Amount::formatWithCode($code, $amount, true) . ')'; + return sprintf(' (%s)', Amount::formatWithCode($code, $amount, true)); } return ''; From 5f7fb77db29d9df228179365a38c85db342cba1f Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 4 Nov 2016 16:04:36 +0100 Subject: [PATCH 036/709] Code to fix #378 --- .../Events/StoredJournalEventHandler.php | 42 +++++++++++++++++- .../Events/UpdatedJournalEventHandler.php | 44 ------------------- .../Transaction/SingleController.php | 4 +- app/Providers/EventServiceProvider.php | 1 - resources/views/transactions/edit-split.twig | 2 - resources/views/transactions/edit.twig | 6 +-- 6 files changed, 43 insertions(+), 56 deletions(-) diff --git a/app/Handlers/Events/StoredJournalEventHandler.php b/app/Handlers/Events/StoredJournalEventHandler.php index 113c542869..29f909b928 100644 --- a/app/Handlers/Events/StoredJournalEventHandler.php +++ b/app/Handlers/Events/StoredJournalEventHandler.php @@ -21,6 +21,7 @@ use FireflyIII\Models\RuleGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Rules\Processor; use FireflyIII\Support\Events\BillScanner; +use Log; /** * Class StoredJournalEventHandler @@ -42,30 +43,69 @@ class StoredJournalEventHandler $journal = $event->journal; $piggyBankId = $event->piggyBankId; + Log::debug(sprintf('Trying to connect journal %d to piggy bank %d.', $journal->id, $piggyBankId)); + /** @var PiggyBank $piggyBank */ $piggyBank = $journal->user->piggyBanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']); if (is_null($piggyBank)) { + Log::error('No such piggy bank!'); + return true; } + Log::debug(sprintf('Found piggy bank #%d: "%s"', $piggyBank->id, $piggyBank->name)); // update piggy bank rep for date of transaction journal. $repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first(); if (is_null($repetition)) { + Log::error(sprintf('No piggy bank repetition on %s!', $journal->date->format('Y-m-d'))); + return true; } $amount = TransactionJournal::amountPositive($journal); + Log::debug(sprintf('Will add/remove %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); // if piggy account matches source account, the amount is positive $sources = TransactionJournal::sourceAccountList($journal)->pluck('id')->toArray(); if (in_array($piggyBank->account_id, $sources)) { $amount = bcmul($amount, '-1'); + Log::debug(sprintf('Account #%d is the source, so will remove amount from piggy bank.', $piggyBank->account_id)); + } + + // if the amount is positive: + // make sure it fits in piggy bank: + if (bccomp($amount, '0') === 1) { + // amount is positive + $room = bcsub(strval($piggyBank->targetamount), strval($repetition->currentamount)); + Log::debug(sprintf('Room in piggy bank for extra money is %f', $room)); + if (bccomp($room, $amount) === -1) { + // $room is smaller than $amount + Log::debug(sprintf('There is NO room to add %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); + Log::debug(sprintf('New amount is %f', $room)); + $amount = $room; + } + } + + if (bccomp($amount, '0') === -1) { + // amount is negative + Log::debug(sprintf('Max amount to remove is %f', $repetition->currentamount)); + $compare = bcmul($repetition->currentamount, '-1'); + if (bccomp($compare, $amount) === 1) { + // $currentamount is smaller than $amount + Log::debug(sprintf('Cannot remove %f from piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); + Log::debug(sprintf('New amount is %f', $compare)); + $amount = $compare; + } } $repetition->currentamount = bcadd($repetition->currentamount, $amount); $repetition->save(); - PiggyBankEvent::create(['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount]); + /** @var PiggyBankEvent $event */ + $event = PiggyBankEvent::create( + ['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount] + ); + Log::debug(sprintf('Created piggy bank event #%d', $event->id)); return true; } diff --git a/app/Handlers/Events/UpdatedJournalEventHandler.php b/app/Handlers/Events/UpdatedJournalEventHandler.php index 8dbd8ae5f3..b6b7a8c8fd 100644 --- a/app/Handlers/Events/UpdatedJournalEventHandler.php +++ b/app/Handlers/Events/UpdatedJournalEventHandler.php @@ -30,50 +30,6 @@ use FireflyIII\Support\Events\BillScanner; */ class UpdatedJournalEventHandler { - /** - * This method will try to reconnect a journal to a piggy bank, updating the piggy bank repetition. - * - * @param UpdatedTransactionJournal $event - * - * @return bool - */ - public function connectToPiggyBank(UpdatedTransactionJournal $event): bool - { - $journal = $event->journal; - - if (!$journal->isTransfer()) { - return true; - } - - // get the event connected to this journal: - /** @var PiggyBankEvent $event */ - $event = PiggyBankEvent::where('transaction_journal_id', $journal->id)->first(); - if (is_null($event)) { - return false; - } - $piggyBank = $event->piggyBank()->first(); - $repetition = null; - if (!is_null($piggyBank)) { - /** @var PiggyBankRepetition $repetition */ - $repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first(); - } - - if (is_null($repetition)) { - return false; - } - - $amount = TransactionJournal::amount($journal); - $diff = bcsub($amount, $event->amount); // update current repetition - - $repetition->currentamount = bcadd($repetition->currentamount, $diff); - $repetition->save(); - - - $event->amount = $amount; - $event->save(); - - return true; - } /** * This method will check all the rules when a journal is updated. diff --git a/app/Http/Controllers/Transaction/SingleController.php b/app/Http/Controllers/Transaction/SingleController.php index dc005002ad..18f4d672eb 100644 --- a/app/Http/Controllers/Transaction/SingleController.php +++ b/app/Http/Controllers/Transaction/SingleController.php @@ -171,7 +171,6 @@ class SingleController extends Controller $assetAccounts = ExpandedForm::makeSelectList($this->accounts->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])); $budgetList = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets()); - $piggyBankList = ExpandedForm::makeSelectListWithEmpty($this->piggyBanks->getPiggyBanks()); // view related code $subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]); @@ -188,7 +187,6 @@ class SingleController extends Controller 'process_date' => TransactionJournal::dateAsString($journal, 'process_date'), 'category' => TransactionJournal::categoryAsString($journal), 'budget_id' => TransactionJournal::budgetId($journal), - 'piggy_bank_id' => TransactionJournal::piggyBankId($journal), 'tags' => join(',', $journal->tags->pluck('tag')->toArray()), 'source_account_id' => $sourceAccounts->first()->id, 'source_account_name' => $sourceAccounts->first()->name, @@ -225,7 +223,7 @@ class SingleController extends Controller return view( 'transactions.edit', - compact('journal', 'optionalFields', 'assetAccounts', 'what', 'budgetList', 'piggyBankList', 'subTitle') + compact('journal', 'optionalFields', 'assetAccounts', 'what', 'budgetList', 'subTitle') )->with('data', $preFilled); } diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 6cfb886b4c..b256815c13 100755 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -70,7 +70,6 @@ class EventServiceProvider extends ServiceProvider 'FireflyIII\Events\UpdatedTransactionJournal' => // is a Transaction Journal related event. [ 'FireflyIII\Handlers\Events\UpdatedJournalEventHandler@scanBills', - 'FireflyIII\Handlers\Events\UpdatedJournalEventHandler@connectToPiggyBank', 'FireflyIII\Handlers\Events\UpdatedJournalEventHandler@processRules', ], diff --git a/resources/views/transactions/edit-split.twig b/resources/views/transactions/edit-split.twig index 5f71a30c78..451371820b 100644 --- a/resources/views/transactions/edit-split.twig +++ b/resources/views/transactions/edit-split.twig @@ -212,8 +212,6 @@ {% endif %}
{{ trans('list.category') }}
@@ -130,3 +137,4 @@ var edit_selected_txt = "{{ 'edit_selected'|_ }}"; var delete_selected_txt = "{{ 'delete_selected'|_ }}"; +{% endif %} \ No newline at end of file diff --git a/resources/views/transactions/index.twig b/resources/views/transactions/index.twig index bf1f2efe79..ab081e5514 100644 --- a/resources/views/transactions/index.twig +++ b/resources/views/transactions/index.twig @@ -12,7 +12,7 @@

{{ subTitle }}

- {% include 'list.journals' %} + {% include 'list.journals-tasker' %}
From 98160e9b6315b45cb61ca87529b782410d37d653 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 5 Nov 2016 08:46:55 +0100 Subject: [PATCH 039/709] Expand use of journal collector. --- app/Helpers/Collector/JournalCollector.php | 59 +++++++--- app/Http/Controllers/CategoryController.php | 35 +++--- .../Controllers/Popup/ReportController.php | 13 ++- .../Category/CategoryRepository.php | 109 ------------------ .../Category/CategoryRepositoryInterface.php | 20 ---- resources/views/categories/show.twig | 2 +- .../views/categories/show_with_date.twig | 2 +- .../views/popup/report/category-entry.twig | 2 +- 8 files changed, 74 insertions(+), 168 deletions(-) diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index 467197d8ca..3383f129fd 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -7,6 +7,7 @@ namespace FireflyIII\Helpers\Collector; use Carbon\Carbon; use Crypt; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Category; use FireflyIII\Models\Transaction; use FireflyIII\User; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; @@ -51,6 +52,8 @@ class JournalCollector 'account_types.type as account_type', ]; + /** @var bool */ + private $joinedCategory = false; /** @var int */ private $limit; /** @var int */ @@ -220,6 +223,46 @@ class JournalCollector return $this; } + /** + * @param Category $category + * + * @return JournalCollector + */ + public function setCategory(Category $category): JournalCollector + { + if (!$this->joinedCategory) { + // join some extra tables: + $this->joinedCategory = true; + $this->query->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'); + $this->query->leftJoin('category_transaction', 'category_transaction.transaction_id', '=', 'transactions.id'); + } + + $this->query->where( + function (EloquentBuilder $q) use ($category) { + $q->where('category_transaction.category_id', $category->id); + $q->orWhere('category_transaction_journal.category_id', $category->id); + } + ); + + return $this; + } + + /** + * @param Collection $accounts + * + * @return JournalCollector + */ + public function setDestinationAccounts(Collection $accounts): JournalCollector + { + if ($accounts->count() > 0) { + $accountIds = $accounts->pluck('id')->toArray(); + $this->query->whereIn('transactions.account_id', $accountIds); + $this->query->where('transactions.amount', '>', 0); + } + + return $this; + } + /** * @param int $limit * @@ -287,22 +330,6 @@ class JournalCollector return $this; } - /** - * @param Collection $accounts - * - * @return JournalCollector - */ - public function setDestinationAccounts(Collection $accounts): JournalCollector - { - if ($accounts->count() > 0) { - $accountIds = $accounts->pluck('id')->toArray(); - $this->query->whereIn('transactions.account_id', $accountIds); - $this->query->where('transactions.amount', '>', 0); - } - - return $this; - } - /** * @param Collection $accounts * diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index f80d3a16bc..c1cfbdf1a5 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -14,13 +14,13 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; use Carbon\Carbon; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Http\Requests\CategoryFormRequest; use FireflyIII\Models\AccountType; use FireflyIII\Models\Category; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI; use FireflyIII\Support\CacheProperties; -use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Input; use Navigation; @@ -180,16 +180,17 @@ class CategoryController extends Controller $start = session('start', Navigation::startOfPeriod(new Carbon, $range)); /** @var Carbon $end */ $end = session('end', Navigation::endOfPeriod(new Carbon, $range)); + $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); $hideCategory = true; // used in list. $page = intval(Input::get('page')); - $pageSize = Preferences::get('transactionPageSize', 50)->data; - $offset = ($page - 1) * $pageSize; - $set = $repository->journalsInPeriod(new Collection([$category]), new Collection, [], $start, $end); // category - $count = $set->count(); - $subSet = $set->splice($offset, $pageSize); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $subTitle = $category->name; $subTitleIcon = 'fa-bar-chart'; - $journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page); + + // use journal collector + $collector = new JournalCollector(auth()->user()); + $collector->setPage($page)->setLimit($pageSize)->setAccounts($accounts)->setRange($start, $end)->setCategory($category); + $journals = $collector->getPaginatedJournals(); $journals->setPath('categories/show/' . $category->id); // oldest transaction in category: @@ -218,7 +219,7 @@ class CategoryController extends Controller $categoryCollection = new Collection([$category]); - $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); + while ($end >= $start) { $end = Navigation::startOfPeriod($end, $range); $currentEnd = Navigation::endOfPeriod($end, $range); @@ -233,7 +234,7 @@ class CategoryController extends Controller } $cache->store($entries); - return view('categories.show', compact('category', 'journals', 'entries', 'hideCategory', 'subTitle')); + return view('categories.show', compact('category', 'journals', 'entries', 'hideCategory', 'subTitle', 'subTitleIcon')); } /** @@ -244,7 +245,7 @@ class CategoryController extends Controller * * @return View */ - public function showWithDate(CRI $repository, Category $category, string $date) + public function showWithDate(AccountRepositoryInterface $repository, Category $category, string $date) { $carbon = new Carbon($date); $range = Preferences::get('viewRange', '1M')->data; @@ -253,14 +254,16 @@ class CategoryController extends Controller $subTitle = $category->name; $hideCategory = true; // used in list. $page = intval(Input::get('page')); - $pageSize = Preferences::get('transactionPageSize', 50)->data; - $offset = ($page - 1) * $pageSize; - $set = $repository->journalsInPeriod(new Collection([$category]), new Collection, [], $start, $end); // category - $count = $set->count(); - $subSet = $set->splice($offset, $pageSize); - $journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); + $accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); + + // new collector: + $collector = new JournalCollector(auth()->user()); + $collector->setPage($page)->setLimit($pageSize)->setAccounts($accounts)->setRange($start, $end)->setCategory($category); + $journals = $collector->getPaginatedJournals(); $journals->setPath('categories/show/' . $category->id . '/' . $date); + return view('categories.show_with_date', compact('category', 'journals', 'hideCategory', 'subTitle', 'carbon')); } diff --git a/app/Http/Controllers/Popup/ReportController.php b/app/Http/Controllers/Popup/ReportController.php index 0b4df645ba..e023cb8284 100644 --- a/app/Http/Controllers/Popup/ReportController.php +++ b/app/Http/Controllers/Popup/ReportController.php @@ -17,6 +17,7 @@ namespace FireflyIII\Http\Controllers\Popup; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collection\BalanceLine; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; @@ -174,10 +175,14 @@ class ReportController extends Controller $repository = app(CategoryRepositoryInterface::class); $category = $repository->find(intval($attributes['categoryId'])); $types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]; - $journals = $repository->journalsInPeriod( - new Collection([$category]), $attributes['accounts'], $types, $attributes['startDate'], $attributes['endDate'] - ); - $view = view('popup.report.category-entry', compact('journals', 'category'))->render(); + // get journal collector instead: + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($attributes['accounts'])->setTypes($types) + ->setRange($attributes['startDate'], $attributes['endDate']) + ->setCategory($category); + $journals = $collector->getJournals(); // 7193 + + $view = view('popup.report.category-entry', compact('journals', 'category'))->render(); return $view; } diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index cc0ec6b192..4ee0a03061 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -174,115 +174,6 @@ class CategoryRepository implements CategoryRepositoryInterface return $set; } - /** - * @param Category $category - * @param int $page - * @param int $pageSize - * - * @return LengthAwarePaginator - */ - public function getJournals(Category $category, int $page, int $pageSize): LengthAwarePaginator - { - $complete = new Collection; - // first collect actual transaction journals (fairly easy) - $query = $this->user->transactionJournals()->expanded()->sortCorrectly(); - $query->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'); - $query->where('category_transaction_journal.category_id', $category->id); - $first = $query->get(TransactionJournal::queryFields()); - - // then collection transactions (harder) - $query = $this->user->transactionJournals()->distinct() - ->leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->leftJoin('category_transaction', 'category_transaction.transaction_id', '=', 'transactions.id') - ->where('category_transaction.category_id', $category->id); - $second = $query->get(['transaction_journals.*']); - - $complete = $complete->merge($first); - $complete = $complete->merge($second); - - // sort: - /** @var Collection $complete */ - $complete = $complete->sortByDesc( - function ($model) { - $date = new Carbon($model->date); - - return intval($date->format('U')); - } - ); - // create paginator - $offset = ($page - 1) * $pageSize; - $subSet = $complete->slice($offset, $pageSize)->all(); - $paginator = new LengthAwarePaginator($subSet, $complete->count(), $pageSize, $page); - - return $paginator; - } - - /** - * Get all transactions in a category in a range. - * - * @param Collection $categories - * @param Collection $accounts - * @param array $types - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function journalsInPeriod(Collection $categories, Collection $accounts, array $types, Carbon $start, Carbon $end): Collection - { - $complete = new Collection; - // first collect actual transaction journals (fairly easy) - $query = $this->user->transactionJournals()->expanded()->sortCorrectly(); - - if ($end >= $start) { - $query->before($end)->after($start); - } - - if (count($types) > 0) { - $query->transactionTypes($types); - } - - if ($accounts->count() > 0) { - $accountIds = $accounts->pluck('id')->toArray(); - $query->leftJoin('transactions as t', 't.transaction_journal_id', '=', 'transaction_journals.id'); - $query->whereIn('t.account_id', $accountIds); - } - if ($categories->count() > 0) { - $categoryIds = $categories->pluck('id')->toArray(); - $query->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'); - $query->whereIn('category_transaction_journal.category_id', $categoryIds); - } - - // that should do it: - $first = $query->get(TransactionJournal::queryFields()); - - - // then collection transactions (harder) - $query = $this->user->transactionJournals()->distinct() - ->leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->leftJoin('category_transaction', 'category_transaction.transaction_id', '=', 'transactions.id') - ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'); - if (count($types) > 0) { - $query->whereIn('transaction_types.type', $types); - } - if ($accounts->count() > 0) { - $accountIds = $accounts->pluck('id')->toArray(); - $query->whereIn('transactions.account_id', $accountIds); - } - if ($categories->count() > 0) { - $categoryIds = $categories->pluck('id')->toArray(); - $query->whereIn('category_transaction.category_id', $categoryIds); - } - - - $second = $query->get(['transaction_journals.*', 'transaction_types.type as transaction_type_type']); - - $complete = $complete->merge($first); - $complete = $complete->merge($second); - - return $complete; - } - /** * @param Collection $accounts * @param array $types diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index d5aaff5bc2..b6079c0be8 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -84,26 +84,6 @@ interface CategoryRepositoryInterface */ public function getCategories(): Collection; - /** - * @param Category $category - * @param int $page - * @param int $pageSize - * - * @return LengthAwarePaginator - */ - public function getJournals(Category $category, int $page, int $pageSize): LengthAwarePaginator; - - /** - * @param Collection $categories - * @param Collection $accounts - * @param array $types - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function journalsInPeriod(Collection $categories, Collection $accounts, array $types, Carbon $start, Carbon $end): Collection; - /** * @param Collection $accounts * @param array $types diff --git a/resources/views/categories/show.twig b/resources/views/categories/show.twig index f80113b6cc..0800aaf7b9 100644 --- a/resources/views/categories/show.twig +++ b/resources/views/categories/show.twig @@ -35,7 +35,7 @@

{{ 'transactions'|_ }}

- {% include 'list.journals' %} + {% include 'list.journals-tasker' %}
diff --git a/resources/views/categories/show_with_date.twig b/resources/views/categories/show_with_date.twig index 4e1cb77b81..6b906643d1 100644 --- a/resources/views/categories/show_with_date.twig +++ b/resources/views/categories/show_with_date.twig @@ -31,7 +31,7 @@

{{ 'transactions'|_ }}

- {% include 'list.journals' %} + {% include 'list.journals-tasker' %}
diff --git a/resources/views/popup/report/category-entry.twig b/resources/views/popup/report/category-entry.twig index 1507983e19..d1ca6ef55a 100644 --- a/resources/views/popup/report/category-entry.twig +++ b/resources/views/popup/report/category-entry.twig @@ -8,7 +8,7 @@
- {% include 'list.journals' with {'journals': list} %} + {% include 'list.journals-tasker' %}
From 205a593721ae052c7af1449a001b657b3ae3ea49 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 5 Nov 2016 10:28:10 +0100 Subject: [PATCH 042/709] Removed unused method. --- app/Http/Controllers/CategoryController.php | 9 +-- .../Category/CategoryRepository.php | 61 ------------------- .../Category/CategoryRepositoryInterface.php | 10 --- 3 files changed, 2 insertions(+), 78 deletions(-) diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index 8f83de9261..39b3bf5b2f 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -147,29 +147,24 @@ class CategoryController extends Controller } /** - * @param CRI $repository - * * @return View */ - public function noCategory(CRI $repository) + public function noCategory() { /** @var Carbon $start */ $start = session('start', Carbon::now()->startOfMonth()); /** @var Carbon $end */ - $end = session('end', Carbon::now()->startOfMonth()); + $end = session('end', Carbon::now()->startOfMonth()); // new collector: $collector = new JournalCollector(auth()->user()); $collector->setAllAssetAccounts()->setRange($start, $end)->withoutCategory();//->groupJournals(); $journals = $collector->getJournals(); -// $list = $repository->journalsInPeriodWithoutCategory(new Collection(), [], $start, $end); // category $subTitle = trans( 'firefly.without_category_between', ['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)] ); - - return view('categories.no-category', compact('journals', 'subTitle')); } diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 4ee0a03061..9004fd682a 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -174,67 +174,6 @@ class CategoryRepository implements CategoryRepositoryInterface return $set; } - /** - * @param Collection $accounts - * @param array $types - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function journalsInPeriodWithoutCategory(Collection $accounts, array $types, Carbon $start, Carbon $end) : Collection - { - /** @var Collection $set */ - $query = $this->user - ->transactionJournals(); - if (count($types) > 0) { - $query->transactionTypes($types); - } - - $query->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->whereNull('category_transaction_journal.id') - ->before($end) - ->after($start); - - if ($accounts->count() > 0) { - $accountIds = $accounts->pluck('id')->toArray(); - $query->leftJoin('transactions as t', 't.transaction_journal_id', '=', 'transaction_journals.id'); - $query->whereIn('t.account_id', $accountIds); - } - - $set = $query->get(['transaction_journals.*']); - - if ($set->count() == 0) { - return new Collection; - } - - // grab all the transactions from this set. - // take only the journals with transactions that all have no category. - // select transactions left join journals where id in this set - // and left join transaction-category where null category - $journalIds = $set->pluck('id')->toArray(); - $secondQuery = $this->user->transactions() - ->leftJoin('category_transaction', 'category_transaction.transaction_id', '=', 'transactions.id') - ->whereNull('category_transaction.id') - ->whereIn('transaction_journals.id', $journalIds); - - if ($accounts->count() > 0) { - $accountIds = $accounts->pluck('id')->toArray(); - $secondQuery->whereIn('transactions.account_id', $accountIds); - } - - // this second set REALLY doesn't have any categories. - $secondSet = $secondQuery->get(['transactions.transaction_journal_id']); - $allIds = $secondSet->pluck('transaction_journal_id')->toArray(); - $return = $this->user->transactionJournals()->sortCorrectly()->expanded()->whereIn('transaction_journals.id', $allIds)->get( - TransactionJournal::queryFields() - ); - - return $return; - - - } - /** * @param Category $category * @param Collection $accounts diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index b6079c0be8..b7e2f68e08 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -84,16 +84,6 @@ interface CategoryRepositoryInterface */ public function getCategories(): Collection; - /** - * @param Collection $accounts - * @param array $types - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function journalsInPeriodWithoutCategory(Collection $accounts, array $types, Carbon $start, Carbon $end) : Collection; - /** * Return most recent transaction(journal) date. * From aeca2ef3b256bc803d7244141edaa8ef3f9a0316 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 5 Nov 2016 10:42:31 +0100 Subject: [PATCH 043/709] Move some code around --- app/Helpers/Collector/JournalCollector.php | 30 +++++--- app/Http/Controllers/AccountController.php | 25 +++---- app/Http/Controllers/HomeController.php | 9 +-- .../Controllers/Popup/ReportController.php | 16 ++--- app/Http/Controllers/ReportController.php | 12 ++-- app/Repositories/Account/AccountTasker.php | 71 ------------------- .../Account/AccountTaskerInterface.php | 12 ---- 7 files changed, 52 insertions(+), 123 deletions(-) diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index 2b454d47e6..030e7ec7f1 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -54,7 +54,8 @@ class JournalCollector 'accounts.encrypted as account_encrypted', 'account_types.type as account_type', ]; - + /** @var bool */ + private $filterTransfers = false; /** @var array */ private $group = [ @@ -68,7 +69,6 @@ class JournalCollector 'bills.name', 'transactions.amount', ]; - /** @var bool */ private $joinedCategory = false; /** @var int */ @@ -127,15 +127,17 @@ class JournalCollector $set = $this->query->get(array_values($this->fields)); // filter out transfers: - $set = $set->filter( - function (Transaction $transaction) { - if (!($transaction->transaction_type_type === TransactionType::TRANSFER && bccomp($transaction->transaction_amount, '0') === -1)) { - return $transaction; - } + if ($this->filterTransfers) { + $set = $set->filter( + function (Transaction $transaction) { + if (!($transaction->transaction_type_type === TransactionType::TRANSFER && bccomp($transaction->transaction_amount, '0') === -1)) { + return $transaction; + } - return false; - } - ); + return false; + } + ); + } // loop for decryption. $set->each( @@ -177,6 +179,10 @@ class JournalCollector $this->query->whereIn('transactions.account_id', $accountIds); } + if ($accounts->count() > 1) { + $this->filterTransfers = true; + } + return $this; } @@ -193,6 +199,10 @@ class JournalCollector $this->query->whereIn('transactions.account_id', $accountIds); } + if ($accounts->count() > 1) { + $this->filterTransfers = true; + } + return $this; } diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 09fe795a2d..5258baa91b 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -16,6 +16,7 @@ namespace FireflyIII\Http\Controllers; use Carbon\Carbon; use ExpandedForm; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Http\Requests\AccountFormRequest; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; @@ -222,12 +223,12 @@ class AccountController extends Controller /** @var Carbon $end */ $end = session('end', Navigation::endOfPeriod(new Carbon, $range)); $page = intval(Input::get('page')); - $pageSize = Preferences::get('transactionPageSize', 50)->data; - $offset = ($page - 1) * $pageSize; - $set = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end); - $count = $set->count(); - $subSet = $set->splice($offset, $pageSize); - $journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); + + // replace with journal collector: + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page); + $journals = $collector->getPaginatedJournals(); $journals->setPath('accounts/show/' . $account->id); // grouped other months thing: @@ -290,12 +291,12 @@ class AccountController extends Controller $subTitle = $account->name . ' (' . Navigation::periodShow($start, $range) . ')'; $page = intval(Input::get('page')); $page = $page === 0 ? 1 : $page; - $pageSize = Preferences::get('transactionPageSize', 50)->data; - $offset = ($page - 1) * $pageSize; - $set = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end); - $count = $set->count(); - $subSet = $set->splice($offset, $pageSize); - $journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); + + // replace with journal collector: + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page); + $journals = $collector->getPaginatedJournals(); $journals->setPath('accounts/show/' . $account->id . '/' . $date); return view('accounts.show_with_date', compact('category', 'date', 'account', 'journals', 'subTitle', 'carbon')); diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 05f3daa755..8137923969 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -15,6 +15,7 @@ namespace FireflyIII\Http\Controllers; use Artisan; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Models\AccountType; use FireflyIII\Models\Tag; use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; @@ -116,11 +117,10 @@ class HomeController extends Controller /** * @param ARI $repository - * @param AccountTaskerInterface $tasker * * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View */ - public function index(ARI $repository, AccountTaskerInterface $tasker) + public function index(ARI $repository) { $types = config('firefly.accountTypesByIdentifier.asset'); @@ -144,8 +144,9 @@ class HomeController extends Controller $showDepositsFrontpage = Preferences::get('showDepositsFrontpage', false)->data; foreach ($accounts as $account) { - $set = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end); - $set = $set->splice(0, 10); + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit(10)->setPage(1); + $set = $collector->getJournals(); if (count($set) > 0) { $transactions[] = [$set, $account]; diff --git a/app/Http/Controllers/Popup/ReportController.php b/app/Http/Controllers/Popup/ReportController.php index e023cb8284..7f0c7d01fa 100644 --- a/app/Http/Controllers/Popup/ReportController.php +++ b/app/Http/Controllers/Popup/ReportController.php @@ -197,14 +197,14 @@ class ReportController extends Controller */ private function expenseEntry(array $attributes): string { - /** @var AccountTaskerInterface $tasker */ - $tasker = app(AccountTaskerInterface::class); /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); - $account = $repository->find(intval($attributes['accountId'])); - $types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]; - $journals = $tasker->getJournalsInPeriod(new Collection([$account]), $types, $attributes['startDate'], $attributes['endDate']); + $account = $repository->find(intval($attributes['accountId'])); + $types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]; + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setTypes($types); + $journals = $collector->getJournals(); $report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report // filter for transfers and withdrawals TO the given $account @@ -233,13 +233,13 @@ class ReportController extends Controller */ private function incomeEntry(array $attributes): string { - /** @var AccountTaskerInterface $tasker */ - $tasker = app(AccountTaskerInterface::class); /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); $account = $repository->find(intval($attributes['accountId'])); $types = [TransactionType::DEPOSIT, TransactionType::TRANSFER]; - $journals = $tasker->getJournalsInPeriod(new Collection([$account]), $types, $attributes['startDate'], $attributes['endDate']); + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setTypes($types); + $journals = $collector->getJournals(); $report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report // filter the set so the destinations outside of $attributes['accounts'] are not included. diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index 6ac94f0883..2f024c88bd 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -15,6 +15,7 @@ namespace FireflyIII\Http\Controllers; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Helpers\Report\ReportHelperInterface; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; @@ -150,8 +151,6 @@ class ReportController extends Controller */ private function auditReport(Carbon $start, Carbon $end, Collection $accounts) { - /** @var AccountTaskerInterface $tasker */ - $tasker = app(AccountTaskerInterface::class); $auditData = []; $dayBefore = clone $start; $dayBefore->subDay(); @@ -160,9 +159,11 @@ class ReportController extends Controller // balance the day before: $id = $account->id; $dayBeforeBalance = Steam::balance($account, $dayBefore); - $journals = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end); - $journals = $journals->reverse(); - $startBalance = $dayBeforeBalance; + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts(new Collection([$account]))->setRange($start, $end); + $journals = $collector->getJournals(); + $journals = $journals->reverse(); + $startBalance = $dayBeforeBalance; /** @var Transaction $journal */ @@ -246,7 +247,6 @@ class ReportController extends Controller // need all years. - // and some id's, joined: $accountIds = []; /** @var Account $account */ diff --git a/app/Repositories/Account/AccountTasker.php b/app/Repositories/Account/AccountTasker.php index 59b46c1b88..8bba27f601 100644 --- a/app/Repositories/Account/AccountTasker.php +++ b/app/Repositories/Account/AccountTasker.php @@ -190,77 +190,6 @@ class AccountTasker implements AccountTaskerInterface return $object; } - /** - * It might be worth it to expand this query to include all account information required. - * - * @param Collection $accounts - * @param array $types - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function getJournalsInPeriod(Collection $accounts, array $types, Carbon $start, Carbon $end): Collection - { - $accountIds = $accounts->pluck('id')->toArray(); - $query = Transaction - ::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transaction_journals.transaction_currency_id') - ->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id') - ->leftJoin('bills', 'bills.id', 'transaction_journals.bill_id') - ->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id') - ->leftJoin('account_types', 'accounts.account_type_id', 'account_types.id') - ->whereIn('transactions.account_id', $accountIds) - ->whereNull('transactions.deleted_at') - ->whereNull('transaction_journals.deleted_at') - ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) - ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) - ->where('transaction_journals.user_id', $this->user->id) - ->orderBy('transaction_journals.date', 'DESC') - ->orderBy('transaction_journals.order', 'ASC') - ->orderBy('transaction_journals.id', 'DESC'); - - if (count($types) > 0) { - $query->whereIn('transaction_types.type', $types); - } - - $set = $query->get( - [ - 'transaction_journals.id as journal_id', - 'transaction_journals.description', - 'transaction_journals.date', - 'transaction_journals.encrypted', - //'transaction_journals.transaction_currency_id', - 'transaction_currencies.code as transaction_currency_code', - //'transaction_currencies.symbol as transaction_currency_symbol', - 'transaction_types.type as transaction_type_type', - 'transaction_journals.bill_id', - 'bills.name as bill_name', - 'transactions.id as id', - 'transactions.amount as transaction_amount', - 'transactions.description as transaction_description', - 'transactions.account_id', - 'transactions.identifier', - 'transactions.transaction_journal_id', - 'accounts.name as account_name', - 'accounts.encrypted as account_encrypted', - 'account_types.type as account_type', - - ] - ); - - // loop for decryption. - $set->each( - function (Transaction $transaction) { - $transaction->date = new Carbon($transaction->date); - $transaction->description = intval($transaction->encrypted) === 1 ? Crypt::decrypt($transaction->description) : $transaction->description; - $transaction->bill_name = !is_null($transaction->bill_name) ? Crypt::decrypt($transaction->bill_name) : ''; - } - ); - - return $set; - } - /** * @param Collection $accounts * @param Collection $excluded diff --git a/app/Repositories/Account/AccountTaskerInterface.php b/app/Repositories/Account/AccountTaskerInterface.php index dcb8c10416..dbf1892667 100644 --- a/app/Repositories/Account/AccountTaskerInterface.php +++ b/app/Repositories/Account/AccountTaskerInterface.php @@ -71,18 +71,6 @@ interface AccountTaskerInterface */ public function getAccountReport(Carbon $start, Carbon $end, Collection $accounts): AccountCollection; - /** - * Experimental getJournals method. - * - * @param Collection $accounts - * @param array $types - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function getJournalsInPeriod(Collection $accounts, array $types, Carbon $start, Carbon $end): Collection; - /** * @param Collection $accounts * @param Collection $excluded From 43afdb021a70096e008441b3cc839931b1aef41b Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 5 Nov 2016 11:24:15 +0100 Subject: [PATCH 044/709] Move collecting journals to the collector. --- app/Helpers/Collector/JournalCollector.php | 16 +++++++++ app/Helpers/Report/ReportHelper.php | 14 ++++---- app/Repositories/Bill/BillRepository.php | 35 ------------------- .../Bill/BillRepositoryInterface.php | 12 ------- 4 files changed, 24 insertions(+), 53 deletions(-) diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index 030e7ec7f1..bc9ca44616 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -206,6 +206,22 @@ class JournalCollector return $this; } + /** + * @param Collection $bills + * + * @return JournalCollector + */ + public function setBills(Collection $bills): JournalCollector + { + if ($bills->count() > 0) { + $billIds = $bills->pluck('id')->toArray(); + $this->query->whereIn('transaction_journals.bill_id', $billIds); + } + + return $this; + + } + /** * @param Category $category * diff --git a/app/Helpers/Report/ReportHelper.php b/app/Helpers/Report/ReportHelper.php index b95ac24315..27c76bb3c5 100644 --- a/app/Helpers/Report/ReportHelper.php +++ b/app/Helpers/Report/ReportHelper.php @@ -14,17 +14,17 @@ declare(strict_types = 1); namespace FireflyIII\Helpers\Report; use Carbon\Carbon; -use DB; use FireflyIII\Helpers\Collection\Bill as BillCollection; use FireflyIII\Helpers\Collection\BillLine; use FireflyIII\Helpers\Collection\Category as CategoryCollection; use FireflyIII\Helpers\Collection\Expense; use FireflyIII\Helpers\Collection\Income; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Helpers\FiscalHelperInterface; use FireflyIII\Models\Bill; -use FireflyIII\Models\Budget; use FireflyIII\Models\Category; use FireflyIII\Models\Tag; +use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface; @@ -79,7 +79,9 @@ class ReportHelper implements ReportHelperInterface /** @var BillRepositoryInterface $repository */ $repository = app(BillRepositoryInterface::class); $bills = $repository->getBillsForAccounts($accounts); - $journals = $repository->getAllJournalsInRange($bills, $start, $end); + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($accounts)->setRange($start, $end)->setBills($bills); + $journals = $collector->getJournals(); $collection = new BillCollection; /** @var Bill $bill */ @@ -93,14 +95,14 @@ class ReportHelper implements ReportHelperInterface // is hit in period? $entry = $journals->filter( - function (TransactionJournal $journal) use ($bill) { - return $journal->bill_id === $bill->id; + function (Transaction $transaction) use ($bill) { + return $transaction->bill_id === $bill->id; } ); $first = $entry->first(); if (!is_null($first)) { $billLine->setTransactionJournalId($first->id); - $billLine->setAmount($first->journalAmount); + $billLine->setAmount($first->transaction_amount); $billLine->setHit(true); } diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index f086a4b19b..2a34567986 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -116,41 +116,6 @@ class BillRepository implements BillRepositoryInterface return $set; } - /** - * Returns all journals connected to these bills in the given range. Amount paid - * is stored in "journalAmount" as a negative number. - * - * @param Collection $bills - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function getAllJournalsInRange(Collection $bills, Carbon $start, Carbon $end): Collection - { - $ids = $bills->pluck('id')->toArray(); - - $set = $this->user->transactionJournals() - ->leftJoin( - 'transactions', function (JoinClause $join) { - $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0); - } - ) - ->whereIn('bill_id', $ids) - ->before($end) - ->after($start) - ->groupBy(['transaction_journals.bill_id', 'transaction_journals.id']) - ->get( - [ - 'transaction_journals.bill_id', - 'transaction_journals.id', - DB::raw('SUM(transactions.amount) AS journalAmount'), - ] - ); - - return $set; - } - /** * @return Collection */ diff --git a/app/Repositories/Bill/BillRepositoryInterface.php b/app/Repositories/Bill/BillRepositoryInterface.php index 26e5f22aef..8e7024240e 100644 --- a/app/Repositories/Bill/BillRepositoryInterface.php +++ b/app/Repositories/Bill/BillRepositoryInterface.php @@ -57,18 +57,6 @@ interface BillRepositoryInterface */ public function getActiveBills(): Collection; - /** - * Returns all journals connected to these bills in the given range. Amount paid - * is stored in "journalAmount" as a negative number. - * - * @param Collection $bills - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function getAllJournalsInRange(Collection $bills, Carbon $start, Carbon $end): Collection; - /** * @return Collection */ From 8e542531b35459f2560499039e179aeb8d998f05 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 5 Nov 2016 11:44:41 +0100 Subject: [PATCH 045/709] Move collecting journals to the collector. --- .../Chart/Bill/ChartJsBillChartGenerator.php | 6 ++--- app/Http/Controllers/BillController.php | 9 ++++++- app/Http/Controllers/Chart/BillController.php | 17 +++++++------ app/Repositories/Bill/BillRepository.php | 24 ------------------- .../Bill/BillRepositoryInterface.php | 10 -------- resources/views/bills/show.twig | 4 ++-- 6 files changed, 23 insertions(+), 47 deletions(-) diff --git a/app/Generator/Chart/Bill/ChartJsBillChartGenerator.php b/app/Generator/Chart/Bill/ChartJsBillChartGenerator.php index e59b49f136..07f29dd58b 100644 --- a/app/Generator/Chart/Bill/ChartJsBillChartGenerator.php +++ b/app/Generator/Chart/Bill/ChartJsBillChartGenerator.php @@ -14,7 +14,7 @@ declare(strict_types = 1); namespace FireflyIII\Generator\Chart\Bill; use FireflyIII\Models\Bill; -use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\Transaction; use Illuminate\Support\Collection; /** @@ -61,13 +61,13 @@ class ChartJsBillChartGenerator implements BillChartGeneratorInterface $minAmount = []; $maxAmount = []; $actualAmount = []; - /** @var TransactionJournal $entry */ + /** @var Transaction $entry */ foreach ($entries as $entry) { $data['labels'][] = $entry->date->formatLocalized($format); $minAmount[] = round($bill->amount_min, 2); $maxAmount[] = round($bill->amount_max, 2); // journalAmount has been collected in BillRepository::getJournals - $actualAmount[] = round(TransactionJournal::amountPositive($entry), 2); + $actualAmount[] = bcmul($entry->transaction_amount, '-1'); } $data['datasets'][] = [ diff --git a/app/Http/Controllers/BillController.php b/app/Http/Controllers/BillController.php index 31de91d724..e0efc0f812 100644 --- a/app/Http/Controllers/BillController.php +++ b/app/Http/Controllers/BillController.php @@ -14,10 +14,12 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; use Carbon\Carbon; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Http\Requests\BillFormRequest; use FireflyIII\Models\Bill; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Bill\BillRepositoryInterface; +use Illuminate\Support\Collection; use Input; use Preferences; use Session; @@ -200,10 +202,15 @@ class BillController extends Controller $year = $date->year; $page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); - $journals = $repository->getJournals($bill, $page, $pageSize); $yearAverage = $repository->getYearAverage($bill, $date); $overallAverage = $repository->getOverallAverage($bill); + + // use collector: + $collector = new JournalCollector(auth()->user()); + $collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->setPage($page)->setLimit($pageSize); + $journals = $collector->getPaginatedJournals(); $journals->setPath('/bills/show/' . $bill->id); + $bill->nextExpectedMatch = $repository->nextExpectedMatch($bill, new Carbon); $hideBill = true; $subTitle = e($bill->name); diff --git a/app/Http/Controllers/Chart/BillController.php b/app/Http/Controllers/Chart/BillController.php index 78385b915b..51ca561e75 100644 --- a/app/Http/Controllers/Chart/BillController.php +++ b/app/Http/Controllers/Chart/BillController.php @@ -15,11 +15,13 @@ namespace FireflyIII\Http\Controllers\Chart; use Carbon\Carbon; use FireflyIII\Generator\Chart\Bill\BillChartGeneratorInterface; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Bill; -use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\Transaction; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Support\CacheProperties; +use Illuminate\Support\Collection; use Response; /** @@ -64,12 +66,11 @@ class BillController extends Controller /** * Shows the overview for a bill. The min/max amount and matched journals. * - * @param BillRepositoryInterface $repository - * @param Bill $bill + * @param Bill $bill * * @return \Symfony\Component\HttpFoundation\Response */ - public function single(BillRepositoryInterface $repository, Bill $bill) + public function single(Bill $bill) { $cache = new CacheProperties; $cache->addProperty('single'); @@ -80,12 +81,14 @@ class BillController extends Controller } // get first transaction or today for start: - $results = $repository->getJournals($bill, 1, 200); + $collector = new JournalCollector(auth()->user()); + $collector->setAllAssetAccounts()->setBills(new Collection([$bill])); + $results = $collector->getJournals(); // resort: $results = $results->sortBy( - function (TransactionJournal $journal) { - return $journal->date->format('U'); + function (Transaction $transaction) { + return $transaction->date->format('U'); } ); diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index 2a34567986..4a35819c56 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -252,30 +252,6 @@ class BillRepository implements BillRepositoryInterface return $sum; } - /** - * This method also returns the amount of the journal in "journalAmount" - * for easy access. - * - * @param Bill $bill - * - * @param int $page - * @param int $pageSize - * - * @return LengthAwarePaginator|Collection - */ - public function getJournals(Bill $bill, int $page, int $pageSize = 50): LengthAwarePaginator - { - $offset = ($page - 1) * $pageSize; - $query = $bill->transactionJournals() - ->expanded() - ->sortCorrectly(); - $count = $query->count(); - $set = $query->take($pageSize)->offset($offset)->get(TransactionJournal::queryFields()); - $paginator = new LengthAwarePaginator($set, $count, $pageSize, $page); - - return $paginator; - } - /** * @param Bill $bill * diff --git a/app/Repositories/Bill/BillRepositoryInterface.php b/app/Repositories/Bill/BillRepositoryInterface.php index 8e7024240e..16368e53d1 100644 --- a/app/Repositories/Bill/BillRepositoryInterface.php +++ b/app/Repositories/Bill/BillRepositoryInterface.php @@ -91,16 +91,6 @@ interface BillRepositoryInterface */ public function getBillsUnpaidInRange(Carbon $start, Carbon $end): string; - /** - * @param Bill $bill - * - * @param int $page - * @param int $pageSize - * - * @return LengthAwarePaginator - */ - public function getJournals(Bill $bill, int $page, int $pageSize = 50): LengthAwarePaginator; - /** * @param Bill $bill * diff --git a/resources/views/bills/show.twig b/resources/views/bills/show.twig index 85dd232568..7622ee694d 100644 --- a/resources/views/bills/show.twig +++ b/resources/views/bills/show.twig @@ -105,8 +105,8 @@

{{ 'connected_journals'|_ }}

-
- {% include 'list.journals' %} +
+ {% include 'list.journals-tasker' %}
From 13e1292bb7f56472e4970e84d7466d6251a5964a Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 5 Nov 2016 11:47:21 +0100 Subject: [PATCH 046/709] Automated code cleanup [skip ci] --- .../Events/UpdatedJournalEventHandler.php | 3 -- app/Helpers/Help/HelpInterface.php | 2 +- app/Helpers/Report/ReportHelper.php | 1 - app/Http/Controllers/AccountController.php | 1 - app/Http/Controllers/BillController.php | 4 +-- app/Http/Controllers/Controller.php | 1 - app/Http/Controllers/HelpController.php | 4 +-- app/Http/Controllers/HomeController.php | 3 +- app/Http/Controllers/JsonController.php | 3 +- .../Controllers/Popup/ReportController.php | 5 ++- app/Http/Controllers/ReportController.php | 1 - .../Controllers/TransactionController.php | 18 +++++----- app/Models/Note.php | 3 +- app/Models/PiggyBank.php | 34 +++++++++---------- app/Repositories/Account/AccountTasker.php | 3 +- app/Repositories/Bill/BillRepository.php | 1 - .../Bill/BillRepositoryInterface.php | 1 - app/Repositories/Budget/BudgetRepository.php | 4 +-- .../Category/CategoryRepository.php | 1 - .../Category/CategoryRepositoryInterface.php | 1 - .../Journal/JournalRepositoryInterface.php | 18 +++++----- 21 files changed, 48 insertions(+), 64 deletions(-) diff --git a/app/Handlers/Events/UpdatedJournalEventHandler.php b/app/Handlers/Events/UpdatedJournalEventHandler.php index b6b7a8c8fd..60871ebf08 100644 --- a/app/Handlers/Events/UpdatedJournalEventHandler.php +++ b/app/Handlers/Events/UpdatedJournalEventHandler.php @@ -15,11 +15,8 @@ namespace FireflyIII\Handlers\Events; use FireflyIII\Events\UpdatedTransactionJournal; -use FireflyIII\Models\PiggyBankEvent; -use FireflyIII\Models\PiggyBankRepetition; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleGroup; -use FireflyIII\Models\TransactionJournal; use FireflyIII\Rules\Processor; use FireflyIII\Support\Events\BillScanner; diff --git a/app/Helpers/Help/HelpInterface.php b/app/Helpers/Help/HelpInterface.php index 7356ba0ba2..4187ddf1ce 100644 --- a/app/Helpers/Help/HelpInterface.php +++ b/app/Helpers/Help/HelpInterface.php @@ -49,7 +49,7 @@ interface HelpInterface * * @return bool */ - public function inCache(string $route, string $language ): bool; + public function inCache(string $route, string $language): bool; /** * @param string $route diff --git a/app/Helpers/Report/ReportHelper.php b/app/Helpers/Report/ReportHelper.php index 27c76bb3c5..5110e3d6c5 100644 --- a/app/Helpers/Report/ReportHelper.php +++ b/app/Helpers/Report/ReportHelper.php @@ -25,7 +25,6 @@ use FireflyIII\Models\Bill; use FireflyIII\Models\Category; use FireflyIII\Models\Tag; use FireflyIII\Models\Transaction; -use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 5258baa91b..57f532f28f 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -24,7 +24,6 @@ use FireflyIII\Models\Transaction; use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Support\CacheProperties; -use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Input; use Navigation; diff --git a/app/Http/Controllers/BillController.php b/app/Http/Controllers/BillController.php index e0efc0f812..4cccbdeba3 100644 --- a/app/Http/Controllers/BillController.php +++ b/app/Http/Controllers/BillController.php @@ -206,9 +206,9 @@ class BillController extends Controller $overallAverage = $repository->getOverallAverage($bill); // use collector: - $collector = new JournalCollector(auth()->user()); + $collector = new JournalCollector(auth()->user()); $collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->setPage($page)->setLimit($pageSize); - $journals = $collector->getPaginatedJournals(); + $journals = $collector->getPaginatedJournals(); $journals->setPath('/bills/show/' . $bill->id); $bill->nextExpectedMatch = $repository->nextExpectedMatch($bill, new Carbon); diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 727ef34f7a..6109a79967 100755 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -13,7 +13,6 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; -use Carbon\Carbon; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; diff --git a/app/Http/Controllers/HelpController.php b/app/Http/Controllers/HelpController.php index 2d4299818e..24d2d75254 100644 --- a/app/Http/Controllers/HelpController.php +++ b/app/Http/Controllers/HelpController.php @@ -42,8 +42,8 @@ class HelpController extends Controller public function show(HelpInterface $help, string $route) { - $language = Preferences::get('language', config('firefly.default_language', 'en_US'))->data; - $content = '

' . strval(trans('firefly.route_has_no_help')) . '

'; + $language = Preferences::get('language', config('firefly.default_language', 'en_US'))->data; + $content = '

' . strval(trans('firefly.route_has_no_help')) . '

'; if (!$help->hasRoute($route)) { Log::error('No such route: ' . $route); diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 8137923969..40872742b5 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -19,7 +19,6 @@ use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Models\AccountType; use FireflyIII\Models\Tag; use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; -use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use Illuminate\Http\Request; use Illuminate\Support\Collection; @@ -116,7 +115,7 @@ class HomeController extends Controller } /** - * @param ARI $repository + * @param ARI $repository * * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View */ diff --git a/app/Http/Controllers/JsonController.php b/app/Http/Controllers/JsonController.php index 197fd7a326..33beceec79 100644 --- a/app/Http/Controllers/JsonController.php +++ b/app/Http/Controllers/JsonController.php @@ -21,7 +21,6 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI; -use FireflyIII\Repositories\Journal\JournalTaskerInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Support\CacheProperties; use Input; @@ -282,7 +281,7 @@ class JsonController extends Controller $types = [$type]; // use journal collector instead: - $collector = new JournalCollector(auth()->user()); + $collector = new JournalCollector(auth()->user()); $collector->setTypes($types)->setLimit(100)->setPage(1); $journals = $collector->getJournals(); foreach ($journals as $j) { diff --git a/app/Http/Controllers/Popup/ReportController.php b/app/Http/Controllers/Popup/ReportController.php index 7f0c7d01fa..b31e514853 100644 --- a/app/Http/Controllers/Popup/ReportController.php +++ b/app/Http/Controllers/Popup/ReportController.php @@ -23,7 +23,6 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Support\Binder\AccountList; @@ -237,10 +236,10 @@ class ReportController extends Controller $repository = app(AccountRepositoryInterface::class); $account = $repository->find(intval($attributes['accountId'])); $types = [TransactionType::DEPOSIT, TransactionType::TRANSFER]; - $collector = new JournalCollector(auth()->user()); + $collector = new JournalCollector(auth()->user()); $collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setTypes($types); $journals = $collector->getJournals(); - $report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report + $report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report // filter the set so the destinations outside of $attributes['accounts'] are not included. $journals = $journals->filter( diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index 2f024c88bd..a92648b398 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -21,7 +21,6 @@ use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use FireflyIII\Models\Transaction; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Account\AccountTaskerInterface; use Illuminate\Support\Collection; use Preferences; use Session; diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 8f443f5950..517ceedd7b 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -15,9 +15,7 @@ namespace FireflyIII\Http\Controllers; use Carbon\Carbon; use FireflyIII\Helpers\Collector\JournalCollector; -use FireflyIII\Models\AccountType; use FireflyIII\Models\TransactionJournal; -use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalTaskerInterface; use Illuminate\Http\Request; @@ -52,19 +50,19 @@ class TransactionController extends Controller } /** - * @param Request $request - * @param string $what + * @param Request $request + * @param string $what * * @return View */ public function index(Request $request, string $what) { - $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); - $subTitleIcon = config('firefly.transactionIconsByWhat.' . $what); - $types = config('firefly.transactionTypesByWhat.' . $what); - $subTitle = trans('firefly.title_' . $what); - $page = intval($request->get('page')); - $collector = new JournalCollector(auth()->user()); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); + $subTitleIcon = config('firefly.transactionIconsByWhat.' . $what); + $types = config('firefly.transactionTypesByWhat.' . $what); + $subTitle = trans('firefly.title_' . $what); + $page = intval($request->get('page')); + $collector = new JournalCollector(auth()->user()); $collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts(); $journals = $collector->getPaginatedJournals(); diff --git a/app/Models/Note.php b/app/Models/Note.php index c811fcc7cd..17444ba3e7 100644 --- a/app/Models/Note.php +++ b/app/Models/Note.php @@ -52,7 +52,8 @@ class Note extends Model */ public function getMarkdownAttribute(): string { - $converter = new CommonMarkConverter; + $converter = new CommonMarkConverter; + return $converter->convertToHtml($this->text); } diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index 3d5e5da150..9a57917b26 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -23,22 +23,22 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\PiggyBank * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property \Carbon\Carbon $deleted_at - * @property integer $account_id - * @property string $name - * @property float $targetamount - * @property \Carbon\Carbon $startdate - * @property \Carbon\Carbon $targetdate - * @property integer $order - * @property boolean $encrypted - * @property-read Account $account - * @property-read \Illuminate\Database\Eloquent\Collection|PiggyBankRepetition[] $piggyBankRepetitions - * @property-read \Illuminate\Database\Eloquent\Collection|PiggyBankEvent[] $piggyBankEvents - * @property string $reminder - * @property PiggyBankRepetition $currentRep + * @property integer $id + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property \Carbon\Carbon $deleted_at + * @property integer $account_id + * @property string $name + * @property float $targetamount + * @property \Carbon\Carbon $startdate + * @property \Carbon\Carbon $targetdate + * @property integer $order + * @property boolean $encrypted + * @property-read Account $account + * @property-read \Illuminate\Database\Eloquent\Collection|PiggyBankRepetition[] $piggyBankRepetitions + * @property-read \Illuminate\Database\Eloquent\Collection|PiggyBankEvent[] $piggyBankEvents + * @property string $reminder + * @property PiggyBankRepetition $currentRep * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereId($value) * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereCreatedAt($value) * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereUpdatedAt($value) @@ -54,7 +54,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereDeletedAt($value) * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereEncrypted($value) * @mixin \Eloquent - * @property boolean $active + * @property boolean $active * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereActive($value) * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Note[] $notes */ diff --git a/app/Repositories/Account/AccountTasker.php b/app/Repositories/Account/AccountTasker.php index 8bba27f601..1711fed6bb 100644 --- a/app/Repositories/Account/AccountTasker.php +++ b/app/Repositories/Account/AccountTasker.php @@ -257,7 +257,6 @@ class AccountTasker implements AccountTaskerInterface $join->on('transaction_journals.id', '=', 'other_side.transaction_journal_id')->where('other_side.amount', $joinModifier, 0); } ) - ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) ->where('transaction_journals.user_id', $this->user->id) @@ -313,7 +312,7 @@ class AccountTasker implements AccountTaskerInterface } ) ->leftJoin('accounts as other_account', 'other_account.id', '=', 'other_side.account_id') - ->where('transaction_types.type','!=', TransactionType::OPENING_BALANCE) + ->where('transaction_types.type', '!=', TransactionType::OPENING_BALANCE) ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) ->where('transaction_journals.user_id', $this->user->id) diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index 4a35819c56..e41196b81d 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -22,7 +22,6 @@ use FireflyIII\Models\TransactionType; use FireflyIII\Support\CacheProperties; use FireflyIII\User; use Illuminate\Database\Query\JoinClause; -use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Log; use Navigation; diff --git a/app/Repositories/Bill/BillRepositoryInterface.php b/app/Repositories/Bill/BillRepositoryInterface.php index 16368e53d1..cf71d133bc 100644 --- a/app/Repositories/Bill/BillRepositoryInterface.php +++ b/app/Repositories/Bill/BillRepositoryInterface.php @@ -16,7 +16,6 @@ namespace FireflyIII\Repositories\Bill; use Carbon\Carbon; use FireflyIII\Models\Bill; use FireflyIII\Models\TransactionJournal; -use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; /** diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 5210d63ec8..b814a8eaf8 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -350,8 +350,8 @@ class BudgetRepository implements BudgetRepositoryInterface public function spentInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end) : string { // collect amount of transaction journals, which is easy: - $budgetIds = $budgets->pluck('id')->toArray(); - $accountIds = $accounts->pluck('id')->toArray(); + $budgetIds = $budgets->pluck('id')->toArray(); + $accountIds = $accounts->pluck('id')->toArray(); Log::debug('spentInPeriod: Now in spentInPeriod for these budgets: ', $budgetIds); Log::debug('spentInPeriod: and these accounts: ', $accountIds); diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 9004fd682a..cbb2927814 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -20,7 +20,6 @@ use FireflyIII\Models\TransactionType; use FireflyIII\User; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Query\JoinClause; -use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; /** diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index b7e2f68e08..d088eebe5d 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -15,7 +15,6 @@ namespace FireflyIII\Repositories\Category; use Carbon\Carbon; use FireflyIII\Models\Category; -use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; /** diff --git a/app/Repositories/Journal/JournalRepositoryInterface.php b/app/Repositories/Journal/JournalRepositoryInterface.php index d1c2825bb5..06e80fb25b 100644 --- a/app/Repositories/Journal/JournalRepositoryInterface.php +++ b/app/Repositories/Journal/JournalRepositoryInterface.php @@ -26,15 +26,6 @@ use Illuminate\Support\MessageBag; interface JournalRepositoryInterface { - /** - * Deletes a journal. - * - * @param TransactionJournal $journal - * - * @return bool - */ - public function delete(TransactionJournal $journal): bool; - /** * @param TransactionJournal $journal * @param TransactionType $type @@ -44,6 +35,15 @@ interface JournalRepositoryInterface */ public function convert(TransactionJournal $journal, TransactionType $type, Account $source, Account $destination): MessageBag; + /** + * Deletes a journal. + * + * @param TransactionJournal $journal + * + * @return bool + */ + public function delete(TransactionJournal $journal): bool; + /** * Find a specific journal * From 05dbd30bbd8471c2a79874afe2fa4ba6402da59e Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 5 Nov 2016 17:17:56 +0100 Subject: [PATCH 047/709] Rename another collector. --- .../{JournalCollector.php => JournalExportCollector.php} | 6 +++--- app/Export/Processor.php | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) rename app/Export/Collector/{JournalCollector.php => JournalExportCollector.php} (98%) diff --git a/app/Export/Collector/JournalCollector.php b/app/Export/Collector/JournalExportCollector.php similarity index 98% rename from app/Export/Collector/JournalCollector.php rename to app/Export/Collector/JournalExportCollector.php index 7ea5e7ae5f..927869718b 100644 --- a/app/Export/Collector/JournalCollector.php +++ b/app/Export/Collector/JournalExportCollector.php @@ -1,6 +1,6 @@ job]); + /** @var JournalExportCollector $collector */ + $collector = app(JournalExportCollector::class, [$this->job]); $collector->setDates($this->settings['startDate'], $this->settings['endDate']); $collector->setAccounts($this->settings['accounts']); $collector->run(); From 37435da45995053b5dc2c920dd1154fa14e5e10d Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 5 Nov 2016 17:47:50 +0100 Subject: [PATCH 048/709] Moved more stuff to the journal collector. --- app/Helpers/Collector/JournalCollector.php | 29 +++++++- app/Http/Controllers/BudgetController.php | 23 +++--- .../Controllers/Popup/ReportController.php | 22 ++++-- app/Repositories/Budget/BudgetRepository.php | 70 ------------------- .../Budget/BudgetRepositoryInterface.php | 10 --- resources/views/budgets/show.twig | 2 +- .../views/popup/report/balance-amount.twig | 2 +- .../popup/report/budget-spent-amount.twig | 2 +- 8 files changed, 59 insertions(+), 101 deletions(-) diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index bc9ca44616..d6790eb096 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -8,6 +8,7 @@ use Carbon\Carbon; use Crypt; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\AccountType; +use FireflyIII\Models\Budget; use FireflyIII\Models\Category; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionType; @@ -70,6 +71,8 @@ class JournalCollector 'transactions.amount', ]; /** @var bool */ + private $joinedBudget = false; + /** @var bool */ private $joinedCategory = false; /** @var int */ private $limit; @@ -193,7 +196,7 @@ class JournalCollector { /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class, [$this->user]); - $accounts = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]); + $accounts = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT, AccountType::CASH]); if ($accounts->count() > 0) { $accountIds = $accounts->pluck('id')->toArray(); $this->query->whereIn('transactions.account_id', $accountIds); @@ -222,6 +225,30 @@ class JournalCollector } + /** + * @param Budget $budget + * + * @return JournalCollector + */ + public function setBudget(Budget $budget): JournalCollector + { + if (!$this->joinedBudget) { + // join some extra tables: + $this->joinedBudget = true; + $this->query->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'); + $this->query->leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'transactions.id'); + } + + $this->query->where( + function (EloquentBuilder $q) use ($budget) { + $q->where('budget_transaction.budget_id', $budget->id); + $q->orWhere('budget_transaction_journal.budget_id', $budget->id); + } + ); + + return $this; + } + /** * @param Category $category * diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index b2ed2af41f..f9b176060a 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -17,6 +17,7 @@ use Amount; use Carbon\Carbon; use Config; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Http\Requests\BudgetFormRequest; use FireflyIII\Models\AccountType; use FireflyIII\Models\Budget; @@ -305,14 +306,13 @@ class BudgetController extends Controller $start = session('first', Carbon::create()->startOfYear()); $end = new Carbon; $page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page')); - $pageSize = Preferences::get('transactionPageSize', 50)->data; - $offset = ($page - 1) * $pageSize; - $journals = $repository->journalsInPeriod(new Collection([$budget]), new Collection, $start, $end); // budget - $count = $journals->count(); - $journals = $journals->slice($offset, $pageSize); - $journals = new LengthAwarePaginator($journals, $count, $pageSize); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]); + // collector: + $collector = new JournalCollector(auth()->user()); + $collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page); + $journals = $collector->getPaginatedJournals(); $journals->setPath('/budgets/show/' . $budget->id); @@ -347,16 +347,15 @@ class BudgetController extends Controller $start = $repetition->startdate; $end = $repetition->enddate; $page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page')); - $pageSize = Preferences::get('transactionPageSize', 50)->data; - $offset = ($page - 1) * $pageSize; - $journals = $repository->journalsInPeriod(new Collection([$budget]), new Collection, $start, $end); // budget - $count = $journals->count(); - $journals = $journals->slice($offset, $pageSize); - $journals = new LengthAwarePaginator($journals, $count, $pageSize); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $subTitle = trans('firefly.budget_in_month', ['name' => $budget->name, 'month' => $repetition->startdate->formatLocalized($this->monthFormat)]); $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]); + // collector: + $collector = new JournalCollector(auth()->user()); + $collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page); + $journals = $collector->getPaginatedJournals(); $journals->setPath('/budgets/show/' . $budget->id . '/' . $repetition->id); diff --git a/app/Http/Controllers/Popup/ReportController.php b/app/Http/Controllers/Popup/ReportController.php index b31e514853..89fd605502 100644 --- a/app/Http/Controllers/Popup/ReportController.php +++ b/app/Http/Controllers/Popup/ReportController.php @@ -101,9 +101,13 @@ class ReportController extends Controller switch (true) { case ($role === BalanceLine::ROLE_DEFAULTROLE && !is_null($budget->id)): - $journals = $budgetRepository->journalsInPeriod( - new Collection([$budget]), new Collection([$account]), $attributes['startDate'], $attributes['endDate'] - ); + $collector = new JournalCollector(auth()->user()); + $collector + ->setAccounts(new Collection([$account])) + ->setRange($attributes['startDate'], $attributes['endDate']) + ->setBudget($budget); + $journals = $collector->getJournals(); + break; case ($role === BalanceLine::ROLE_DEFAULTROLE && is_null($budget->id)): $budget->name = strval(trans('firefly.no_budget')); @@ -150,9 +154,17 @@ class ReportController extends Controller $budget = $repository->find(intval($attributes['budgetId'])); if (is_null($budget->id)) { $journals = $repository->journalsInPeriodWithoutBudget($attributes['accounts'], $attributes['startDate'], $attributes['endDate']); - } else { + } + if (!is_null($budget->id)) { // get all expenses in budget in period: - $journals = $repository->journalsInPeriod(new Collection([$budget]), $attributes['accounts'], $attributes['startDate'], $attributes['endDate']); + $collector = new JournalCollector(auth()->user()); + $collector + ->setAccounts($attributes['accounts']) + ->setRange($attributes['startDate'], $attributes['endDate']) + ->setBudget($budget); + $journals = $collector->getJournals(); + + } $view = view('popup.report.budget-spent-amount', compact('journals', 'budget'))->render(); diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index b814a8eaf8..0e0e281dfd 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -210,76 +210,6 @@ class BudgetRepository implements BudgetRepositoryInterface return $set; } - /** - * @param Collection $budgets - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function journalsInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): Collection - { - $return = new Collection; - $accountIds = []; - // expand the number of grabbed fields: - $fields = TransactionJournal::queryFields(); - $fields[] = 'source.account_id'; - if ($accounts->count() > 0) { - $accountIds = $accounts->pluck('id')->toArray(); - } - - // first get all journals for all budget(s): - $journalQuery = $this->user->transactionJournals() - ->expanded() - ->sortCorrectly() - ->before($end) - ->after($start) - ->leftJoin( - 'transactions as source', - function (JoinClause $join) { - $join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', '0'); - } - ) - ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->whereIn('budget_transaction_journal.budget_id', $budgets->pluck('id')->toArray()); - // add account id's, if relevant: - if (count($accountIds) > 0) { - $journalQuery->whereIn('source.account_id', $accountIds); - } - // get them: - $journals = $journalQuery->get(TransactionJournal::queryFields()); - - // then get transactions themselves. - $transactionQuery = $this->user->transactionJournals() - ->expanded() - ->before($end) - ->sortCorrectly() - ->after($start) - ->leftJoin('transactions as related', 'related.transaction_journal_id', '=', 'transaction_journals.id') - ->leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'related.id') - ->leftJoin( - 'transactions as source', - function (JoinClause $join) { - $join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', '0'); - } - ) - ->groupBy(['source.account_id']) - ->whereIn('budget_transaction.budget_id', $budgets->pluck('id')->toArray()); - - if (count($accountIds) > 0) { - $transactionQuery->whereIn('source.account_id', $accountIds); - } - - $transactions = $transactionQuery->get($fields); - - // return complete set: - $return = $return->merge($transactions); - $return = $return->merge($journals); - - return $return; - } - /** * @param Collection $accounts * @param Carbon $start diff --git a/app/Repositories/Budget/BudgetRepositoryInterface.php b/app/Repositories/Budget/BudgetRepositoryInterface.php index 0f6aba141e..e6c5503c1f 100644 --- a/app/Repositories/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetRepositoryInterface.php @@ -89,16 +89,6 @@ interface BudgetRepositoryInterface */ public function getInactiveBudgets(): Collection; - /** - * @param Collection $budgets - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function journalsInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): Collection; - /** * @param Collection $accounts * @param Carbon $start diff --git a/resources/views/budgets/show.twig b/resources/views/budgets/show.twig index f88ab7ba9b..65ba0465fa 100644 --- a/resources/views/budgets/show.twig +++ b/resources/views/budgets/show.twig @@ -36,7 +36,7 @@

{{ 'transactions'|_ }}

- {% include 'list.journals' with {budgetPerspective: budget} %} + {% include 'list.journals-tasker' %}
diff --git a/resources/views/popup/report/balance-amount.twig b/resources/views/popup/report/balance-amount.twig index 179726fc51..1707235ee7 100644 --- a/resources/views/popup/report/balance-amount.twig +++ b/resources/views/popup/report/balance-amount.twig @@ -10,7 +10,7 @@
- {% include 'list.journals' with {'journals': list} %} + {% include 'list.journals-tasker' with {'journals': journals} %}
From 962cad33e2c6da562cb3c4aeb4607e04be9a6a59 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 5 Nov 2016 18:43:18 +0100 Subject: [PATCH 050/709] Code cleanup. --- app/Export/Exporter/CsvExporter.php | 1 - app/Export/Processor.php | 2 -- app/Helpers/Attachments/AttachmentHelper.php | 11 ++--------- app/Helpers/Collector/JournalCollector.php | 15 ++------------- app/Http/Controllers/AccountController.php | 3 +-- app/Http/Controllers/BudgetController.php | 15 +++++++-------- app/Http/Controllers/CategoryController.php | 6 ++---- app/Http/Controllers/HomeController.php | 2 +- app/Http/Controllers/ProfileController.php | 1 - app/Http/Controllers/Report/BudgetController.php | 2 ++ .../Controllers/Transaction/ConvertController.php | 1 - app/Models/Note.php | 2 -- app/Repositories/Budget/BudgetRepository.php | 1 - .../Journal/JournalRepositoryInterface.php | 3 ++- app/Rules/Actions/ClearCategory.php | 1 + 15 files changed, 20 insertions(+), 46 deletions(-) diff --git a/app/Export/Exporter/CsvExporter.php b/app/Export/Exporter/CsvExporter.php index 0091b55ff1..b1f12f4e30 100644 --- a/app/Export/Exporter/CsvExporter.php +++ b/app/Export/Exporter/CsvExporter.php @@ -14,7 +14,6 @@ declare(strict_types = 1); namespace FireflyIII\Export\Exporter; use FireflyIII\Export\Entry\Entry; -use FireflyIII\Export\Entry\EntryAccount; use FireflyIII\Models\ExportJob; use League\Csv\Writer; use SplFileObject; diff --git a/app/Export/Processor.php b/app/Export/Processor.php index aff27c1dbc..62fbe7a6a0 100644 --- a/app/Export/Processor.php +++ b/app/Export/Processor.php @@ -45,8 +45,6 @@ class Processor public $job; /** @var array */ public $settings; - /** @var \FireflyIII\Export\ConfigurationFile */ - private $configurationMaker; /** @var Collection */ private $exportEntries; /** @var Collection */ diff --git a/app/Helpers/Attachments/AttachmentHelper.php b/app/Helpers/Attachments/AttachmentHelper.php index a7bb7c8573..2c7e2d2b60 100644 --- a/app/Helpers/Attachments/AttachmentHelper.php +++ b/app/Helpers/Attachments/AttachmentHelper.php @@ -17,10 +17,8 @@ use FireflyIII\Models\Attachment; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\MessageBag; use Input; -use Log; use Storage; use Symfony\Component\HttpFoundation\File\UploadedFile; -use TypeError; /** * Class AttachmentHelper @@ -236,13 +234,8 @@ class AttachmentHelper implements AttachmentHelperInterface private function getFiles() { $files = null; - try { - if (Input::hasFile('attachments')) { - $files = Input::file('attachments'); - } - } catch (TypeError $e) { - // Log it, do nothing else. - Log::error($e->getMessage()); + if (Input::hasFile('attachments')) { + $files = Input::file('attachments'); } return $files; diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index 0179a204c0..9b1a513d42 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -57,19 +57,6 @@ class JournalCollector ]; /** @var bool */ private $filterTransfers = false; - /** @var array */ - private $group - = [ - 'transaction_journals.id', - 'transaction_journals.description', - 'firefly-iii.transaction_journals.date', - 'transaction_journals.encrypted', - 'transaction_currencies.code', - 'transaction_types.type', - 'transaction_journals.bill_id', - 'bills.name', - 'transactions.amount', - ]; /** @var bool */ private $joinedBudget = false; /** @var bool */ @@ -285,6 +272,8 @@ class JournalCollector public function setOffset(int $offset): JournalCollector { $this->offset = $offset; + + return $this; } /** diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 57f532f28f..e5ac8095e2 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -275,13 +275,12 @@ class AccountController extends Controller } /** - * @param AccountTaskerInterface $tasker * @param Account $account * @param string $date * * @return View */ - public function showWithDate(AccountTaskerInterface $tasker, Account $account, string $date) + public function showWithDate(Account $account, string $date) { $carbon = new Carbon($date); $range = Preferences::get('viewRange', '1M')->data; diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index b09e56f9c9..d8f18646e4 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -24,7 +24,6 @@ use FireflyIII\Models\Budget; use FireflyIII\Models\LimitRepetition; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; -use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Input; use Log; @@ -255,7 +254,7 @@ class BudgetController extends Controller /** @var Carbon $start */ $start = session('start', Carbon::now()->startOfMonth()); /** @var Carbon $end */ - $end = session('end', Carbon::now()->endOfMonth()); + $end = session('end', Carbon::now()->endOfMonth()); $page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $subTitle = trans( @@ -264,9 +263,9 @@ class BudgetController extends Controller ); // collector - $collector = new JournalCollector(auth()->user()); + $collector = new JournalCollector(auth()->user()); $collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withoutBudget(); - $journals = $collector->getPaginatedJournals(); + $journals = $collector->getPaginatedJournals(); $journals->setPath('/budgets/list/noBudget'); return view('budgets.no-budget', compact('journals', 'subTitle')); @@ -307,9 +306,9 @@ class BudgetController extends Controller $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]); // collector: - $collector = new JournalCollector(auth()->user()); + $collector = new JournalCollector(auth()->user()); $collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page); - $journals = $collector->getPaginatedJournals(); + $journals = $collector->getPaginatedJournals(); $journals->setPath('/budgets/show/' . $budget->id); @@ -350,9 +349,9 @@ class BudgetController extends Controller // collector: - $collector = new JournalCollector(auth()->user()); + $collector = new JournalCollector(auth()->user()); $collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page); - $journals = $collector->getPaginatedJournals(); + $journals = $collector->getPaginatedJournals(); $journals->setPath('/budgets/show/' . $budget->id . '/' . $repetition->id); diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index 39b3bf5b2f..f95a1a968b 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -240,10 +240,8 @@ class CategoryController extends Controller } /** - * @param CRI $repository - * @param Category $category - * - * @param $date + * @param Category $category + * @param $date * * @return View */ diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 40872742b5..8bf0a6c6e6 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -117,7 +117,7 @@ class HomeController extends Controller /** * @param ARI $repository * - * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View */ public function index(ARI $repository) { diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 23c42d3c1e..c10c1701ac 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -13,7 +13,6 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; -use FireflyIII\Events\DeletedUser; use FireflyIII\Http\Requests\DeleteAccountFormRequest; use FireflyIII\Http\Requests\ProfileFormRequest; use FireflyIII\User; diff --git a/app/Http/Controllers/Report/BudgetController.php b/app/Http/Controllers/Report/BudgetController.php index b18f1b8d43..c000f86a24 100644 --- a/app/Http/Controllers/Report/BudgetController.php +++ b/app/Http/Controllers/Report/BudgetController.php @@ -33,6 +33,8 @@ class BudgetController extends Controller * @param Carbon $start * @param Carbon $end * @param Collection $accounts + * + * @return mixed|string */ public function budgetMultiYear(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts) { diff --git a/app/Http/Controllers/Transaction/ConvertController.php b/app/Http/Controllers/Transaction/ConvertController.php index 851fbfe2c5..f00fb809ad 100644 --- a/app/Http/Controllers/Transaction/ConvertController.php +++ b/app/Http/Controllers/Transaction/ConvertController.php @@ -206,7 +206,6 @@ class ConvertController extends Controller $sourceAccount = TransactionJournal::sourceAccountList($journal)->first(); $destinationAccount = TransactionJournal::destinationAccountList($journal)->first(); $sourceType = $journal->transactionType; - $source = new Account; $joined = $sourceType->type . '-' . $destinationType->type; switch ($joined) { default: diff --git a/app/Models/Note.php b/app/Models/Note.php index 17444ba3e7..c26a397421 100644 --- a/app/Models/Note.php +++ b/app/Models/Note.php @@ -46,8 +46,6 @@ class Note extends Model /** - * @param $value - * * @return string */ public function getMarkdownAttribute(): string diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index b6fecfdca4..6726287704 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -24,7 +24,6 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\User; use Illuminate\Database\Eloquent\Builder; -use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; use Log; diff --git a/app/Repositories/Journal/JournalRepositoryInterface.php b/app/Repositories/Journal/JournalRepositoryInterface.php index 06e80fb25b..8a25f542b9 100644 --- a/app/Repositories/Journal/JournalRepositoryInterface.php +++ b/app/Repositories/Journal/JournalRepositoryInterface.php @@ -29,7 +29,8 @@ interface JournalRepositoryInterface /** * @param TransactionJournal $journal * @param TransactionType $type - * @param array $data + * @param Account $source + * @param Account $destination * * @return MessageBag */ diff --git a/app/Rules/Actions/ClearCategory.php b/app/Rules/Actions/ClearCategory.php index 5af30a1811..d60afcf77a 100644 --- a/app/Rules/Actions/ClearCategory.php +++ b/app/Rules/Actions/ClearCategory.php @@ -16,6 +16,7 @@ namespace FireflyIII\Rules\Actions; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; +use Log; /** * Class ClearCategory From b24e97a4492b472a8241395f7ed75d98b7988805 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 5 Nov 2016 18:50:13 +0100 Subject: [PATCH 051/709] Update version and change log. --- CHANGELOG.md | 6 +++++- config/firefly.php | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83e339a29b..95d0a81eb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,13 +6,17 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added - New budget table for multi year report. + + ### Changed - Greatly expanded help pages and their function. +- Built a new transaction collector, which I think was the idea of @roberthorlings originally. + ### Fixed - #375, thanks to @schoentoon which made it impossible to resurrect currencies. - #370 thanks to @ksmolder - +- #378, thanks to @HomelessAvatar ## [4.1.5] - 2016-11-01 ### Changed diff --git a/config/firefly.php b/config/firefly.php index 97b843110a..40f65d62d5 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -22,7 +22,7 @@ return [ 'single_user_mode' => true, ], 'chart' => 'chartjs', - 'version' => '4.1.5', + 'version' => '4.1.6', 'csv_import_enabled' => true, 'maxUploadSize' => 5242880, 'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'], From bd55636b3f4aa6daa8336960f2f2ebb7f35509b9 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 5 Nov 2016 18:51:26 +0100 Subject: [PATCH 052/709] Add repository move info. --- composer.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/composer.json b/composer.json index b443593700..feb695bcff 100755 --- a/composer.json +++ b/composer.json @@ -71,6 +71,7 @@ ], "post-install-cmd": [ "Illuminate\\Foundation\\ComposerScripts::postInstall", + "php artisan firefly:github-move", "php artisan optimize" ], "post-update-cmd": [ @@ -78,6 +79,7 @@ "php artisan firefly:upgrade-instructions", "php artisan firefly:upgrade-database", "php artisan firefly:verify", + "php artisan firefly:github-move", "php artisan optimize" ] }, From ee6e047596430318ccb129f442c1c2a0cb9d15bc Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 5 Nov 2016 18:55:09 +0100 Subject: [PATCH 053/709] Do not order the count query. --- app/Helpers/Collector/JournalCollector.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index 9b1a513d42..896b7f36a7 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -102,6 +102,7 @@ class JournalCollector $countQuery->getQuery()->offset = null; $countQuery->getQuery()->unionLimit = null; $countQuery->getQuery()->groups = null; + $countQuery->getQuery()->orders = null; $countQuery->groupBy('accounts.user_id'); $this->count = $countQuery->count(); From 0edffd8ea1cb09066aa6f682a2401d1209d80d0f Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 5 Nov 2016 18:57:45 +0100 Subject: [PATCH 054/709] Lighter icon [skip ci] --- app/Support/Twig/Transaction.php | 2 +- resources/views/list/journals.twig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Support/Twig/Transaction.php b/app/Support/Twig/Transaction.php index 696464932c..65e76090ac 100644 --- a/app/Support/Twig/Transaction.php +++ b/app/Support/Twig/Transaction.php @@ -144,7 +144,7 @@ class Transaction extends Twig_Extension 'splitJournalIndicator', function (int $journalId) { $count = TransactionModel::where('transaction_journal_id', $journalId)->whereNull('deleted_at')->count(); if ($count > 2) { - return ''; + return ''; } return ''; diff --git a/resources/views/list/journals.twig b/resources/views/list/journals.twig index 86fa131105..45d5005fb8 100644 --- a/resources/views/list/journals.twig +++ b/resources/views/list/journals.twig @@ -59,7 +59,7 @@ title="{{ Lang.choice('firefly.nr_of_attachments', journal.attachments|length, {count: journal.attachments|length}) }}"> {% endif %} {% if journal.count_transactions > 2 %} - + {% endif %} From 77e2cf40df61f2b19923c1e03fd18b36a09a9f43 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 6 Nov 2016 08:08:06 +0100 Subject: [PATCH 055/709] Removed more getJournals functions in favour of the collector. --- app/Helpers/Collector/JournalCollector.php | 92 ++++++++++++++++--- app/Http/Controllers/AccountController.php | 9 +- app/Http/Controllers/CategoryController.php | 4 +- app/Http/Controllers/RuleController.php | 2 +- app/Http/Controllers/TagController.php | 25 +++-- .../Controllers/TransactionController.php | 2 +- ...ExecuteRuleGroupOnExistingTransactions.php | 14 +-- app/Repositories/Journal/JournalTasker.php | 67 -------------- .../Journal/JournalTaskerInterface.php | 21 ----- app/Repositories/Tag/TagRepository.php | 18 ---- .../Tag/TagRepositoryInterface.php | 7 -- app/Rules/Processor.php | 31 +++++++ app/Rules/TransactionMatcher.php | 30 ++++-- resources/views/tags/show.twig | 2 +- 14 files changed, 163 insertions(+), 161 deletions(-) diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index 896b7f36a7..a1f5c1e854 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -10,6 +10,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\AccountType; use FireflyIII\Models\Budget; use FireflyIII\Models\Category; +use FireflyIII\Models\Tag; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; @@ -61,6 +62,8 @@ class JournalCollector private $joinedBudget = false; /** @var bool */ private $joinedCategory = false; + /** @var bool */ + private $joinedTag = false; /** @var int */ private $limit; /** @var int */ @@ -116,19 +119,7 @@ class JournalCollector { $this->run = true; $set = $this->query->get(array_values($this->fields)); - - // filter out transfers: - if ($this->filterTransfers) { - $set = $set->filter( - function (Transaction $transaction) { - if (!($transaction->transaction_type_type === TransactionType::TRANSFER && bccomp($transaction->transaction_amount, '0') === -1)) { - return $transaction; - } - - return false; - } - ); - } + $set = $this->filterTransfers($set); // loop for decryption. $set->each( @@ -184,7 +175,7 @@ class JournalCollector { /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class, [$this->user]); - $accounts = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT, AccountType::CASH]); + $accounts = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]); if ($accounts->count() > 0) { $accountIds = $accounts->pluck('id')->toArray(); $this->query->whereIn('transactions.account_id', $accountIds); @@ -320,6 +311,19 @@ class JournalCollector return $this; } + /** + * @param Tag $tag + * + * @return JournalCollector + */ + public function setTag(Tag $tag): JournalCollector + { + $this->joinTagTables(); + $this->query->where('tag_transaction_journal.tag_id', $tag->id); + + return $this; + } + /** * @param array $types * @@ -368,6 +372,54 @@ class JournalCollector return $this; } + /** + * If the set of accounts used by the collector includes more than one asset + * account, chances are the set include double entries: transfers get selected + * on both the source, and then again on the destination account. + * + * This method filters them out. + * + * @param Collection $set + * + * @return Collection + */ + private function filterTransfers(Collection $set): Collection + { + if ($this->filterTransfers) { + $set = $set->filter( + function (Transaction $transaction) { + if (!($transaction->transaction_type_type === TransactionType::TRANSFER && bccomp($transaction->transaction_amount, '0') === -1)) { + Log::debug( + sprintf( + 'Included journal #%d (transaction #%d) because its a %s with amount %f', + $transaction->transaction_journal_id, + $transaction->id, + $transaction->transaction_type_type, + $transaction->transaction_amount + ) + ); + + return $transaction; + } + + Log::debug( + sprintf( + 'Removed journal #%d (transaction #%d) because its a %s with amount %f', + $transaction->transaction_journal_id, + $transaction->id, + $transaction->transaction_type_type, + $transaction->transaction_amount + ) + ); + + return false; + } + ); + } + + return $set; + } + /** * */ @@ -394,6 +446,18 @@ class JournalCollector } } + /** + * + */ + private function joinTagTables() + { + if (!$this->joinedTag) { + // join some extra tables: + $this->joinedTag = true; + $this->query->leftJoin('tag_transaction_journal', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'); + } + } + /** * @return EloquentBuilder */ diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index e5ac8095e2..750e90d53a 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -221,7 +221,7 @@ class AccountController extends Controller $start = session('start', Navigation::startOfPeriod(new Carbon, $range)); /** @var Carbon $end */ $end = session('end', Navigation::endOfPeriod(new Carbon, $range)); - $page = intval(Input::get('page')); + $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); // replace with journal collector: @@ -275,8 +275,8 @@ class AccountController extends Controller } /** - * @param Account $account - * @param string $date + * @param Account $account + * @param string $date * * @return View */ @@ -287,8 +287,7 @@ class AccountController extends Controller $start = Navigation::startOfPeriod($carbon, $range); $end = Navigation::endOfPeriod($carbon, $range); $subTitle = $account->name . ' (' . Navigation::periodShow($start, $range) . ')'; - $page = intval(Input::get('page')); - $page = $page === 0 ? 1 : $page; + $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); // replace with journal collector: diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index f95a1a968b..7068130116 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -184,7 +184,7 @@ class CategoryController extends Controller $end = session('end', Navigation::endOfPeriod(new Carbon, $range)); $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); $hideCategory = true; // used in list. - $page = intval(Input::get('page')); + $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $subTitle = $category->name; $subTitleIcon = 'fa-bar-chart'; @@ -253,7 +253,7 @@ class CategoryController extends Controller $end = Navigation::endOfPeriod($carbon, $range); $subTitle = $category->name; $hideCategory = true; // used in list. - $page = intval(Input::get('page')); + $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); // new collector: diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 3f55f44e35..98443574d2 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -306,7 +306,7 @@ class RuleController extends Controller } // Return json response - $view = view('list.journals-tiny', ['transactions' => $matchingTransactions])->render(); + $view = view('list.journals-tiny-tasker', ['transactions' => $matchingTransactions])->render(); return Response::json(['html' => $view, 'warning' => $warning]); } diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index fc7013d41a..7a790c284a 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -13,10 +13,11 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Http\Requests\TagFormRequest; use FireflyIII\Models\Preference; use FireflyIII\Models\Tag; -use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\Transaction; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use Illuminate\Support\Collection; use Input; @@ -226,19 +227,27 @@ class TagController extends Controller } /** - * @param Tag $tag - * @param TagRepositoryInterface $repository + * @param Tag $tag * * @return View */ - public function show(Tag $tag, TagRepositoryInterface $repository) + public function show(Tag $tag) { $subTitle = $tag->tag; $subTitleIcon = 'fa-tag'; - $journals = $repository->getJournals($tag); - $sum = $journals->sum( - function (TransactionJournal $journal) { - return TransactionJournal::amount($journal); + $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); + + // use collector: + // replace with journal collector: + $collector = new JournalCollector(auth()->user()); + $collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page)->setTag($tag); + $journals = $collector->getPaginatedJournals(); + $journals->setPath('tags/show/' . $tag->id); + + $sum = $journals->sum( + function (Transaction $transaction) { + return $transaction->transaction_amount; } ); diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 517ceedd7b..f1f65eebba 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -61,7 +61,7 @@ class TransactionController extends Controller $subTitleIcon = config('firefly.transactionIconsByWhat.' . $what); $types = config('firefly.transactionTypesByWhat.' . $what); $subTitle = trans('firefly.title_' . $what); - $page = intval($request->get('page')); + $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); $collector = new JournalCollector(auth()->user()); $collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts(); diff --git a/app/Jobs/ExecuteRuleGroupOnExistingTransactions.php b/app/Jobs/ExecuteRuleGroupOnExistingTransactions.php index 8048fe9cd7..fec910a6b3 100644 --- a/app/Jobs/ExecuteRuleGroupOnExistingTransactions.php +++ b/app/Jobs/ExecuteRuleGroupOnExistingTransactions.php @@ -14,8 +14,8 @@ declare(strict_types = 1); namespace FireflyIII\Jobs; use Carbon\Carbon; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Models\RuleGroup; -use FireflyIII\Repositories\Journal\JournalTaskerInterface; use FireflyIII\Rules\Processor; use FireflyIII\User; use Illuminate\Contracts\Queue\ShouldQueue; @@ -129,16 +129,16 @@ class ExecuteRuleGroupOnExistingTransactions extends Job implements ShouldQueue public function handle() { // Lookup all journals that match the parameters specified - $journals = $this->collectJournals(); + $transactions = $this->collectJournals(); // Find processors for each rule within the current rule group $processors = $this->collectProcessors(); // Execute the rules for each transaction - foreach ($journals as $journal) { + foreach ($transactions as $transaction) { /** @var Processor $processor */ foreach ($processors as $processor) { - $processor->handleTransactionJournal($journal); + $processor->handleTransaction($transaction); // Stop processing this group if the rule specifies 'stop_processing' if ($processor->getRule()->stop_processing) { @@ -155,10 +155,10 @@ class ExecuteRuleGroupOnExistingTransactions extends Job implements ShouldQueue */ protected function collectJournals() { - /** @var JournalTaskerInterface $tasker */ - $tasker = app(JournalTaskerInterface::class); + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($this->accounts)->setRange($this->startDate, $this->endDate); - return $tasker->getJournalsInRange($this->accounts, $this->startDate, $this->endDate); + return $collector->getJournals(); } /** diff --git a/app/Repositories/Journal/JournalTasker.php b/app/Repositories/Journal/JournalTasker.php index 26e3c63274..862bafadb8 100644 --- a/app/Repositories/Journal/JournalTasker.php +++ b/app/Repositories/Journal/JournalTasker.php @@ -46,73 +46,6 @@ class JournalTasker implements JournalTaskerInterface $this->user = $user; } - /** - * Returns a page of a specific type(s) of journal. - * - * @param array $types - * @param int $page - * @param int $pageSize - * - * @return LengthAwarePaginator - */ - public function getJournals(array $types, int $page, int $pageSize = 50): LengthAwarePaginator - { - $offset = ($page - 1) * $pageSize; - $query = $this->user->transactionJournals()->expanded()->sortCorrectly(); - $query->where('transaction_journals.completed', 1); - if (count($types) > 0) { - $query->transactionTypes($types); - } - $count = $this->user->transactionJournals()->transactionTypes($types)->count(); - $set = $query->take($pageSize)->offset($offset)->get(TransactionJournal::queryFields()); - $journals = new LengthAwarePaginator($set, $count, $pageSize, $page); - - return $journals; - } - - /** - * Returns a collection of ALL journals, given a specific account and a date range. - * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function getJournalsInRange(Collection $accounts, Carbon $start, Carbon $end): Collection - { - $query = $this->user->transactionJournals()->expanded()->sortCorrectly(); - $query->where('transaction_journals.completed', 1); - $query->before($end); - $query->after($start); - - if ($accounts->count() > 0) { - $ids = $accounts->pluck('id')->toArray(); - // join source and destination: - $query->leftJoin( - 'transactions as source', function (JoinClause $join) { - $join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', 0); - } - ); - $query->leftJoin( - 'transactions as destination', function (JoinClause $join) { - $join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')->where('destination.amount', '>', 0); - } - ); - - $query->where( - function (Builder $q) use ($ids) { - $q->whereIn('destination.account_id', $ids); - $q->orWhereIn('source.account_id', $ids); - } - ); - } - - $set = $query->get(TransactionJournal::queryFields()); - - return $set; - } - /** * @param TransactionJournal $journal * diff --git a/app/Repositories/Journal/JournalTaskerInterface.php b/app/Repositories/Journal/JournalTaskerInterface.php index 89ae1c8289..09ad5813a7 100644 --- a/app/Repositories/Journal/JournalTaskerInterface.php +++ b/app/Repositories/Journal/JournalTaskerInterface.php @@ -26,27 +26,6 @@ use Illuminate\Support\Collection; */ interface JournalTaskerInterface { - /** - * Returns a page of a specific type(s) of journal. - * - * @param array $types - * @param int $page - * @param int $pageSize - * - * @return LengthAwarePaginator - */ - public function getJournals(array $types, int $page, int $pageSize = 50): LengthAwarePaginator; - - /** - * Returns a collection of ALL journals, given a specific account and a date range. - * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function getJournalsInRange(Collection $accounts, Carbon $start, Carbon $end): Collection; /** * @param TransactionJournal $journal diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index 6c29921d88..c71d031d72 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -133,24 +133,6 @@ class TagRepository implements TagRepositoryInterface return $tags; } - /** - * @param Tag $tag - * - * @return Collection - */ - public function getJournals(Tag $tag) : Collection - { - /** @var Collection $journals */ - $journals = $tag - ->transactionJournals() - ->sortCorrectly() - ->expanded() - ->groupBy(['tag_transaction_journal.tag_id', 'tag_transaction_journal.transaction_journal_id']) - ->get(TransactionJournal::queryFields()); - - return $journals; - } - /** * @param array $data * diff --git a/app/Repositories/Tag/TagRepositoryInterface.php b/app/Repositories/Tag/TagRepositoryInterface.php index b1676b2f03..dd1966bd51 100644 --- a/app/Repositories/Tag/TagRepositoryInterface.php +++ b/app/Repositories/Tag/TagRepositoryInterface.php @@ -65,13 +65,6 @@ interface TagRepositoryInterface */ public function get(): Collection; - /** - * @param Tag $tag - * - * @return Collection - */ - public function getJournals(Tag $tag) : Collection; - /** * This method stores a tag. * diff --git a/app/Rules/Processor.php b/app/Rules/Processor.php index d7192cddf9..9b0daf0ae9 100644 --- a/app/Rules/Processor.php +++ b/app/Rules/Processor.php @@ -16,6 +16,7 @@ namespace FireflyIII\Rules; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleTrigger; +use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Rules\Actions\ActionInterface; use FireflyIII\Rules\Factory\ActionFactory; @@ -144,6 +145,36 @@ final class Processor return $this->rule; } + /** + * This method will scan the given transaction journal and check if it matches the triggers found in the Processor + * If so, it will also attempt to run the given actions on the journal. It returns a bool indicating if the transaction journal + * matches all of the triggers (regardless of whether the Processor could act on it). + * + * @param Transaction $transaction + * + * @return bool + */ + public function handleTransaction(Transaction $transaction): bool + { + Log::debug(sprintf('handleTransactionJournal for journal #%d (transaction #%d)', $transaction->transaction_journal_id, $transaction->id)); + + // grab the actual journal. + $journal = $transaction->transactionJournal()->first(); + $this->journal = $journal; + // get all triggers: + $triggered = $this->triggered(); + if ($triggered) { + if ($this->actions->count() > 0) { + $this->actions(); + } + + return true; + } + + return false; + + } + /** * This method will scan the given transaction journal and check if it matches the triggers found in the Processor * If so, it will also attempt to run the given actions on the journal. It returns a bool indicating if the transaction journal diff --git a/app/Rules/TransactionMatcher.php b/app/Rules/TransactionMatcher.php index 7491bbbb88..eb845dee29 100644 --- a/app/Rules/TransactionMatcher.php +++ b/app/Rules/TransactionMatcher.php @@ -13,7 +13,8 @@ declare(strict_types = 1); namespace FireflyIII\Rules; -use FireflyIII\Models\TransactionJournal; +use FireflyIII\Helpers\Collector\JournalCollector; +use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Journal\JournalTaskerInterface; use Illuminate\Support\Collection; @@ -62,7 +63,7 @@ class TransactionMatcher if (count($this->triggers) === 0) { return new Collection; } - $pagesize = min($this->range / 2, $this->limit * 2); + $pageSize = min($this->range / 2, $this->limit * 2); // Variables used within the loop $processed = 0; @@ -76,31 +77,42 @@ class TransactionMatcher // - the maximum number of transactions to search in have been searched do { // Fetch a batch of transactions from the database - $paginator = $this->tasker->getJournals($this->transactionTypes, $page, $pagesize); - $set = $paginator->getCollection(); - + $collector = new JournalCollector(auth()->user()); + $collector->setAllAssetAccounts()->setLimit($pageSize * 2)->setPage($page)->setTypes($this->transactionTypes); + $set = $collector->getPaginatedJournals(); + Log::debug(sprintf('Found %d journals to check. ', $set->count())); // Filter transactions that match the given triggers. $filtered = $set->filter( - function (TransactionJournal $journal) use ($processor) { - Log::debug(sprintf('Test these triggers on #%d', $journal->id)); + function (Transaction $transaction) use ($processor) { + Log::debug(sprintf('Test these triggers on journal #%d (transaction #%d)', $transaction->transaction_journal_id, $transaction->id)); - return $processor->handleTransactionJournal($journal); + return $processor->handleTransaction($transaction); } ); + Log::debug(sprintf('Found %d journals that match.', $filtered->count())); + // merge: /** @var Collection $result */ $result = $result->merge($filtered); + Log::debug(sprintf('Total count is now %d', $result->count())); // Update counters $page++; $processed += count($set); + Log::debug(sprintf('Page is now %d, processed is %d', $page, $processed)); + // Check for conditions to finish the loop - $reachedEndOfList = $set->count() < $pagesize; + $reachedEndOfList = $set->count() < $pageSize; $foundEnough = $result->count() >= $this->limit; $searchedEnough = ($processed >= $this->range); + + Log::debug(sprintf('reachedEndOfList: %s', var_export($reachedEndOfList, true))); + Log::debug(sprintf('foundEnough: %s', var_export($foundEnough, true))); + Log::debug(sprintf('searchedEnough: %s', var_export($searchedEnough, true))); + } while (!$reachedEndOfList && !$foundEnough && !$searchedEnough); // If the list of matchingTransactions is larger than the maximum number of results diff --git a/resources/views/tags/show.twig b/resources/views/tags/show.twig index 47c7f5c54b..fbdc4432d4 100644 --- a/resources/views/tags/show.twig +++ b/resources/views/tags/show.twig @@ -95,7 +95,7 @@
- {% include 'list/journals.twig' %} + {% include 'list/journals-tasker' %}
From 1ebb59b35252e3877870cf44ae57d146617de40f Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 6 Nov 2016 08:11:43 +0100 Subject: [PATCH 056/709] Remove .twig extension. [skip ci] --- resources/views/accounts/create.twig | 2 +- resources/views/accounts/delete.twig | 2 +- resources/views/accounts/edit.twig | 2 +- resources/views/accounts/index.twig | 4 ++-- resources/views/accounts/show.twig | 2 +- resources/views/accounts/show_with_date.twig | 2 +- resources/views/admin/configuration/index.twig | 2 +- resources/views/admin/domains/index.twig | 2 +- resources/views/admin/index.twig | 2 +- resources/views/admin/users/index.twig | 2 +- resources/views/admin/users/show.twig | 2 +- resources/views/attachments/delete.twig | 2 +- resources/views/attachments/edit.twig | 2 +- resources/views/auth/confirmation/error.twig | 2 +- resources/views/auth/confirmation/no-resent.twig | 2 +- resources/views/auth/confirmation/resent.twig | 2 +- resources/views/auth/login.twig | 2 +- resources/views/auth/lost-two-factor.twig | 2 +- resources/views/auth/passwords/email.twig | 2 +- resources/views/auth/passwords/reset.twig | 2 +- resources/views/auth/register.twig | 2 +- resources/views/auth/two-factor.twig | 2 +- resources/views/bills/create.twig | 2 +- resources/views/bills/delete.twig | 2 +- resources/views/bills/edit.twig | 2 +- resources/views/bills/index.twig | 4 ++-- resources/views/bills/show.twig | 2 +- resources/views/budgets/create.twig | 2 +- resources/views/budgets/delete.twig | 2 +- resources/views/budgets/edit.twig | 2 +- resources/views/budgets/index.twig | 2 +- resources/views/budgets/no-budget.twig | 2 +- resources/views/budgets/show.twig | 2 +- resources/views/categories/create.twig | 2 +- resources/views/categories/delete.twig | 2 +- resources/views/categories/edit.twig | 2 +- resources/views/categories/index.twig | 4 ++-- resources/views/categories/no-category.twig | 2 +- resources/views/categories/show.twig | 2 +- resources/views/categories/show_with_date.twig | 2 +- resources/views/csv/column-roles.twig | 2 +- resources/views/csv/download-config.twig | 2 +- resources/views/csv/index.twig | 2 +- resources/views/csv/map.twig | 2 +- resources/views/csv/process.twig | 2 +- resources/views/currency/create.twig | 2 +- resources/views/currency/delete.twig | 2 +- resources/views/currency/edit.twig | 2 +- resources/views/currency/index.twig | 2 +- resources/views/error.twig | 2 +- resources/views/errors/FireflyException.twig | 2 +- resources/views/export/index.twig | 2 +- resources/views/form/amount.twig | 2 +- resources/views/form/balance.twig | 2 +- resources/views/form/checkbox.twig | 4 ++-- resources/views/form/date.twig | 4 ++-- resources/views/form/file.twig | 4 ++-- resources/views/form/integer.twig | 2 +- resources/views/form/location.twig | 2 +- resources/views/form/multiCheckbox.twig | 4 ++-- resources/views/form/multiRadio.twig | 4 ++-- resources/views/form/select.twig | 4 ++-- resources/views/form/tags.twig | 2 +- resources/views/form/text.twig | 4 ++-- resources/views/form/textarea.twig | 4 ++-- resources/views/import/complete.twig | 2 +- resources/views/import/csv/configure.twig | 2 +- resources/views/import/csv/map.twig | 2 +- resources/views/import/csv/roles.twig | 2 +- resources/views/import/finished.twig | 2 +- resources/views/import/index.twig | 2 +- resources/views/import/status.twig | 2 +- resources/views/index.twig | 4 ++-- resources/views/layout/default.twig | 10 +++++----- resources/views/layout/empty.twig | 2 +- resources/views/layout/guest.twig | 2 +- resources/views/new-user/index.twig | 2 +- resources/views/piggy-banks/add-mobile.twig | 2 +- resources/views/piggy-banks/create.twig | 2 +- resources/views/piggy-banks/delete.twig | 2 +- resources/views/piggy-banks/edit.twig | 2 +- resources/views/piggy-banks/index.twig | 4 ++-- resources/views/piggy-banks/remove-mobile.twig | 2 +- resources/views/piggy-banks/show.twig | 2 +- resources/views/popup/report/balance-amount.twig | 2 +- resources/views/popup/report/category-entry.twig | 2 +- resources/views/popup/report/expense-entry.twig | 2 +- resources/views/popup/report/income-entry.twig | 2 +- resources/views/preferences/code.twig | 2 +- resources/views/preferences/index.twig | 2 +- resources/views/profile/change-password.twig | 2 +- resources/views/profile/delete-account.twig | 2 +- resources/views/profile/index.twig | 2 +- resources/views/reminders/index.twig | 2 +- resources/views/reminders/show.twig | 2 +- resources/views/reports/audit/report.twig | 4 ++-- resources/views/reports/default/month.twig | 6 +++--- resources/views/reports/default/multi-year.twig | 2 +- resources/views/reports/default/year.twig | 4 ++-- resources/views/reports/index.twig | 2 +- resources/views/rules/index.twig | 2 +- resources/views/rules/rule-group/create.twig | 2 +- resources/views/rules/rule-group/delete.twig | 2 +- resources/views/rules/rule-group/edit.twig | 2 +- .../views/rules/rule-group/select-transactions.twig | 2 +- resources/views/rules/rule/create.twig | 4 ++-- resources/views/rules/rule/delete.twig | 2 +- resources/views/rules/rule/edit.twig | 4 ++-- resources/views/search/index.twig | 2 +- resources/views/tags/create.twig | 2 +- resources/views/tags/delete.twig | 2 +- resources/views/tags/edit.twig | 2 +- resources/views/tags/index.twig | 2 +- resources/views/tags/show.twig | 2 +- resources/views/transactions/convert.twig | 2 +- resources/views/transactions/create.twig | 2 +- resources/views/transactions/delete.twig | 2 +- resources/views/transactions/edit-split.twig | 2 +- resources/views/transactions/edit.twig | 2 +- resources/views/transactions/index.twig | 2 +- resources/views/transactions/mass-delete.twig | 2 +- resources/views/transactions/mass-edit.twig | 2 +- resources/views/transactions/show.twig | 2 +- 123 files changed, 146 insertions(+), 146 deletions(-) diff --git a/resources/views/accounts/create.twig b/resources/views/accounts/create.twig index cc48551a42..b068c909c4 100644 --- a/resources/views/accounts/create.twig +++ b/resources/views/accounts/create.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, what) }} {% endblock %} diff --git a/resources/views/accounts/delete.twig b/resources/views/accounts/delete.twig index ac4771189c..3fe4850f90 100644 --- a/resources/views/accounts/delete.twig +++ b/resources/views/accounts/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute().getName(), account) }} diff --git a/resources/views/accounts/edit.twig b/resources/views/accounts/edit.twig index 5e31575226..4ccd0f3a22 100644 --- a/resources/views/accounts/edit.twig +++ b/resources/views/accounts/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute().getName(), account) }} diff --git a/resources/views/accounts/index.twig b/resources/views/accounts/index.twig index af0a8d246d..f5b6bda9e6 100644 --- a/resources/views/accounts/index.twig +++ b/resources/views/accounts/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, what) }} @@ -26,7 +26,7 @@
- {% include 'list/accounts.twig' %} + {% include 'list/accounts' %}
diff --git a/resources/views/accounts/show.twig b/resources/views/accounts/show.twig index 61d41da0c5..ac18d8a0cd 100644 --- a/resources/views/accounts/show.twig +++ b/resources/views/accounts/show.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, account) }} diff --git a/resources/views/accounts/show_with_date.twig b/resources/views/accounts/show_with_date.twig index 89ae367972..957e3b5e98 100644 --- a/resources/views/accounts/show_with_date.twig +++ b/resources/views/accounts/show_with_date.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} diff --git a/resources/views/admin/configuration/index.twig b/resources/views/admin/configuration/index.twig index 412fb2459b..73e183c8b2 100644 --- a/resources/views/admin/configuration/index.twig +++ b/resources/views/admin/configuration/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists }} diff --git a/resources/views/admin/domains/index.twig b/resources/views/admin/domains/index.twig index ad854f3c52..5245f5901c 100644 --- a/resources/views/admin/domains/index.twig +++ b/resources/views/admin/domains/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists }} diff --git a/resources/views/admin/index.twig b/resources/views/admin/index.twig index 0c7d5f3dfb..8ca383246f 100644 --- a/resources/views/admin/index.twig +++ b/resources/views/admin/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists }} diff --git a/resources/views/admin/users/index.twig b/resources/views/admin/users/index.twig index 60708d175c..602c4dba48 100644 --- a/resources/views/admin/users/index.twig +++ b/resources/views/admin/users/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists }} diff --git a/resources/views/admin/users/show.twig b/resources/views/admin/users/show.twig index e008708de1..38794f32c2 100644 --- a/resources/views/admin/users/show.twig +++ b/resources/views/admin/users/show.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, user) }} diff --git a/resources/views/attachments/delete.twig b/resources/views/attachments/delete.twig index a6191d5c6e..3e854f8bde 100644 --- a/resources/views/attachments/delete.twig +++ b/resources/views/attachments/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute().getName(), attachment) }} diff --git a/resources/views/attachments/edit.twig b/resources/views/attachments/edit.twig index 976e3b2981..199b39b64f 100644 --- a/resources/views/attachments/edit.twig +++ b/resources/views/attachments/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute().getName(), attachment) }} diff --git a/resources/views/auth/confirmation/error.twig b/resources/views/auth/confirmation/error.twig index d95de0b214..3a5888d4d3 100644 --- a/resources/views/auth/confirmation/error.twig +++ b/resources/views/auth/confirmation/error.twig @@ -13,7 +13,7 @@ - {% include('partials/favicons.twig') %} + {% include('partials/favicons') %} diff --git a/resources/views/auth/confirmation/no-resent.twig b/resources/views/auth/confirmation/no-resent.twig index 01e079cbad..7d23f394ae 100644 --- a/resources/views/auth/confirmation/no-resent.twig +++ b/resources/views/auth/confirmation/no-resent.twig @@ -13,7 +13,7 @@ - {% include('partials/favicons.twig') %} + {% include('partials/favicons') %} diff --git a/resources/views/auth/confirmation/resent.twig b/resources/views/auth/confirmation/resent.twig index c7e0307d3d..da8f9272e4 100644 --- a/resources/views/auth/confirmation/resent.twig +++ b/resources/views/auth/confirmation/resent.twig @@ -13,7 +13,7 @@ - {% include('partials/favicons.twig') %} + {% include('partials/favicons') %} diff --git a/resources/views/auth/login.twig b/resources/views/auth/login.twig index 435870685a..1411157599 100644 --- a/resources/views/auth/login.twig +++ b/resources/views/auth/login.twig @@ -1,4 +1,4 @@ -{% extends "./layout/guest.twig" %} +{% extends "./layout/guest" %} {% block content %} diff --git a/resources/views/auth/lost-two-factor.twig b/resources/views/auth/lost-two-factor.twig index 7f64e57379..fa71e19f8b 100644 --- a/resources/views/auth/lost-two-factor.twig +++ b/resources/views/auth/lost-two-factor.twig @@ -13,7 +13,7 @@ - {% include('partials/favicons.twig') %} + {% include('partials/favicons') %} diff --git a/resources/views/auth/passwords/email.twig b/resources/views/auth/passwords/email.twig index 3f9a286116..475f9bb7e6 100644 --- a/resources/views/auth/passwords/email.twig +++ b/resources/views/auth/passwords/email.twig @@ -1,4 +1,4 @@ -{% extends "./layout/guest.twig" %} +{% extends "./layout/guest" %} {% block content %} diff --git a/resources/views/auth/passwords/reset.twig b/resources/views/auth/passwords/reset.twig index c70b06e8a8..bbe9a732d8 100644 --- a/resources/views/auth/passwords/reset.twig +++ b/resources/views/auth/passwords/reset.twig @@ -1,4 +1,4 @@ -{% extends "./layout/guest.twig" %} +{% extends "./layout/guest" %} {% block content %} diff --git a/resources/views/auth/register.twig b/resources/views/auth/register.twig index fc3f7bf51b..532dc26d91 100644 --- a/resources/views/auth/register.twig +++ b/resources/views/auth/register.twig @@ -1,4 +1,4 @@ -{% extends "./layout/guest.twig" %} +{% extends "./layout/guest" %} {% block content %} diff --git a/resources/views/auth/two-factor.twig b/resources/views/auth/two-factor.twig index e7148d811e..65a29bed85 100644 --- a/resources/views/auth/two-factor.twig +++ b/resources/views/auth/two-factor.twig @@ -1,4 +1,4 @@ -{% extends "./layout/guest.twig" %} +{% extends "./layout/guest" %} {% block content %} diff --git a/resources/views/bills/create.twig b/resources/views/bills/create.twig index eb1a970b22..69ccaa19fb 100644 --- a/resources/views/bills/create.twig +++ b/resources/views/bills/create.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, piggyBank) }} diff --git a/resources/views/bills/delete.twig b/resources/views/bills/delete.twig index d9185aa759..344779bdc7 100644 --- a/resources/views/bills/delete.twig +++ b/resources/views/bills/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, bill) }} diff --git a/resources/views/bills/edit.twig b/resources/views/bills/edit.twig index ed8d331b82..53c1d7a717 100644 --- a/resources/views/bills/edit.twig +++ b/resources/views/bills/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, bill) }} diff --git a/resources/views/bills/index.twig b/resources/views/bills/index.twig index 644c0f243e..28bc6261fe 100644 --- a/resources/views/bills/index.twig +++ b/resources/views/bills/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} @@ -22,7 +22,7 @@
- {% include 'list/bills.twig' %} + {% include 'list/bills' %}
diff --git a/resources/views/bills/show.twig b/resources/views/bills/show.twig index 7622ee694d..5730340721 100644 --- a/resources/views/bills/show.twig +++ b/resources/views/bills/show.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, bill) }} diff --git a/resources/views/budgets/create.twig b/resources/views/budgets/create.twig index 24f47d7ac2..3974791a23 100644 --- a/resources/views/budgets/create.twig +++ b/resources/views/budgets/create.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/budgets/delete.twig b/resources/views/budgets/delete.twig index 10601310ee..297cef882a 100644 --- a/resources/views/budgets/delete.twig +++ b/resources/views/budgets/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, budget) }} diff --git a/resources/views/budgets/edit.twig b/resources/views/budgets/edit.twig index 0bf5ec3daa..e8010bc424 100644 --- a/resources/views/budgets/edit.twig +++ b/resources/views/budgets/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, budget) }} diff --git a/resources/views/budgets/index.twig b/resources/views/budgets/index.twig index e9524e6acf..66e95d7f1f 100644 --- a/resources/views/budgets/index.twig +++ b/resources/views/budgets/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/budgets/no-budget.twig b/resources/views/budgets/no-budget.twig index 33223893ba..e8e1f0809b 100644 --- a/resources/views/budgets/no-budget.twig +++ b/resources/views/budgets/no-budget.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, subTitle) }} diff --git a/resources/views/budgets/show.twig b/resources/views/budgets/show.twig index 65ba0465fa..b0331ae279 100644 --- a/resources/views/budgets/show.twig +++ b/resources/views/budgets/show.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, budget, repetition) }} diff --git a/resources/views/categories/create.twig b/resources/views/categories/create.twig index a7911555fd..e99d27b20a 100644 --- a/resources/views/categories/create.twig +++ b/resources/views/categories/create.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/categories/delete.twig b/resources/views/categories/delete.twig index 858af38417..ffe9a5b0ef 100644 --- a/resources/views/categories/delete.twig +++ b/resources/views/categories/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, category) }} diff --git a/resources/views/categories/edit.twig b/resources/views/categories/edit.twig index 5e40a3254e..3a465adf1b 100644 --- a/resources/views/categories/edit.twig +++ b/resources/views/categories/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, category) }} diff --git a/resources/views/categories/index.twig b/resources/views/categories/index.twig index 95428b52a3..18a1e08314 100644 --- a/resources/views/categories/index.twig +++ b/resources/views/categories/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} @@ -23,7 +23,7 @@
- {% include 'list/categories.twig' %} + {% include 'list/categories' %}
diff --git a/resources/views/categories/no-category.twig b/resources/views/categories/no-category.twig index 93db7971a8..c302ca0203 100644 --- a/resources/views/categories/no-category.twig +++ b/resources/views/categories/no-category.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, subTitle) }} diff --git a/resources/views/categories/show.twig b/resources/views/categories/show.twig index 0800aaf7b9..311b857d6e 100644 --- a/resources/views/categories/show.twig +++ b/resources/views/categories/show.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, category) }} diff --git a/resources/views/categories/show_with_date.twig b/resources/views/categories/show_with_date.twig index 6b906643d1..624a933957 100644 --- a/resources/views/categories/show_with_date.twig +++ b/resources/views/categories/show_with_date.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, category, carbon) }} diff --git a/resources/views/csv/column-roles.twig b/resources/views/csv/column-roles.twig index f25a0a2ea7..92b2590df7 100644 --- a/resources/views/csv/column-roles.twig +++ b/resources/views/csv/column-roles.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/csv/download-config.twig b/resources/views/csv/download-config.twig index bd317bea7c..1dd7a94795 100644 --- a/resources/views/csv/download-config.twig +++ b/resources/views/csv/download-config.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/csv/index.twig b/resources/views/csv/index.twig index cfa3f6450a..141566dff6 100644 --- a/resources/views/csv/index.twig +++ b/resources/views/csv/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/csv/map.twig b/resources/views/csv/map.twig index f8562205bd..3f1cca1587 100644 --- a/resources/views/csv/map.twig +++ b/resources/views/csv/map.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/csv/process.twig b/resources/views/csv/process.twig index 8ca39b64c9..2561268912 100644 --- a/resources/views/csv/process.twig +++ b/resources/views/csv/process.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/currency/create.twig b/resources/views/currency/create.twig index 70943d3e15..26dec5a0b1 100644 --- a/resources/views/currency/create.twig +++ b/resources/views/currency/create.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/currency/delete.twig b/resources/views/currency/delete.twig index 492f33b6e0..5874f126f2 100644 --- a/resources/views/currency/delete.twig +++ b/resources/views/currency/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, currency) }} diff --git a/resources/views/currency/edit.twig b/resources/views/currency/edit.twig index a39ab1b0e4..e54d5f5ca1 100644 --- a/resources/views/currency/edit.twig +++ b/resources/views/currency/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, currency) }} diff --git a/resources/views/currency/index.twig b/resources/views/currency/index.twig index 4effcba0a5..7720779d98 100644 --- a/resources/views/currency/index.twig +++ b/resources/views/currency/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/error.twig b/resources/views/error.twig index 4390f9c05c..6d5f52c6ab 100644 --- a/resources/views/error.twig +++ b/resources/views/error.twig @@ -1,4 +1,4 @@ -{% extends "./layout/guest.twig" %} +{% extends "./layout/guest" %} {% block content %} diff --git a/resources/views/errors/FireflyException.twig b/resources/views/errors/FireflyException.twig index 3e0a701038..aee457c80d 100644 --- a/resources/views/errors/FireflyException.twig +++ b/resources/views/errors/FireflyException.twig @@ -13,7 +13,7 @@ - {% include('partials/favicons.twig') %} + {% include('partials/favicons') %} diff --git a/resources/views/export/index.twig b/resources/views/export/index.twig index 1b599e19d5..50b0123c5d 100644 --- a/resources/views/export/index.twig +++ b/resources/views/export/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/form/amount.twig b/resources/views/form/amount.twig index 46f9fec160..c28227d1f7 100644 --- a/resources/views/form/amount.twig +++ b/resources/views/form/amount.twig @@ -26,7 +26,7 @@ - {% include 'form/feedback.twig' %} + {% include 'form/feedback' %} diff --git a/resources/views/form/balance.twig b/resources/views/form/balance.twig index 46f9fec160..c28227d1f7 100644 --- a/resources/views/form/balance.twig +++ b/resources/views/form/balance.twig @@ -26,7 +26,7 @@ - {% include 'form/feedback.twig' %} + {% include 'form/feedback' %} diff --git a/resources/views/form/checkbox.twig b/resources/views/form/checkbox.twig index 9e2d718b81..d4a0143871 100644 --- a/resources/views/form/checkbox.twig +++ b/resources/views/form/checkbox.twig @@ -13,7 +13,7 @@ {{ Form.checkbox(name, value, options.checked, options) }} - {% include 'form/help.twig' %} - {% include 'form/feedback.twig' %} + {% include 'form/help' %} + {% include 'form/feedback' %} diff --git a/resources/views/form/date.twig b/resources/views/form/date.twig index 3f08f20e3b..0e86cf5a07 100644 --- a/resources/views/form/date.twig +++ b/resources/views/form/date.twig @@ -8,7 +8,7 @@ {{ Form.input('date', name, value, options) }} - {% include 'form/help.twig' %} - {% include 'form/feedback.twig' %} + {% include 'form/help' %} + {% include 'form/feedback' %} diff --git a/resources/views/form/file.twig b/resources/views/form/file.twig index 0596ef7dbd..21da01da64 100644 --- a/resources/views/form/file.twig +++ b/resources/views/form/file.twig @@ -3,7 +3,7 @@
{{ Form.file(name, options) }} - {% include 'form/help.twig' %} - {% include 'form/feedback.twig' %} + {% include 'form/help' %} + {% include 'form/feedback' %}
diff --git a/resources/views/form/integer.twig b/resources/views/form/integer.twig index 6b0b94fe09..9ce0e38f5b 100644 --- a/resources/views/form/integer.twig +++ b/resources/views/form/integer.twig @@ -4,7 +4,7 @@
{{ Form.input('number', name, value, options) }} - {% include 'form/feedback.twig' %} + {% include 'form/feedback' %}
diff --git a/resources/views/form/location.twig b/resources/views/form/location.twig index 0f57303f3b..34503bab77 100644 --- a/resources/views/form/location.twig +++ b/resources/views/form/location.twig @@ -10,6 +10,6 @@ - {% include 'form/feedback.twig' %} + {% include 'form/feedback' %} diff --git a/resources/views/form/multiCheckbox.twig b/resources/views/form/multiCheckbox.twig index 5687551004..39776e2be0 100644 --- a/resources/views/form/multiCheckbox.twig +++ b/resources/views/form/multiCheckbox.twig @@ -15,8 +15,8 @@ {% endfor %} - {% include 'form/help.twig' %} - {% include 'form/feedback.twig' %} + {% include 'form/help' %} + {% include 'form/feedback' %} diff --git a/resources/views/form/multiRadio.twig b/resources/views/form/multiRadio.twig index 1bef4bec1e..f321eda908 100644 --- a/resources/views/form/multiRadio.twig +++ b/resources/views/form/multiRadio.twig @@ -10,8 +10,8 @@ {% endfor %} - {% include 'form/help.twig' %} - {% include 'form/feedback.twig' %} + {% include 'form/help' %} + {% include 'form/feedback' %} diff --git a/resources/views/form/select.twig b/resources/views/form/select.twig index e5b6a7874f..2d48ddfcaa 100644 --- a/resources/views/form/select.twig +++ b/resources/views/form/select.twig @@ -3,8 +3,8 @@
{{ Form.select(name, list, selected , options ) }} - {% include 'form/help.twig' %} - {% include 'form/feedback.twig' %} + {% include 'form/help' %} + {% include 'form/feedback' %}
diff --git a/resources/views/form/tags.twig b/resources/views/form/tags.twig index 0b199f9117..e504f6c452 100644 --- a/resources/views/form/tags.twig +++ b/resources/views/form/tags.twig @@ -3,6 +3,6 @@
{{ Form.input('text', name, value, options) }} - {% include 'form/feedback.twig' %} + {% include 'form/feedback' %}
diff --git a/resources/views/form/text.twig b/resources/views/form/text.twig index c2f890c0fe..e94cee84fb 100644 --- a/resources/views/form/text.twig +++ b/resources/views/form/text.twig @@ -3,7 +3,7 @@
{{ Form.input('text', name, value, options) }} - {% include 'form/help.twig' %} - {% include 'form/feedback.twig' %} + {% include 'form/help' %} + {% include 'form/feedback' %}
diff --git a/resources/views/form/textarea.twig b/resources/views/form/textarea.twig index 9bed7883e2..c765ee4eee 100644 --- a/resources/views/form/textarea.twig +++ b/resources/views/form/textarea.twig @@ -3,7 +3,7 @@
{{ Form.textarea(name, value, options) }} - {% include 'form/help.twig' %} - {% include 'form/feedback.twig' %} + {% include 'form/help' %} + {% include 'form/feedback' %}
diff --git a/resources/views/import/complete.twig b/resources/views/import/complete.twig index 4272cc3648..9a95c16620 100644 --- a/resources/views/import/complete.twig +++ b/resources/views/import/complete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists }} diff --git a/resources/views/import/csv/configure.twig b/resources/views/import/csv/configure.twig index 7dccd35ede..d650adbbb8 100644 --- a/resources/views/import/csv/configure.twig +++ b/resources/views/import/csv/configure.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, job) }} diff --git a/resources/views/import/csv/map.twig b/resources/views/import/csv/map.twig index 5f21f1a9d0..159f73edd6 100644 --- a/resources/views/import/csv/map.twig +++ b/resources/views/import/csv/map.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/import/csv/roles.twig b/resources/views/import/csv/roles.twig index f10122ea1e..aad820fbbd 100644 --- a/resources/views/import/csv/roles.twig +++ b/resources/views/import/csv/roles.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/import/finished.twig b/resources/views/import/finished.twig index abf47a20c6..63e5b450fc 100644 --- a/resources/views/import/finished.twig +++ b/resources/views/import/finished.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists }} diff --git a/resources/views/import/index.twig b/resources/views/import/index.twig index d3188651b3..922a3fa12e 100644 --- a/resources/views/import/index.twig +++ b/resources/views/import/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists }} diff --git a/resources/views/import/status.twig b/resources/views/import/status.twig index c2bb550fff..fbddfa8421 100644 --- a/resources/views/import/status.twig +++ b/resources/views/import/status.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists }} diff --git a/resources/views/index.twig b/resources/views/index.twig index 14a14c66e3..b13beb55b7 100644 --- a/resources/views/index.twig +++ b/resources/views/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists }} @@ -8,7 +8,7 @@ - {% include 'partials/boxes.twig' %} + {% include 'partials/boxes' %}
diff --git a/resources/views/layout/default.twig b/resources/views/layout/default.twig index cd63a16aa3..40e3644cee 100644 --- a/resources/views/layout/default.twig +++ b/resources/views/layout/default.twig @@ -29,7 +29,7 @@ - {% include('partials/favicons.twig') %} + {% include('partials/favicons') %} @@ -98,7 +98,7 @@ - {% include('partials/menu-sidebar.twig') %} + {% include('partials/menu-sidebar') %} @@ -107,14 +107,14 @@
- {% include('partials/page-header.twig') %} + {% include('partials/page-header') %} {% block breadcrumbs %}{% endblock %}
- {% include('partials/flashes.twig') %} + {% include('partials/flashes') %} {% block content %}{% endblock %} @@ -130,7 +130,7 @@ Firefly III - {% include('partials/control-bar.twig') %} + {% include('partials/control-bar') %}
diff --git a/resources/views/layout/empty.twig b/resources/views/layout/empty.twig index 6545719f45..9fa7600bdc 100644 --- a/resources/views/layout/empty.twig +++ b/resources/views/layout/empty.twig @@ -17,7 +17,7 @@ - {% include('partials/favicons.twig') %} + {% include('partials/favicons') %} diff --git a/resources/views/layout/guest.twig b/resources/views/layout/guest.twig index a55e49c412..e0e0ccc07f 100644 --- a/resources/views/layout/guest.twig +++ b/resources/views/layout/guest.twig @@ -27,7 +27,7 @@ - {% include('partials/favicons.twig') %} + {% include('partials/favicons') %} diff --git a/resources/views/new-user/index.twig b/resources/views/new-user/index.twig index 7dbb572dcc..3dbcdb81ae 100644 --- a/resources/views/new-user/index.twig +++ b/resources/views/new-user/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/piggy-banks/add-mobile.twig b/resources/views/piggy-banks/add-mobile.twig index 8839178622..29460b3e47 100644 --- a/resources/views/piggy-banks/add-mobile.twig +++ b/resources/views/piggy-banks/add-mobile.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/piggy-banks/create.twig b/resources/views/piggy-banks/create.twig index c2d5480ef1..4d462164bc 100644 --- a/resources/views/piggy-banks/create.twig +++ b/resources/views/piggy-banks/create.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/piggy-banks/delete.twig b/resources/views/piggy-banks/delete.twig index 6c52087c91..3b7b88beef 100644 --- a/resources/views/piggy-banks/delete.twig +++ b/resources/views/piggy-banks/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, piggyBank) }} diff --git a/resources/views/piggy-banks/edit.twig b/resources/views/piggy-banks/edit.twig index 8daeef739f..35f8f26cc5 100644 --- a/resources/views/piggy-banks/edit.twig +++ b/resources/views/piggy-banks/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, piggyBank) }} diff --git a/resources/views/piggy-banks/index.twig b/resources/views/piggy-banks/index.twig index b01ceac8b7..6c4afac57b 100644 --- a/resources/views/piggy-banks/index.twig +++ b/resources/views/piggy-banks/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} @@ -11,7 +11,7 @@

{{ 'piggyBanks'|_ }}

- {% include 'list/piggy-banks.twig' %} + {% include 'list/piggy-banks' %}
@@ -108,7 +108,7 @@
- {% include 'reports/partials/bills.twig' %} + {% include 'reports/partials/bills' %}
diff --git a/resources/views/reports/default/multi-year.twig b/resources/views/reports/default/multi-year.twig index d4a059577a..f48272e92c 100644 --- a/resources/views/reports/default/multi-year.twig +++ b/resources/views/reports/default/multi-year.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, start, end, reportType, accountIds) }} diff --git a/resources/views/reports/default/year.twig b/resources/views/reports/default/year.twig index 99a9a75d11..bce36d9970 100644 --- a/resources/views/reports/default/year.twig +++ b/resources/views/reports/default/year.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, start, end, reportType, accountIds) }} @@ -67,7 +67,7 @@
- {% include 'reports/partials/tags.twig' %} + {% include 'reports/partials/tags' %}
diff --git a/resources/views/reports/index.twig b/resources/views/reports/index.twig index 0fe5cfbbc7..4877fffc62 100644 --- a/resources/views/reports/index.twig +++ b/resources/views/reports/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/rules/index.twig b/resources/views/rules/index.twig index 5c0dee4ef9..803575e88a 100644 --- a/resources/views/rules/index.twig +++ b/resources/views/rules/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} {% endblock %} diff --git a/resources/views/rules/rule-group/create.twig b/resources/views/rules/rule-group/create.twig index 5cb21384f7..e8243d4d4c 100644 --- a/resources/views/rules/rule-group/create.twig +++ b/resources/views/rules/rule-group/create.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/rules/rule-group/delete.twig b/resources/views/rules/rule-group/delete.twig index 123977b5c1..6690c83efc 100644 --- a/resources/views/rules/rule-group/delete.twig +++ b/resources/views/rules/rule-group/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, ruleGroup) }} diff --git a/resources/views/rules/rule-group/edit.twig b/resources/views/rules/rule-group/edit.twig index 4156879820..e556feabb5 100644 --- a/resources/views/rules/rule-group/edit.twig +++ b/resources/views/rules/rule-group/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, ruleGroup) }} diff --git a/resources/views/rules/rule-group/select-transactions.twig b/resources/views/rules/rule-group/select-transactions.twig index 2dccd88379..90a85ee0d7 100644 --- a/resources/views/rules/rule-group/select-transactions.twig +++ b/resources/views/rules/rule-group/select-transactions.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, ruleGroup) }} diff --git a/resources/views/rules/rule/create.twig b/resources/views/rules/rule/create.twig index 7b1e55736f..15b80d88af 100644 --- a/resources/views/rules/rule/create.twig +++ b/resources/views/rules/rule/create.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, ruleGroup) }} @@ -71,7 +71,7 @@
- {% include '/rules/partials/test-trigger-modal.twig' %} + {% include '/rules/partials/test-trigger-modal' %}
diff --git a/resources/views/rules/rule/delete.twig b/resources/views/rules/rule/delete.twig index 3c37f0da08..0d01f303b8 100644 --- a/resources/views/rules/rule/delete.twig +++ b/resources/views/rules/rule/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, rule) }} diff --git a/resources/views/rules/rule/edit.twig b/resources/views/rules/rule/edit.twig index 69559d895d..1260c34b52 100644 --- a/resources/views/rules/rule/edit.twig +++ b/resources/views/rules/rule/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, rule) }} @@ -69,7 +69,7 @@
- {% include '/rules/partials/test-trigger-modal.twig' %} + {% include '/rules/partials/test-trigger-modal' %}
diff --git a/resources/views/search/index.twig b/resources/views/search/index.twig index 2f9b8afdc5..35bf5a3d28 100644 --- a/resources/views/search/index.twig +++ b/resources/views/search/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, query) }} diff --git a/resources/views/tags/create.twig b/resources/views/tags/create.twig index ea9c4e12ac..95b99f7d9d 100644 --- a/resources/views/tags/create.twig +++ b/resources/views/tags/create.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/tags/delete.twig b/resources/views/tags/delete.twig index 966ad7e011..e6cd27f096 100644 --- a/resources/views/tags/delete.twig +++ b/resources/views/tags/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, tag) }} diff --git a/resources/views/tags/edit.twig b/resources/views/tags/edit.twig index 0fc6ec210d..e6a089d269 100644 --- a/resources/views/tags/edit.twig +++ b/resources/views/tags/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, tag) }} diff --git a/resources/views/tags/index.twig b/resources/views/tags/index.twig index 22358ac738..78512e056d 100644 --- a/resources/views/tags/index.twig +++ b/resources/views/tags/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/tags/show.twig b/resources/views/tags/show.twig index fbdc4432d4..e78b69f5b1 100644 --- a/resources/views/tags/show.twig +++ b/resources/views/tags/show.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, tag) }} diff --git a/resources/views/transactions/convert.twig b/resources/views/transactions/convert.twig index c5c7c95429..40c8429b71 100644 --- a/resources/views/transactions/convert.twig +++ b/resources/views/transactions/convert.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, destinationType, journal) }} diff --git a/resources/views/transactions/create.twig b/resources/views/transactions/create.twig index 78de00ecd4..38c961ccc0 100644 --- a/resources/views/transactions/create.twig +++ b/resources/views/transactions/create.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, what) }} diff --git a/resources/views/transactions/delete.twig b/resources/views/transactions/delete.twig index 0ce9edbec9..69e019d0e8 100644 --- a/resources/views/transactions/delete.twig +++ b/resources/views/transactions/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, journal) }} diff --git a/resources/views/transactions/edit-split.twig b/resources/views/transactions/edit-split.twig index 451371820b..1ceb75baf3 100644 --- a/resources/views/transactions/edit-split.twig +++ b/resources/views/transactions/edit-split.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, journal) }} diff --git a/resources/views/transactions/edit.twig b/resources/views/transactions/edit.twig index bd8a39cf54..edf11ef10b 100644 --- a/resources/views/transactions/edit.twig +++ b/resources/views/transactions/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, journal) }} diff --git a/resources/views/transactions/index.twig b/resources/views/transactions/index.twig index ab081e5514..3d63c4e0b1 100644 --- a/resources/views/transactions/index.twig +++ b/resources/views/transactions/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, what) }} diff --git a/resources/views/transactions/mass-delete.twig b/resources/views/transactions/mass-delete.twig index 2be459dbbc..0cc411b8d0 100644 --- a/resources/views/transactions/mass-delete.twig +++ b/resources/views/transactions/mass-delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/transactions/mass-edit.twig b/resources/views/transactions/mass-edit.twig index 59260f2603..b42592d13a 100644 --- a/resources/views/transactions/mass-edit.twig +++ b/resources/views/transactions/mass-edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/transactions/show.twig b/resources/views/transactions/show.twig index f4928d89f4..2ade2b2752 100644 --- a/resources/views/transactions/show.twig +++ b/resources/views/transactions/show.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, journal) }} From 22a2fe3f610fb05939b6c423e4665886ccc4a403 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 6 Nov 2016 14:52:31 +0100 Subject: [PATCH 057/709] Improved search. --- app/Http/Controllers/SearchController.php | 23 +- app/Providers/FireflyServiceProvider.php | 1 - app/Providers/SearchServiceProvider.php | 59 +++++ app/Rules/TransactionMatcher.php | 4 +- app/Support/Search/Search.php | 204 +++++++++++++----- config/app.php | 1 + resources/lang/en_US/firefly.php | 12 ++ resources/views/search/index.twig | 104 +++++---- resources/views/search/partials/accounts.twig | 34 +++ resources/views/search/partials/budgets.twig | 22 ++ .../views/search/partials/categories.twig | 22 ++ resources/views/search/partials/tags.twig | 24 +++ .../views/search/partials/transactions.twig | 81 +++++++ 13 files changed, 466 insertions(+), 125 deletions(-) create mode 100644 app/Providers/SearchServiceProvider.php create mode 100644 resources/views/search/partials/accounts.twig create mode 100644 resources/views/search/partials/budgets.twig create mode 100644 resources/views/search/partials/categories.twig create mode 100644 resources/views/search/partials/tags.twig create mode 100644 resources/views/search/partials/transactions.twig diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index bb602229d3..e9b63f4b17 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -14,7 +14,7 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; use FireflyIII\Support\Search\SearchInterface; -use Input; +use Illuminate\Http\Request; /** * Class SearchController @@ -30,12 +30,6 @@ class SearchController extends Controller { parent::__construct(); - $this->middleware( - function ($request, $next) { - - return $next($request); - } - ); } /** @@ -45,16 +39,21 @@ class SearchController extends Controller * * @return $this */ - public function index(SearchInterface $searcher) + public function index(Request $request, SearchInterface $searcher) { - + $minSearchLen = 1; $subTitle = null; $query = null; $result = []; $title = trans('firefly.search'); + $limit = 20; $mainTitleIcon = 'fa-search'; - if (!is_null(Input::get('q')) && strlen(Input::get('q')) > 0) { - $query = trim(Input::get('q')); + + // set limit for search: + $searcher->setLimit($limit); + + if (!is_null($request->get('q')) && strlen($request->get('q')) >= $minSearchLen) { + $query = trim(strtolower($request->get('q'))); $words = explode(' ', $query); $subTitle = trans('firefly.search_results_for', ['query' => $query]); @@ -67,7 +66,7 @@ class SearchController extends Controller } - return view('search.index', compact('title', 'subTitle', 'mainTitleIcon', 'query', 'result')); + return view('search.index', compact('title', 'subTitle', 'limit', 'mainTitleIcon', 'query', 'result')); } } diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php index b1530b08ca..91122f0d5f 100644 --- a/app/Providers/FireflyServiceProvider.php +++ b/app/Providers/FireflyServiceProvider.php @@ -94,7 +94,6 @@ class FireflyServiceProvider extends ServiceProvider ); $this->app->bind('FireflyIII\Repositories\Currency\CurrencyRepositoryInterface', 'FireflyIII\Repositories\Currency\CurrencyRepository'); - $this->app->bind('FireflyIII\Support\Search\SearchInterface', 'FireflyIII\Support\Search\Search'); $this->app->bind('FireflyIII\Repositories\User\UserRepositoryInterface', 'FireflyIII\Repositories\User\UserRepository'); $this->app->bind('FireflyIII\Helpers\Attachments\AttachmentHelperInterface', 'FireflyIII\Helpers\Attachments\AttachmentHelper'); $this->app->bind( diff --git a/app/Providers/SearchServiceProvider.php b/app/Providers/SearchServiceProvider.php new file mode 100644 index 0000000000..a7be767e5e --- /dev/null +++ b/app/Providers/SearchServiceProvider.php @@ -0,0 +1,59 @@ +app->bind( + 'FireflyIII\Support\Search\SearchInterface', + function (Application $app, array $arguments) { + if (!isset($arguments[0]) && $app->auth->check()) { + return app('FireflyIII\Support\Search\Search', [auth()->user()]); + } + if (!isset($arguments[0]) && !$app->auth->check()) { + throw new FireflyException('There is no user present.'); + } + + return app('FireflyIII\Support\Search\Search', $arguments); + } + ); + } +} diff --git a/app/Rules/TransactionMatcher.php b/app/Rules/TransactionMatcher.php index eb845dee29..0e63c06085 100644 --- a/app/Rules/TransactionMatcher.php +++ b/app/Rules/TransactionMatcher.php @@ -78,7 +78,7 @@ class TransactionMatcher do { // Fetch a batch of transactions from the database $collector = new JournalCollector(auth()->user()); - $collector->setAllAssetAccounts()->setLimit($pageSize * 2)->setPage($page)->setTypes($this->transactionTypes); + $collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page)->setTypes($this->transactionTypes); $set = $collector->getPaginatedJournals(); Log::debug(sprintf('Found %d journals to check. ', $set->count())); @@ -105,7 +105,7 @@ class TransactionMatcher Log::debug(sprintf('Page is now %d, processed is %d', $page, $processed)); // Check for conditions to finish the loop - $reachedEndOfList = $set->count() < $pageSize; + $reachedEndOfList = $set->count() < 1; $foundEnough = $result->count() >= $this->limit; $searchedEnough = ($processed >= $this->range); diff --git a/app/Support/Search/Search.php b/app/Support/Search/Search.php index d20e83413f..c5e1da96c7 100644 --- a/app/Support/Search/Search.php +++ b/app/Support/Search/Search.php @@ -14,11 +14,15 @@ declare(strict_types = 1); namespace FireflyIII\Support\Search; +use FireflyIII\Helpers\Collector\JournalCollector; +use FireflyIII\Models\Account; use FireflyIII\Models\Budget; use FireflyIII\Models\Category; -use FireflyIII\Models\TransactionJournal; -use Illuminate\Database\Eloquent\Builder as EloquentBuilder; +use FireflyIII\Models\Tag; +use FireflyIII\Models\Transaction; +use FireflyIII\User; use Illuminate\Support\Collection; +use Log; /** * Class Search @@ -27,20 +31,46 @@ use Illuminate\Support\Collection; */ class Search implements SearchInterface { + /** @var int */ + private $limit = 100; + /** @var User */ + private $user; + /** + * AttachmentRepository constructor. + * + * @param User $user + */ + public function __construct(User $user) + { + $this->user = $user; + } + + /** + * The search will assume that the user does not have so many accounts + * that this search should be paginated. + * * @param array $words * * @return Collection */ public function searchAccounts(array $words): Collection { - return auth()->user()->accounts()->with('accounttype')->where( - function (EloquentBuilder $q) use ($words) { - foreach ($words as $word) { - $q->orWhere('name', 'LIKE', '%' . e($word) . '%'); + $accounts = $this->user->accounts()->get(); + /** @var Collection $result */ + $result = $accounts->filter( + function (Account $account) use ($words) { + if ($this->strpos_arr(strtolower($account->name), $words)) { + return $account; } + + return false; } - )->get(); + ); + + $result = $result->slice(0, $this->limit); + + return $result; } /** @@ -51,46 +81,46 @@ class Search implements SearchInterface public function searchBudgets(array $words): Collection { /** @var Collection $set */ - $set = auth()->user()->budgets()->get(); - $newSet = $set->filter( - function (Budget $b) use ($words) { - $found = 0; - foreach ($words as $word) { - if (!(strpos(strtolower($b->name), strtolower($word)) === false)) { - $found++; - } + $set = auth()->user()->budgets()->get(); + /** @var Collection $result */ + $result = $set->filter( + function (Budget $budget) use ($words) { + if ($this->strpos_arr(strtolower($budget->name), $words)) { + return $budget; } - return $found > 0; + return false; } ); - return $newSet; + $result = $result->slice(0, $this->limit); + + return $result; } /** + * Search assumes the user does not have that many categories. So no paginated search. + * * @param array $words * * @return Collection */ public function searchCategories(array $words): Collection { - /** @var Collection $set */ - $set = auth()->user()->categories()->get(); - $newSet = $set->filter( - function (Category $c) use ($words) { - $found = 0; - foreach ($words as $word) { - if (!(strpos(strtolower($c->name), strtolower($word)) === false)) { - $found++; - } + $categories = $this->user->categories()->get(); + /** @var Collection $result */ + $result = $categories->filter( + function (Category $category) use ($words) { + if ($this->strpos_arr(strtolower($category->name), $words)) { + return $category; } - return $found > 0; + return false; } ); + $result = $result->slice(0, $this->limit); - return $newSet; + return $result; } /** @@ -101,7 +131,21 @@ class Search implements SearchInterface */ public function searchTags(array $words): Collection { - return new Collection; + $tags = $this->user->tags()->get(); + + /** @var Collection $result */ + $result = $tags->filter( + function (Tag $tag) use ($words) { + if ($this->strpos_arr(strtolower($tag->tag), $words)) { + return $tag; + } + + return false; + } + ); + $result = $result->slice(0, $this->limit); + + return $result; } /** @@ -111,40 +155,86 @@ class Search implements SearchInterface */ public function searchTransactions(array $words): Collection { - // decrypted transaction journals: - $decrypted = auth()->user()->transactionJournals()->expanded()->where('transaction_journals.encrypted', 0)->where( - function (EloquentBuilder $q) use ($words) { - foreach ($words as $word) { - $q->orWhere('transaction_journals.description', 'LIKE', '%' . e($word) . '%'); - } - } - )->get(TransactionJournal::queryFields()); + $pageSize = 100; + $processed = 0; + $page = 1; + $result = new Collection(); + do { + $collector = new JournalCollector($this->user); + $collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page); + $set = $collector->getPaginatedJournals(); + Log::debug(sprintf('Found %d journals to check. ', $set->count())); - // encrypted - $all = auth()->user()->transactionJournals()->expanded()->where('transaction_journals.encrypted', 1)->get(TransactionJournal::queryFields()); - $set = $all->filter( - function (TransactionJournal $journal) use ($words) { - foreach ($words as $word) { - $haystack = strtolower($journal->description); - $word = strtolower($word); - if (!(strpos($haystack, $word) === false)) { - return $journal; + // Filter transactions that match the given triggers. + $filtered = $set->filter( + function (Transaction $transaction) use ($words) { + // check descr of journal: + if ($this->strpos_arr(strtolower(strval($transaction->description)), $words)) { + return $transaction; } + + // check descr of transaction + if ($this->strpos_arr(strtolower(strval($transaction->transaction_description)), $words)) { + return $transaction; + } + + // return false: + return false; } + ); - return null; + Log::debug(sprintf('Found %d journals that match.', $filtered->count())); + // merge: + /** @var Collection $result */ + $result = $result->merge($filtered); + Log::debug(sprintf('Total count is now %d', $result->count())); + + // Update counters + $page++; + $processed += count($set); + + Log::debug(sprintf('Page is now %d, processed is %d', $page, $processed)); + + // Check for conditions to finish the loop + $reachedEndOfList = $set->count() < 1; + $foundEnough = $result->count() >= $this->limit; + + Log::debug(sprintf('reachedEndOfList: %s', var_export($reachedEndOfList, true))); + Log::debug(sprintf('foundEnough: %s', var_export($foundEnough, true))); + + } while (!$reachedEndOfList && !$foundEnough); + + $result = $result->slice(0, $this->limit); + + return $result; + } + + /** + * @param int $limit + */ + public function setLimit(int $limit) + { + $this->limit = $limit; + } + + /** + * @param string $haystack + * @param array $needle + * + * @return bool + */ + private function strpos_arr(string $haystack, array $needle) + { + if (strlen($haystack) === 0) { + return false; + } + foreach ($needle as $what) { + if (($pos = strpos($haystack, $what)) !== false) { + return true; } - ); - $filtered = $set->merge($decrypted); - $filtered = $filtered->sortBy( - function (TransactionJournal $journal) { - return intval($journal->date->format('U')); - } - ); + } - $filtered = $filtered->reverse(); - - return $filtered; + return false; } } diff --git a/config/app.php b/config/app.php index 0611c68b7f..478fae8b8d 100755 --- a/config/app.php +++ b/config/app.php @@ -208,6 +208,7 @@ return [ FireflyIII\Providers\PiggyBankServiceProvider::class, FireflyIII\Providers\RuleServiceProvider::class, FireflyIII\Providers\RuleGroupServiceProvider::class, + FireflyIII\Providers\SearchServiceProvider::class, FireflyIII\Providers\TagServiceProvider::class, diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 8519610ca7..b7643c4751 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -67,7 +67,19 @@ return [ 'warning_much_data' => ':days days of data may take a while to load.', 'registered' => 'You have registered successfully!', 'search' => 'Search', + 'search_found_accounts' => 'Found :count account(s) for your query.', + 'search_found_categories' => 'Found :count category(ies) for your query.', + 'search_found_budgets' => 'Found :count budget(s) for your query.', + 'search_found_tags' => 'Found :count tag(s) for your query.', + 'search_found_transactions' => 'Found :count transaction(s) for your query.', + 'results_limited' => 'The results are limited to :count entries.', + 'tagbalancingAct' => 'Balancing act', + 'tagadvancePayment' => 'Advance payment', + 'tagnothing' => '', + 'Default asset account' => 'Default asset account', 'no_budget_pointer' => 'You seem to have no budgets yet. You should create some on the budgets-page. Budgets can help you keep track of expenses.', + 'Savings account' => 'Savings account', + 'Credit card' => 'Credit card', 'source_accounts' => 'Source account(s)', 'destination_accounts' => 'Destination account(s)', 'user_id_is' => 'Your user id is :user', diff --git a/resources/views/search/index.twig b/resources/views/search/index.twig index 35bf5a3d28..88107c52d6 100644 --- a/resources/views/search/index.twig +++ b/resources/views/search/index.twig @@ -5,19 +5,35 @@ {% endblock %} {% block content %} - {% if query %} + {% if query == "" %}
+
+

{{ 'no_results_for_empty_search'|_ }}

+
+
+ {% endif %} + {% if query %} + +
+
+

{{ trans('firefly.results_limited', {count: limit}) }}

+
+
+ +
+ + {% if result.transactions|length > 0 %}
-

Transactions

+

{{ 'transactions'|_ }}

-
- {% include 'list.journals-tiny' with {'transactions' : result.transactions} %} -
-
@@ -26,19 +42,13 @@
-

Categories

+

{{ 'categories'|_ }}

-
-
- {% for category in result.categories %} - - {{ category.name }} - - {% endfor %} -
-
-
@@ -47,13 +57,13 @@
-

Tags

+

{{ 'tags'|_ }}

-
-

Bla bla

-
-
@@ -62,19 +72,13 @@
-

Accounts

+

{{ 'accounts'|_ }}

-
-
- {% for account in result.accounts %} - - {{ account.name }} - - {% endfor %} -
-
-
@@ -83,19 +87,13 @@
-

Budgets

+

{{ 'budgets'|_ }}

-
- {% for budget in result.budgets %} - - {{ budget.name }} - - {% endfor %} -
-
-
@@ -106,8 +104,8 @@ {% endblock %} -{% block scripts %} - -{% endblock %} + {% block scripts %} + + {% endblock %} diff --git a/resources/views/search/partials/accounts.twig b/resources/views/search/partials/accounts.twig new file mode 100644 index 0000000000..e21b443d59 --- /dev/null +++ b/resources/views/search/partials/accounts.twig @@ -0,0 +1,34 @@ +
+ + + + + + + + + + + {% for account in result.accounts %} + + + + + + + + {% endfor %} + + +
{{ trans('list.name') }}
{{ account.name }}{{ trans('firefly.'~account.accountType.type) }}
diff --git a/resources/views/search/partials/budgets.twig b/resources/views/search/partials/budgets.twig new file mode 100644 index 0000000000..ad14e57747 --- /dev/null +++ b/resources/views/search/partials/budgets.twig @@ -0,0 +1,22 @@ + + + + + + + + + {% for budget in result.budgets %} + + + + + {% endfor %} + + +
{{ trans('list.name') }}
{{ budget.name }}
diff --git a/resources/views/search/partials/categories.twig b/resources/views/search/partials/categories.twig new file mode 100644 index 0000000000..4176de3240 --- /dev/null +++ b/resources/views/search/partials/categories.twig @@ -0,0 +1,22 @@ + + + + + + + + + {% for category in result.categories %} + + + + + {% endfor %} + + +
{{ trans('list.name') }}
{{ category.name }}
diff --git a/resources/views/search/partials/tags.twig b/resources/views/search/partials/tags.twig new file mode 100644 index 0000000000..c4b3e21a2c --- /dev/null +++ b/resources/views/search/partials/tags.twig @@ -0,0 +1,24 @@ + + + + + + + + + + {% for tag in result.tags %} + + + + + + {% endfor %} + + +
{{ trans('list.name') }}{{ trans('list.type') }}
{{ tag.tag }}{{ ('tag'~tag.tagMode)|_ }}
diff --git a/resources/views/search/partials/transactions.twig b/resources/views/search/partials/transactions.twig new file mode 100644 index 0000000000..c20da1ff07 --- /dev/null +++ b/resources/views/search/partials/transactions.twig @@ -0,0 +1,81 @@ +{{ journals.render|raw }} + + + + + + + + + + + + {% for transaction in transactions %} + + + + + + + + {% endfor %} + +
{{ trans('list.description') }}{{ trans('list.amount') }}
+ + + {% if transaction.transaction_description|length > 0 %} + {{ transaction.transaction_description }} ({{ transaction.description }}) + {% else %} + {{ transaction.description }} + {% endif %} + + {{ splitJournalIndicator(transaction.journal_id) }} + + {% if transaction.transactionJournal.attachments|length > 0 %} + + {% endif %} + + + + {{ formatAmountWithCode(transaction.transaction_amount, transaction.transaction_currency_code) }} + + {{ optionalJournalAmount(transaction.journal_id, transaction.transaction_amount, + transaction.transaction_currency_code, transaction.transaction_type_type) }} + + +
+ +
+
+ {{ journals.render|raw }} +
+
+ From 7612f1f91adc47a7575d9b5bff8e4e602ca7fc49 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 6 Nov 2016 14:52:48 +0100 Subject: [PATCH 058/709] Small changes to twig files. --- resources/views/accounts/index.twig | 2 +- resources/views/auth/confirmation/error.twig | 4 ++-- resources/views/auth/confirmation/no-resent.twig | 4 ++-- resources/views/auth/confirmation/resent.twig | 4 ++-- resources/views/auth/lost-two-factor.twig | 4 ++-- resources/views/errors/FireflyException.twig | 4 ++-- resources/views/index.twig | 2 +- resources/views/layout/default.twig | 12 ++++++------ resources/views/layout/empty.twig | 4 ++-- resources/views/layout/guest.twig | 4 ++-- resources/views/reports/audit/report.twig | 2 +- 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/resources/views/accounts/index.twig b/resources/views/accounts/index.twig index f5b6bda9e6..f433cddf8b 100644 --- a/resources/views/accounts/index.twig +++ b/resources/views/accounts/index.twig @@ -26,7 +26,7 @@
- {% include 'list/accounts' %} + {% include 'list.accounts' %}
diff --git a/resources/views/auth/confirmation/error.twig b/resources/views/auth/confirmation/error.twig index 3a5888d4d3..a417e4db7b 100644 --- a/resources/views/auth/confirmation/error.twig +++ b/resources/views/auth/confirmation/error.twig @@ -12,8 +12,8 @@ - - {% include('partials/favicons') %} + {# favicons #} + {% include('partials.favicons') %} diff --git a/resources/views/auth/confirmation/no-resent.twig b/resources/views/auth/confirmation/no-resent.twig index 7d23f394ae..64c24e95e0 100644 --- a/resources/views/auth/confirmation/no-resent.twig +++ b/resources/views/auth/confirmation/no-resent.twig @@ -12,8 +12,8 @@ - - {% include('partials/favicons') %} + {# favicons #} + {% include('partials.favicons') %} diff --git a/resources/views/auth/confirmation/resent.twig b/resources/views/auth/confirmation/resent.twig index da8f9272e4..accae395df 100644 --- a/resources/views/auth/confirmation/resent.twig +++ b/resources/views/auth/confirmation/resent.twig @@ -12,8 +12,8 @@ - - {% include('partials/favicons') %} + {# favicons #} + {% include('partials.favicons') %} diff --git a/resources/views/auth/lost-two-factor.twig b/resources/views/auth/lost-two-factor.twig index fa71e19f8b..c82ffddd6c 100644 --- a/resources/views/auth/lost-two-factor.twig +++ b/resources/views/auth/lost-two-factor.twig @@ -12,8 +12,8 @@ - - {% include('partials/favicons') %} + {# favicons #} + {% include('partials.favicons') %} diff --git a/resources/views/errors/FireflyException.twig b/resources/views/errors/FireflyException.twig index aee457c80d..9d8343e04c 100644 --- a/resources/views/errors/FireflyException.twig +++ b/resources/views/errors/FireflyException.twig @@ -12,8 +12,8 @@ - - {% include('partials/favicons') %} + {# favicons #} + {% include('partials.favicons') %} diff --git a/resources/views/index.twig b/resources/views/index.twig index b13beb55b7..679b8a1581 100644 --- a/resources/views/index.twig +++ b/resources/views/index.twig @@ -8,7 +8,7 @@ - {% include 'partials/boxes' %} + {% include 'partials.boxes' %}
diff --git a/resources/views/layout/default.twig b/resources/views/layout/default.twig index 40e3644cee..45a77cca73 100644 --- a/resources/views/layout/default.twig +++ b/resources/views/layout/default.twig @@ -28,8 +28,8 @@ - - {% include('partials/favicons') %} + {# favicons #} + {% include('partials.favicons') %} @@ -98,7 +98,7 @@ - {% include('partials/menu-sidebar') %} + {% include('partials.menu-sidebar') %} @@ -107,14 +107,14 @@
- {% include('partials/page-header') %} + {% include('partials.page-header') %} {% block breadcrumbs %}{% endblock %}
- {% include('partials/flashes') %} + {% include('partials.flashes') %} {% block content %}{% endblock %} @@ -130,7 +130,7 @@ Firefly III - {% include('partials/control-bar') %} + {% include('partials.control-bar') %}
diff --git a/resources/views/layout/empty.twig b/resources/views/layout/empty.twig index 9fa7600bdc..5d0caaf749 100644 --- a/resources/views/layout/empty.twig +++ b/resources/views/layout/empty.twig @@ -16,8 +16,8 @@ - - {% include('partials/favicons') %} + {# favicons #} + {% include('partials.favicons') %} diff --git a/resources/views/layout/guest.twig b/resources/views/layout/guest.twig index e0e0ccc07f..94ac3316ea 100644 --- a/resources/views/layout/guest.twig +++ b/resources/views/layout/guest.twig @@ -26,8 +26,8 @@ - - {% include('partials/favicons') %} + {# favicons #} + {% include('partials.favicons') %} diff --git a/resources/views/reports/audit/report.twig b/resources/views/reports/audit/report.twig index 7c266182cf..ef8b1af2dc 100644 --- a/resources/views/reports/audit/report.twig +++ b/resources/views/reports/audit/report.twig @@ -62,7 +62,7 @@ balance: auditData[account.id].endBalance|formatAmount })|raw }}

- {% include 'reports/partials/journals-audit-tasker' with {'journals': auditData[account.id].journals,'account':account} %} + {% include 'reports.partials.journals-audit-tasker' with {'journals': auditData[account.id].journals,'account':account} %}

{{ trans('firefly.audit_end_balance', { From 0c8a1b51e9c0ebc97ba90d32d141dbaf5289c23c Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 6 Nov 2016 15:01:04 +0100 Subject: [PATCH 059/709] Quick bug fix: missing class. --- app/Http/Controllers/TransactionController.php | 2 +- composer.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index f1f65eebba..264524d20e 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -61,7 +61,7 @@ class TransactionController extends Controller $subTitleIcon = config('firefly.transactionIconsByWhat.' . $what); $types = config('firefly.transactionTypesByWhat.' . $what); $subTitle = trans('firefly.title_' . $what); - $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); + $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); $collector = new JournalCollector(auth()->user()); $collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts(); diff --git a/composer.lock b/composer.lock index 2e8a6bc01f..193f6cd621 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "cc3d23620e727ee1f4741b2e83f8685f", + "hash": "12c9e9450c824d192b8c049989188b8e", "content-hash": "473d3c681e5c41989e9dced651a939df", "packages": [ { From 0b5e25960f14f19fa12c73216f9136c6f9c715e8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 6 Nov 2016 15:04:35 +0100 Subject: [PATCH 060/709] Fix small JS bug. --- public/js/ff/reports/default/all.js | 68 ++++++++++++++++++++++++++++ public/js/ff/reports/default/year.js | 68 ---------------------------- 2 files changed, 68 insertions(+), 68 deletions(-) diff --git a/public/js/ff/reports/default/all.js b/public/js/ff/reports/default/all.js index 5714f46ae3..9ab3c6c8d7 100644 --- a/public/js/ff/reports/default/all.js +++ b/public/js/ff/reports/default/all.js @@ -114,6 +114,9 @@ function displayAjaxPartial(data, holder) { // trigger list thing listLengthInitial(); + + // budget thing + $('.budget-chart-activate').unbind('click').on('click', clickBudgetChart); } function failAjaxPartial(uri, holder) { @@ -121,4 +124,69 @@ function failAjaxPartial(uri, holder) { console.log('Failed to load' + uri); $('#' + holder).removeClass('loading').addClass('general-chart-error'); +} + +function clickBudgetChart(e) { + "use strict"; + var link = $(e.target); + var budgetId = link.data('budget'); + var URL = 'chart/budget/period/' + budgetId + '/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds; + var container = 'budget_chart'; + // if chart drawn is false, draw the first one, then + // set to true + if (chartDrawn == false) { + // do new chart: + + + $.getJSON(URL).done(function (data) { + console.log('Will draw new columnChart(' + URL + ')'); + + var ctx = document.getElementById(container).getContext("2d"); + var newData = {}; + newData.datasets = []; + + for (var i = 0; i < data.count; i++) { + newData.labels = data.labels; + var dataset = data.datasets[i]; + dataset.backgroundColor = fillColors[i]; + newData.datasets.push(dataset); + } + // completely new chart. + budgetChart = new Chart(ctx, { + type: 'bar', + data: data, + options: defaultColumnOptions + }); + + }).fail(function () { + $('#' + container).addClass('general-chart-error'); + }); + console.log('URL for column chart : ' + URL); + chartDrawn = true; + } else { + console.log('Will now handle remove data and add new!'); + $.getJSON(URL).done(function (data) { + console.log('Will draw updated columnChart(' + URL + ')'); + var newData = {}; + newData.datasets = []; + + for (var i = 0; i < data.count; i++) { + newData.labels = data.labels; + var dataset = data.datasets[i]; + dataset.backgroundColor = fillColors[i]; + newData.datasets.push(dataset); + } + // update the chart + console.log('Now update chart thing.'); + budgetChart.data.datasets = newData.datasets; + budgetChart.update(); + + }).fail(function () { + $('#' + container).addClass('general-chart-error'); + }); + + + } + + return false; } \ No newline at end of file diff --git a/public/js/ff/reports/default/year.js b/public/js/ff/reports/default/year.js index 6e718e3f5c..0609105bf6 100644 --- a/public/js/ff/reports/default/year.js +++ b/public/js/ff/reports/default/year.js @@ -8,9 +8,6 @@ $(function () { drawChart(); loadAjaxPartial('budgetOverview',budgetYearOverviewUri); - - // trigger thing for budgets: - $('.budget-chart-activate').unbind('click').on('click', clickBudgetChart); }); function drawChart() { @@ -22,68 +19,3 @@ function drawChart() { } - -function clickBudgetChart(e) { - "use strict"; - var link = $(e.target); - var budgetId = link.data('budget'); - var URL = 'chart/budget/period/' + budgetId + '/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds; - var container = 'budget_chart'; - // if chart drawn is false, draw the first one, then - // set to true - if (chartDrawn == false) { - // do new chart: - - - $.getJSON(URL).done(function (data) { - console.log('Will draw new columnChart(' + URL + ')'); - - var ctx = document.getElementById(container).getContext("2d"); - var newData = {}; - newData.datasets = []; - - for (var i = 0; i < data.count; i++) { - newData.labels = data.labels; - var dataset = data.datasets[i]; - dataset.backgroundColor = fillColors[i]; - newData.datasets.push(dataset); - } - // completely new chart. - budgetChart = new Chart(ctx, { - type: 'bar', - data: data, - options: defaultColumnOptions - }); - - }).fail(function () { - $('#' + container).addClass('general-chart-error'); - }); - console.log('URL for column chart : ' + URL); - chartDrawn = true; - } else { - console.log('Will now handle remove data and add new!'); - $.getJSON(URL).done(function (data) { - console.log('Will draw updated columnChart(' + URL + ')'); - var newData = {}; - newData.datasets = []; - - for (var i = 0; i < data.count; i++) { - newData.labels = data.labels; - var dataset = data.datasets[i]; - dataset.backgroundColor = fillColors[i]; - newData.datasets.push(dataset); - } - // update the chart - console.log('Now update chart thing.'); - budgetChart.data.datasets = newData.datasets; - budgetChart.update(); - - }).fail(function () { - $('#' + container).addClass('general-chart-error'); - }); - - - } - - return false; -} \ No newline at end of file From a7d35cd1c3bc8bf13bce5a8b0f35babada0efc83 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 6 Nov 2016 15:09:44 +0100 Subject: [PATCH 061/709] Fix multi year report. [skip ci] --- public/js/ff/reports/default/multi-year.js | 4 ++-- resources/views/reports/default/multi-year.twig | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/public/js/ff/reports/default/multi-year.js b/public/js/ff/reports/default/multi-year.js index 9274a86ac1..93ecb50039 100644 --- a/public/js/ff/reports/default/multi-year.js +++ b/public/js/ff/reports/default/multi-year.js @@ -1,11 +1,11 @@ -/* globals budgetMultiUrl, accountIds */ +/* globals budgetMultiUri, accountIds */ $(function () { "use strict"; drawChart(); - loadAjaxPartial('budgetMultiYear', budgetMultiUrl); + loadAjaxPartial('budgetMultiYear', budgetMultiUri); }); function drawChart() { diff --git a/resources/views/reports/default/multi-year.twig b/resources/views/reports/default/multi-year.twig index f48272e92c..39ee4403ab 100644 --- a/resources/views/reports/default/multi-year.twig +++ b/resources/views/reports/default/multi-year.twig @@ -118,9 +118,12 @@ var accountIds = '{{ accountIds }}'; - var accountReportUrl = '{{ route('reports.data.accountReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; - var inOutReportUrl = '{{ route('reports.data.inOutReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; - var budgetMultiUrl = '{{ route('reports.data.budgetMultiYear', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var accountReportUri = '{{ route('reports.data.accountReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var incomeReportUri = '{{ route('reports.data.incomeReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var expenseReportUri = '{{ route('reports.data.expenseReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var incExpReportUri = '{{ route('reports.data.incExpReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + + var budgetMultiUri = '{{ route('reports.data.budgetMultiYear', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; From 5f9a9bc89a48e8296ad03a9426024e4524a04594 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 6 Nov 2016 16:16:05 +0100 Subject: [PATCH 062/709] Change log for 4.1.6 --- CHANGELOG.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95d0a81eb0..8582dae225 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,16 +2,14 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## [4.1.6] - 2016-11-05 +## [4.1.6] - 2016-11-06 ### Added - New budget table for multi year report. - - ### Changed - Greatly expanded help pages and their function. - Built a new transaction collector, which I think was the idea of @roberthorlings originally. - +- Rebuilt seach engine. ### Fixed - #375, thanks to @schoentoon which made it impossible to resurrect currencies. From 69422cc796c514a2dc1b007b808a57bea1cf3287 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 6 Nov 2016 16:17:22 +0100 Subject: [PATCH 063/709] Code for 4.1.6 --- CHANGELOG.md | 14 + app/Console/Commands/Import.php | 2 +- app/Console/Commands/MoveRepository.php | 86 ++++ app/Console/Commands/UpgradeDatabase.php | 2 +- .../Commands/UpgradeFireflyInstructions.php | 15 +- app/Console/Commands/VerifyDatabase.php | 7 +- app/Console/Kernel.php | 3 +- ...llector.php => JournalExportCollector.php} | 6 +- app/Export/Exporter/CsvExporter.php | 1 - app/Export/Processor.php | 8 +- .../Chart/Bill/ChartJsBillChartGenerator.php | 6 +- .../Events/StoredJournalEventHandler.php | 42 +- .../Events/UpdatedJournalEventHandler.php | 47 -- app/Helpers/Attachments/AttachmentHelper.php | 11 +- app/Helpers/Collector/JournalCollector.php | 485 ++++++++++++++++++ app/Helpers/Help/Help.php | 25 +- app/Helpers/Help/HelpInterface.php | 2 +- app/Helpers/Report/BudgetReportHelper.php | 142 ++++- .../Report/BudgetReportHelperInterface.php | 17 + app/Helpers/Report/ReportHelper.php | 146 +----- app/Helpers/Report/ReportHelperInterface.php | 18 - app/Http/Controllers/AccountController.php | 38 +- app/Http/Controllers/BillController.php | 9 +- app/Http/Controllers/BudgetController.php | 47 +- app/Http/Controllers/CategoryController.php | 58 ++- app/Http/Controllers/Chart/BillController.php | 17 +- .../Controllers/Chart/BudgetController.php | 27 +- app/Http/Controllers/Controller.php | 28 - app/Http/Controllers/CurrencyController.php | 2 +- app/Http/Controllers/HelpController.php | 25 +- app/Http/Controllers/HomeController.php | 14 +- app/Http/Controllers/JsonController.php | 15 +- app/Http/Controllers/PiggyBankController.php | 6 + .../Controllers/Popup/ReportController.php | 82 ++- app/Http/Controllers/ProfileController.php | 1 - .../Controllers/Report/BudgetController.php | 29 ++ .../Controllers/Report/InOutController.php | 73 ++- app/Http/Controllers/ReportController.php | 17 +- app/Http/Controllers/RuleController.php | 2 +- app/Http/Controllers/SearchController.php | 23 +- app/Http/Controllers/TagController.php | 25 +- .../Transaction/ConvertController.php | 1 - .../Transaction/SingleController.php | 4 +- .../Controllers/TransactionController.php | 14 +- ...ExecuteRuleGroupOnExistingTransactions.php | 14 +- app/Models/Note.php | 5 +- app/Models/PiggyBank.php | 34 +- app/Providers/EventServiceProvider.php | 1 - app/Providers/FireflyServiceProvider.php | 1 - app/Providers/SearchServiceProvider.php | 59 +++ app/Repositories/Account/AccountTasker.php | 74 +-- .../Account/AccountTaskerInterface.php | 12 - app/Repositories/Bill/BillRepository.php | 60 --- .../Bill/BillRepositoryInterface.php | 23 - app/Repositories/Budget/BudgetRepository.php | 134 +---- .../Budget/BudgetRepositoryInterface.php | 19 - .../Category/CategoryRepository.php | 171 ------ .../Category/CategoryRepositoryInterface.php | 31 -- .../Journal/JournalRepository.php | 2 +- .../Journal/JournalRepositoryInterface.php | 19 +- app/Repositories/Journal/JournalTasker.php | 67 --- .../Journal/JournalTaskerInterface.php | 21 - app/Repositories/Tag/TagRepository.php | 18 - .../Tag/TagRepositoryInterface.php | 7 - app/Rules/Actions/ClearCategory.php | 1 + app/Rules/Processor.php | 31 ++ app/Rules/TransactionMatcher.php | 30 +- app/Support/Amount.php | 12 +- app/Support/Navigation.php | 14 +- app/Support/Preferences.php | 6 +- app/Support/Search/Search.php | 204 ++++++-- app/Support/Twig/Journal.php | 21 +- app/Support/Twig/Transaction.php | 16 +- app/Support/Twig/Translation.php | 2 +- composer.json | 2 + composer.lock | 2 +- config/app.php | 1 + config/firefly.php | 2 +- public/js/ff/reports/default/all.js | 165 ++++-- public/js/ff/reports/default/month.js | 67 +-- public/js/ff/reports/default/multi-year.js | 4 +- public/js/ff/reports/default/year.js | 90 +--- resources/lang/de_DE/csv.php | 3 +- resources/lang/de_DE/firefly.php | 31 +- resources/lang/en_US/csv.php | 3 +- resources/lang/en_US/firefly.php | 31 +- resources/lang/fr_FR/csv.php | 3 +- resources/lang/fr_FR/firefly.php | 31 +- resources/lang/hr_HR/csv.php | 3 +- resources/lang/hr_HR/firefly.php | 31 +- resources/lang/nl_NL/csv.php | 3 +- resources/lang/nl_NL/firefly.php | 31 +- resources/lang/pt_BR/csv.php | 3 +- resources/lang/pt_BR/firefly.php | 31 +- resources/lang/zh_HK/csv.php | 3 +- resources/lang/zh_HK/firefly.php | 31 +- resources/lang/zh_TW/auth.php | 4 +- resources/lang/zh_TW/csv.php | 3 +- resources/lang/zh_TW/firefly.php | 41 +- resources/lang/zh_TW/form.php | 12 +- resources/lang/zh_TW/list.php | 8 +- resources/views/accounts/create.twig | 2 +- resources/views/accounts/delete.twig | 2 +- resources/views/accounts/edit.twig | 2 +- resources/views/accounts/index.twig | 4 +- resources/views/accounts/show.twig | 2 +- resources/views/accounts/show_with_date.twig | 2 +- .../views/admin/configuration/index.twig | 2 +- resources/views/admin/domains/index.twig | 12 +- resources/views/admin/index.twig | 2 +- resources/views/admin/users/index.twig | 26 +- resources/views/admin/users/show.twig | 2 +- resources/views/attachments/delete.twig | 2 +- resources/views/attachments/edit.twig | 2 +- resources/views/auth/confirmation/error.twig | 4 +- .../views/auth/confirmation/no-resent.twig | 4 +- resources/views/auth/confirmation/resent.twig | 4 +- resources/views/auth/login.twig | 2 +- resources/views/auth/lost-two-factor.twig | 4 +- resources/views/auth/passwords/email.twig | 2 +- resources/views/auth/passwords/reset.twig | 2 +- resources/views/auth/register.twig | 2 +- resources/views/auth/two-factor.twig | 2 +- resources/views/bills/create.twig | 2 +- resources/views/bills/delete.twig | 2 +- resources/views/bills/edit.twig | 2 +- resources/views/bills/index.twig | 4 +- resources/views/bills/show.twig | 6 +- resources/views/budgets/create.twig | 2 +- resources/views/budgets/delete.twig | 2 +- resources/views/budgets/edit.twig | 2 +- resources/views/budgets/index.twig | 6 +- .../budgets/{noBudget.twig => no-budget.twig} | 4 +- resources/views/budgets/show.twig | 4 +- resources/views/categories/create.twig | 2 +- resources/views/categories/delete.twig | 2 +- resources/views/categories/edit.twig | 2 +- resources/views/categories/index.twig | 4 +- .../{noCategory.twig => no-category.twig} | 4 +- resources/views/categories/show.twig | 4 +- .../views/categories/show_with_date.twig | 4 +- resources/views/csv/column-roles.twig | 2 +- resources/views/csv/download-config.twig | 2 +- resources/views/csv/index.twig | 2 +- resources/views/csv/map.twig | 2 +- resources/views/csv/process.twig | 2 +- resources/views/currency/create.twig | 2 +- resources/views/currency/delete.twig | 2 +- resources/views/currency/edit.twig | 2 +- resources/views/currency/index.twig | 2 +- resources/views/error.twig | 2 +- resources/views/errors/FireflyException.twig | 4 +- resources/views/export/index.twig | 2 +- resources/views/form/amount.twig | 2 +- resources/views/form/balance.twig | 2 +- resources/views/form/checkbox.twig | 4 +- resources/views/form/date.twig | 4 +- resources/views/form/file.twig | 4 +- resources/views/form/integer.twig | 2 +- resources/views/form/location.twig | 2 +- resources/views/form/multiCheckbox.twig | 4 +- resources/views/form/multiRadio.twig | 4 +- resources/views/form/select.twig | 4 +- resources/views/form/tags.twig | 2 +- resources/views/form/text.twig | 4 +- resources/views/form/textarea.twig | 4 +- resources/views/import/complete.twig | 2 +- resources/views/import/csv/configure.twig | 2 +- resources/views/import/csv/map.twig | 2 +- resources/views/import/csv/roles.twig | 6 +- resources/views/import/finished.twig | 2 +- resources/views/import/index.twig | 17 +- resources/views/import/status.twig | 2 +- resources/views/index.twig | 4 +- resources/views/layout/default.twig | 15 +- resources/views/layout/empty.twig | 4 +- resources/views/layout/guest.twig | 4 +- resources/views/list/journals.twig | 10 +- resources/views/new-user/index.twig | 2 +- resources/views/piggy-banks/add-mobile.twig | 2 +- resources/views/piggy-banks/create.twig | 2 +- resources/views/piggy-banks/delete.twig | 2 +- resources/views/piggy-banks/edit.twig | 2 +- resources/views/piggy-banks/index.twig | 4 +- .../views/piggy-banks/remove-mobile.twig | 2 +- resources/views/piggy-banks/show.twig | 2 +- .../views/popup/report/balance-amount.twig | 2 +- .../popup/report/budget-spent-amount.twig | 2 +- .../views/popup/report/category-entry.twig | 2 +- .../views/popup/report/expense-entry.twig | 2 +- .../views/popup/report/income-entry.twig | 2 +- resources/views/preferences/code.twig | 2 +- resources/views/preferences/index.twig | 2 +- resources/views/profile/change-password.twig | 2 +- resources/views/profile/delete-account.twig | 2 +- resources/views/profile/index.twig | 2 +- resources/views/reminders/index.twig | 2 +- resources/views/reminders/show.twig | 2 +- resources/views/reports/audit/report.twig | 4 +- resources/views/reports/default/month.twig | 19 +- .../views/reports/default/multi-year.twig | 36 +- resources/views/reports/default/year.twig | 13 +- resources/views/reports/index.twig | 2 +- .../reports/partials/budget-multi-year.twig | 33 ++ resources/views/rules/index.twig | 2 +- resources/views/rules/rule-group/create.twig | 2 +- resources/views/rules/rule-group/delete.twig | 2 +- resources/views/rules/rule-group/edit.twig | 2 +- .../rules/rule-group/select-transactions.twig | 2 +- resources/views/rules/rule/create.twig | 4 +- resources/views/rules/rule/delete.twig | 2 +- resources/views/rules/rule/edit.twig | 4 +- resources/views/search/index.twig | 106 ++-- resources/views/search/partials/accounts.twig | 34 ++ resources/views/search/partials/budgets.twig | 22 + .../views/search/partials/categories.twig | 22 + resources/views/search/partials/tags.twig | 24 + .../views/search/partials/transactions.twig | 81 +++ resources/views/tags/create.twig | 2 +- resources/views/tags/delete.twig | 2 +- resources/views/tags/edit.twig | 2 +- resources/views/tags/index.twig | 2 +- resources/views/tags/show.twig | 4 +- resources/views/transactions/convert.twig | 2 +- resources/views/transactions/create.twig | 2 +- resources/views/transactions/delete.twig | 2 +- resources/views/transactions/edit-split.twig | 4 +- resources/views/transactions/edit.twig | 8 +- resources/views/transactions/index.twig | 4 +- resources/views/transactions/mass-delete.twig | 2 +- resources/views/transactions/mass-edit.twig | 2 +- resources/views/transactions/show.twig | 2 +- routes/web.php | 24 +- 233 files changed, 2307 insertions(+), 1889 deletions(-) create mode 100644 app/Console/Commands/MoveRepository.php rename app/Export/Collector/{JournalCollector.php => JournalExportCollector.php} (98%) create mode 100644 app/Helpers/Collector/JournalCollector.php create mode 100644 app/Providers/SearchServiceProvider.php rename resources/views/budgets/{noBudget.twig => no-budget.twig} (83%) rename resources/views/categories/{noCategory.twig => no-category.twig} (84%) create mode 100644 resources/views/reports/partials/budget-multi-year.twig create mode 100644 resources/views/search/partials/accounts.twig create mode 100644 resources/views/search/partials/budgets.twig create mode 100644 resources/views/search/partials/categories.twig create mode 100644 resources/views/search/partials/tags.twig create mode 100644 resources/views/search/partials/transactions.twig diff --git a/CHANGELOG.md b/CHANGELOG.md index ac1c4e09df..8582dae225 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,20 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [4.1.6] - 2016-11-06 +### Added +- New budget table for multi year report. + +### Changed +- Greatly expanded help pages and their function. +- Built a new transaction collector, which I think was the idea of @roberthorlings originally. +- Rebuilt seach engine. + +### Fixed +- #375, thanks to @schoentoon which made it impossible to resurrect currencies. +- #370 thanks to @ksmolder +- #378, thanks to @HomelessAvatar + ## [4.1.5] - 2016-11-01 ### Changed - Report parts are loaded using AJAX, making a lot of code more simple. diff --git a/app/Console/Commands/Import.php b/app/Console/Commands/Import.php index 0459e1c75b..09a1f5bec9 100644 --- a/app/Console/Commands/Import.php +++ b/app/Console/Commands/Import.php @@ -66,7 +66,7 @@ class Import extends Command return; } - $this->line('Going to import job with key "' . $job->key . '" of type ' . $job->file_type); + $this->line(sprintf('Going to import job with key "%s" of type "%s"', $job->key, $job->file_type)); $monolog = Log::getMonolog(); $handler = new CommandHandler($this); diff --git a/app/Console/Commands/MoveRepository.php b/app/Console/Commands/MoveRepository.php new file mode 100644 index 0000000000..51d170d7c3 --- /dev/null +++ b/app/Console/Commands/MoveRepository.php @@ -0,0 +1,86 @@ + $now) { + $this->line('+------------------------------------------------------------------------------+'); + $this->line(''); + $this->line('The Github repository for Firefly III will MOVE'); + $this->line('This move will be on January 1st 2017'); + $this->line(''); + $this->error('READ THIS WIKI PAGE FOR MORE INFORMATION'); + $this->line(''); + $this->info('https://github.com/firefly-iii/help/wiki/New-Github-repository'); + $this->line(''); + $this->line('+------------------------------------------------------------------------------+'); + } + + // display message after 2017-01-01 but before 2017-03-01 + if ($moveDate <= $now && $now <= $final) { + $this->line('+------------------------------------------------------------------------------+'); + $this->line(''); + $this->line('The Github repository for Firefly III has MOVED'); + $this->line('This move was on January 1st 2017!'); + $this->line(''); + $this->error('READ THIS WIKI PAGE FOR MORE INFORMATION'); + $this->line(''); + $this->info('https://github.com/firefly-iii/help/wiki/New-Github-repository'); + $this->line(''); + $this->line('+------------------------------------------------------------------------------+'); + } + + } +} diff --git a/app/Console/Commands/UpgradeDatabase.php b/app/Console/Commands/UpgradeDatabase.php index 5b0ed46a5f..2c1a4dee63 100644 --- a/app/Console/Commands/UpgradeDatabase.php +++ b/app/Console/Commands/UpgradeDatabase.php @@ -99,7 +99,7 @@ class UpgradeDatabase extends Command } catch (QueryException $e) { Log::error($e->getMessage()); $this->error('Firefly III could not find the "identifier" field in the "transactions" table.'); - $this->error('This field is required for Firefly III version ' . config('firefly.version') . ' to run.'); + $this->error(sprintf('This field is required for Firefly III version %s to run.', config('firefly.version'))); $this->error('Please run "php artisan migrate" to add this field to the table.'); $this->info('Then, run "php artisan firefly:upgrade-database" to try again.'); break 2; diff --git a/app/Console/Commands/UpgradeFireflyInstructions.php b/app/Console/Commands/UpgradeFireflyInstructions.php index 2a4a801ade..df007ab40f 100644 --- a/app/Console/Commands/UpgradeFireflyInstructions.php +++ b/app/Console/Commands/UpgradeFireflyInstructions.php @@ -63,21 +63,20 @@ class UpgradeFireflyInstructions extends Command } - $this->line('+------------------------------------------------------------------------------+'); - $this->line(''); if (is_null($text)) { - $this->line('Thank you for installing Firefly III, v' . $version); + $this->line(sprintf('Thank you for installing Firefly III, v%s', $version)); $this->info('There are no extra upgrade instructions.'); $this->line('Firefly III should be ready for use.'); } else { - $this->line('Thank you for installing Firefly III, v' . $version); - $this->line('If you are upgrading from a previous version,'); - $this->line('please follow these upgrade instructions carefully:'); + $this->line('+------------------------------------------------------------------------------+'); + $this->line(''); + $this->line(sprintf('Thank you for installing Firefly III, v%s', $version)); $this->info(wordwrap($text)); + $this->line(''); + $this->line('+------------------------------------------------------------------------------+'); } - $this->line(''); - $this->line('+------------------------------------------------------------------------------+'); + } } diff --git a/app/Console/Commands/VerifyDatabase.php b/app/Console/Commands/VerifyDatabase.php index 8571d599af..6050902ecd 100644 --- a/app/Console/Commands/VerifyDatabase.php +++ b/app/Console/Commands/VerifyDatabase.php @@ -127,8 +127,11 @@ class VerifyDatabase extends Command /** @var stdClass $entry */ foreach ($set as $entry) { - $line = 'Notice: User #' . $entry->user_id . ' (' . $entry->email . ') has budget #' . $entry->id . ' ("' . Crypt::decrypt($entry->name) - . '") which has no budget limits.'; + + $line = sprintf( + 'Notice: User #%d (%s) has budget #%d ("%s") which has no budget limits.', + $entry->user_id, $entry->email, $entry->id, Crypt::decrypt($entry->name) + ); $this->line($line); } } diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index d62f5371ca..7c9c52fc01 100755 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -16,6 +16,7 @@ namespace FireflyIII\Console; use FireflyIII\Console\Commands\CreateImport; use FireflyIII\Console\Commands\EncryptFile; use FireflyIII\Console\Commands\Import; +use FireflyIII\Console\Commands\MoveRepository; use FireflyIII\Console\Commands\ScanAttachments; use FireflyIII\Console\Commands\UpgradeDatabase; use FireflyIII\Console\Commands\UpgradeFireflyInstructions; @@ -63,7 +64,7 @@ class Kernel extends ConsoleKernel EncryptFile::class, ScanAttachments::class, UpgradeDatabase::class, - + MoveRepository::class, ]; /** diff --git a/app/Export/Collector/JournalCollector.php b/app/Export/Collector/JournalExportCollector.php similarity index 98% rename from app/Export/Collector/JournalCollector.php rename to app/Export/Collector/JournalExportCollector.php index 7ea5e7ae5f..927869718b 100644 --- a/app/Export/Collector/JournalCollector.php +++ b/app/Export/Collector/JournalExportCollector.php @@ -1,6 +1,6 @@ job]); + /** @var JournalExportCollector $collector */ + $collector = app(JournalExportCollector::class, [$this->job]); $collector->setDates($this->settings['startDate'], $this->settings['endDate']); $collector->setAccounts($this->settings['accounts']); $collector->run(); diff --git a/app/Generator/Chart/Bill/ChartJsBillChartGenerator.php b/app/Generator/Chart/Bill/ChartJsBillChartGenerator.php index e59b49f136..07f29dd58b 100644 --- a/app/Generator/Chart/Bill/ChartJsBillChartGenerator.php +++ b/app/Generator/Chart/Bill/ChartJsBillChartGenerator.php @@ -14,7 +14,7 @@ declare(strict_types = 1); namespace FireflyIII\Generator\Chart\Bill; use FireflyIII\Models\Bill; -use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\Transaction; use Illuminate\Support\Collection; /** @@ -61,13 +61,13 @@ class ChartJsBillChartGenerator implements BillChartGeneratorInterface $minAmount = []; $maxAmount = []; $actualAmount = []; - /** @var TransactionJournal $entry */ + /** @var Transaction $entry */ foreach ($entries as $entry) { $data['labels'][] = $entry->date->formatLocalized($format); $minAmount[] = round($bill->amount_min, 2); $maxAmount[] = round($bill->amount_max, 2); // journalAmount has been collected in BillRepository::getJournals - $actualAmount[] = round(TransactionJournal::amountPositive($entry), 2); + $actualAmount[] = bcmul($entry->transaction_amount, '-1'); } $data['datasets'][] = [ diff --git a/app/Handlers/Events/StoredJournalEventHandler.php b/app/Handlers/Events/StoredJournalEventHandler.php index 113c542869..29f909b928 100644 --- a/app/Handlers/Events/StoredJournalEventHandler.php +++ b/app/Handlers/Events/StoredJournalEventHandler.php @@ -21,6 +21,7 @@ use FireflyIII\Models\RuleGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Rules\Processor; use FireflyIII\Support\Events\BillScanner; +use Log; /** * Class StoredJournalEventHandler @@ -42,30 +43,69 @@ class StoredJournalEventHandler $journal = $event->journal; $piggyBankId = $event->piggyBankId; + Log::debug(sprintf('Trying to connect journal %d to piggy bank %d.', $journal->id, $piggyBankId)); + /** @var PiggyBank $piggyBank */ $piggyBank = $journal->user->piggyBanks()->where('piggy_banks.id', $piggyBankId)->first(['piggy_banks.*']); if (is_null($piggyBank)) { + Log::error('No such piggy bank!'); + return true; } + Log::debug(sprintf('Found piggy bank #%d: "%s"', $piggyBank->id, $piggyBank->name)); // update piggy bank rep for date of transaction journal. $repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first(); if (is_null($repetition)) { + Log::error(sprintf('No piggy bank repetition on %s!', $journal->date->format('Y-m-d'))); + return true; } $amount = TransactionJournal::amountPositive($journal); + Log::debug(sprintf('Will add/remove %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); // if piggy account matches source account, the amount is positive $sources = TransactionJournal::sourceAccountList($journal)->pluck('id')->toArray(); if (in_array($piggyBank->account_id, $sources)) { $amount = bcmul($amount, '-1'); + Log::debug(sprintf('Account #%d is the source, so will remove amount from piggy bank.', $piggyBank->account_id)); + } + + // if the amount is positive: + // make sure it fits in piggy bank: + if (bccomp($amount, '0') === 1) { + // amount is positive + $room = bcsub(strval($piggyBank->targetamount), strval($repetition->currentamount)); + Log::debug(sprintf('Room in piggy bank for extra money is %f', $room)); + if (bccomp($room, $amount) === -1) { + // $room is smaller than $amount + Log::debug(sprintf('There is NO room to add %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); + Log::debug(sprintf('New amount is %f', $room)); + $amount = $room; + } + } + + if (bccomp($amount, '0') === -1) { + // amount is negative + Log::debug(sprintf('Max amount to remove is %f', $repetition->currentamount)); + $compare = bcmul($repetition->currentamount, '-1'); + if (bccomp($compare, $amount) === 1) { + // $currentamount is smaller than $amount + Log::debug(sprintf('Cannot remove %f from piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); + Log::debug(sprintf('New amount is %f', $compare)); + $amount = $compare; + } } $repetition->currentamount = bcadd($repetition->currentamount, $amount); $repetition->save(); - PiggyBankEvent::create(['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount]); + /** @var PiggyBankEvent $event */ + $event = PiggyBankEvent::create( + ['piggy_bank_id' => $piggyBank->id, 'transaction_journal_id' => $journal->id, 'date' => $journal->date, 'amount' => $amount] + ); + Log::debug(sprintf('Created piggy bank event #%d', $event->id)); return true; } diff --git a/app/Handlers/Events/UpdatedJournalEventHandler.php b/app/Handlers/Events/UpdatedJournalEventHandler.php index 8dbd8ae5f3..60871ebf08 100644 --- a/app/Handlers/Events/UpdatedJournalEventHandler.php +++ b/app/Handlers/Events/UpdatedJournalEventHandler.php @@ -15,11 +15,8 @@ namespace FireflyIII\Handlers\Events; use FireflyIII\Events\UpdatedTransactionJournal; -use FireflyIII\Models\PiggyBankEvent; -use FireflyIII\Models\PiggyBankRepetition; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleGroup; -use FireflyIII\Models\TransactionJournal; use FireflyIII\Rules\Processor; use FireflyIII\Support\Events\BillScanner; @@ -30,50 +27,6 @@ use FireflyIII\Support\Events\BillScanner; */ class UpdatedJournalEventHandler { - /** - * This method will try to reconnect a journal to a piggy bank, updating the piggy bank repetition. - * - * @param UpdatedTransactionJournal $event - * - * @return bool - */ - public function connectToPiggyBank(UpdatedTransactionJournal $event): bool - { - $journal = $event->journal; - - if (!$journal->isTransfer()) { - return true; - } - - // get the event connected to this journal: - /** @var PiggyBankEvent $event */ - $event = PiggyBankEvent::where('transaction_journal_id', $journal->id)->first(); - if (is_null($event)) { - return false; - } - $piggyBank = $event->piggyBank()->first(); - $repetition = null; - if (!is_null($piggyBank)) { - /** @var PiggyBankRepetition $repetition */ - $repetition = $piggyBank->piggyBankRepetitions()->relevantOnDate($journal->date)->first(); - } - - if (is_null($repetition)) { - return false; - } - - $amount = TransactionJournal::amount($journal); - $diff = bcsub($amount, $event->amount); // update current repetition - - $repetition->currentamount = bcadd($repetition->currentamount, $diff); - $repetition->save(); - - - $event->amount = $amount; - $event->save(); - - return true; - } /** * This method will check all the rules when a journal is updated. diff --git a/app/Helpers/Attachments/AttachmentHelper.php b/app/Helpers/Attachments/AttachmentHelper.php index a7bb7c8573..2c7e2d2b60 100644 --- a/app/Helpers/Attachments/AttachmentHelper.php +++ b/app/Helpers/Attachments/AttachmentHelper.php @@ -17,10 +17,8 @@ use FireflyIII\Models\Attachment; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\MessageBag; use Input; -use Log; use Storage; use Symfony\Component\HttpFoundation\File\UploadedFile; -use TypeError; /** * Class AttachmentHelper @@ -236,13 +234,8 @@ class AttachmentHelper implements AttachmentHelperInterface private function getFiles() { $files = null; - try { - if (Input::hasFile('attachments')) { - $files = Input::file('attachments'); - } - } catch (TypeError $e) { - // Log it, do nothing else. - Log::error($e->getMessage()); + if (Input::hasFile('attachments')) { + $files = Input::file('attachments'); } return $files; diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php new file mode 100644 index 0000000000..a1f5c1e854 --- /dev/null +++ b/app/Helpers/Collector/JournalCollector.php @@ -0,0 +1,485 @@ +user = $user; + $this->query = $this->startQuery(); + } + + /** + * @return int + * @throws FireflyException + */ + public function count(): int + { + if ($this->run === true) { + throw new FireflyException('Cannot count after run in JournalCollector.'); + } + + $countQuery = clone $this->query; + + // dont need some fields: + $countQuery->getQuery()->limit = null; + $countQuery->getQuery()->offset = null; + $countQuery->getQuery()->unionLimit = null; + $countQuery->getQuery()->groups = null; + $countQuery->getQuery()->orders = null; + $countQuery->groupBy('accounts.user_id'); + $this->count = $countQuery->count(); + + return $this->count; + } + + /** + * @return Collection + */ + public function getJournals(): Collection + { + $this->run = true; + $set = $this->query->get(array_values($this->fields)); + $set = $this->filterTransfers($set); + + // loop for decryption. + $set->each( + function (Transaction $transaction) { + $transaction->date = new Carbon($transaction->date); + $transaction->description = intval($transaction->encrypted) === 1 ? Crypt::decrypt($transaction->description) : $transaction->description; + $transaction->bill_name = !is_null($transaction->bill_name) ? Crypt::decrypt($transaction->bill_name) : ''; + } + ); + + return $set; + } + + /** + * @return LengthAwarePaginator + * @throws FireflyException + */ + public function getPaginatedJournals():LengthAwarePaginator + { + if ($this->run === true) { + throw new FireflyException('Cannot getPaginatedJournals after run in JournalCollector.'); + } + $this->count(); + $set = $this->getJournals(); + $journals = new LengthAwarePaginator($set, $this->count, $this->limit, $this->page); + + return $journals; + } + + /** + * @param Collection $accounts + * + * @return JournalCollector + */ + public function setAccounts(Collection $accounts): JournalCollector + { + if ($accounts->count() > 0) { + $accountIds = $accounts->pluck('id')->toArray(); + $this->query->whereIn('transactions.account_id', $accountIds); + } + + if ($accounts->count() > 1) { + $this->filterTransfers = true; + } + + return $this; + } + + /** + * @return JournalCollector + */ + public function setAllAssetAccounts(): JournalCollector + { + /** @var AccountRepositoryInterface $repository */ + $repository = app(AccountRepositoryInterface::class, [$this->user]); + $accounts = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]); + if ($accounts->count() > 0) { + $accountIds = $accounts->pluck('id')->toArray(); + $this->query->whereIn('transactions.account_id', $accountIds); + } + + if ($accounts->count() > 1) { + $this->filterTransfers = true; + } + + return $this; + } + + /** + * @param Collection $bills + * + * @return JournalCollector + */ + public function setBills(Collection $bills): JournalCollector + { + if ($bills->count() > 0) { + $billIds = $bills->pluck('id')->toArray(); + $this->query->whereIn('transaction_journals.bill_id', $billIds); + } + + return $this; + + } + + /** + * @param Budget $budget + * + * @return JournalCollector + */ + public function setBudget(Budget $budget): JournalCollector + { + $this->joinBudgetTables(); + + $this->query->where( + function (EloquentBuilder $q) use ($budget) { + $q->where('budget_transaction.budget_id', $budget->id); + $q->orWhere('budget_transaction_journal.budget_id', $budget->id); + } + ); + + return $this; + } + + /** + * @param Category $category + * + * @return JournalCollector + */ + public function setCategory(Category $category): JournalCollector + { + $this->joinCategoryTables(); + + $this->query->where( + function (EloquentBuilder $q) use ($category) { + $q->where('category_transaction.category_id', $category->id); + $q->orWhere('category_transaction_journal.category_id', $category->id); + } + ); + + return $this; + } + + /** + * @param int $limit + * + * @return JournalCollector + */ + public function setLimit(int $limit): JournalCollector + { + $this->limit = $limit; + $this->query->limit($limit); + Log::debug(sprintf('Set limit to %d', $limit)); + + return $this; + } + + /** + * @param int $offset + * + * @return JournalCollector + */ + public function setOffset(int $offset): JournalCollector + { + $this->offset = $offset; + + return $this; + } + + /** + * @param int $page + * + * @return JournalCollector + */ + public function setPage(int $page): JournalCollector + { + $this->page = $page; + + if ($page > 0) { + $page--; + } + Log::debug(sprintf('Page is %d', $page)); + + if (!is_null($this->limit)) { + $offset = ($this->limit * $page); + $this->offset = $offset; + $this->query->skip($offset); + Log::debug(sprintf('Changed offset to %d', $offset)); + } + if (is_null($this->limit)) { + Log::debug('The limit is zero, cannot set the page.'); + } + + return $this; + } + + /** + * @param Carbon $start + * @param Carbon $end + * + * @return JournalCollector + */ + public function setRange(Carbon $start, Carbon $end): JournalCollector + { + if ($start <= $end) { + $this->query->where('transaction_journals.date', '>=', $start->format('Y-m-d')); + $this->query->where('transaction_journals.date', '<=', $end->format('Y-m-d')); + } + + return $this; + } + + /** + * @param Tag $tag + * + * @return JournalCollector + */ + public function setTag(Tag $tag): JournalCollector + { + $this->joinTagTables(); + $this->query->where('tag_transaction_journal.tag_id', $tag->id); + + return $this; + } + + /** + * @param array $types + * + * @return JournalCollector + */ + public function setTypes(array $types): JournalCollector + { + if (count($types) > 0) { + $this->query->whereIn('transaction_types.type', $types); + } + + return $this; + } + + /** + * @return JournalCollector + */ + public function withoutBudget(): JournalCollector + { + $this->joinBudgetTables(); + + $this->query->where( + function (EloquentBuilder $q) { + $q->whereNull('budget_transaction.budget_id'); + $q->whereNull('budget_transaction_journal.budget_id'); + } + ); + + return $this; + } + + /** + * @return JournalCollector + */ + public function withoutCategory(): JournalCollector + { + $this->joinCategoryTables(); + + $this->query->where( + function (EloquentBuilder $q) { + $q->whereNull('category_transaction.category_id'); + $q->whereNull('category_transaction_journal.category_id'); + } + ); + + return $this; + } + + /** + * If the set of accounts used by the collector includes more than one asset + * account, chances are the set include double entries: transfers get selected + * on both the source, and then again on the destination account. + * + * This method filters them out. + * + * @param Collection $set + * + * @return Collection + */ + private function filterTransfers(Collection $set): Collection + { + if ($this->filterTransfers) { + $set = $set->filter( + function (Transaction $transaction) { + if (!($transaction->transaction_type_type === TransactionType::TRANSFER && bccomp($transaction->transaction_amount, '0') === -1)) { + Log::debug( + sprintf( + 'Included journal #%d (transaction #%d) because its a %s with amount %f', + $transaction->transaction_journal_id, + $transaction->id, + $transaction->transaction_type_type, + $transaction->transaction_amount + ) + ); + + return $transaction; + } + + Log::debug( + sprintf( + 'Removed journal #%d (transaction #%d) because its a %s with amount %f', + $transaction->transaction_journal_id, + $transaction->id, + $transaction->transaction_type_type, + $transaction->transaction_amount + ) + ); + + return false; + } + ); + } + + return $set; + } + + /** + * + */ + private function joinBudgetTables() + { + if (!$this->joinedBudget) { + // join some extra tables: + $this->joinedBudget = true; + $this->query->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'); + $this->query->leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'transactions.id'); + } + } + + /** + * + */ + private function joinCategoryTables() + { + if (!$this->joinedCategory) { + // join some extra tables: + $this->joinedCategory = true; + $this->query->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'); + $this->query->leftJoin('category_transaction', 'category_transaction.transaction_id', '=', 'transactions.id'); + } + } + + /** + * + */ + private function joinTagTables() + { + if (!$this->joinedTag) { + // join some extra tables: + $this->joinedTag = true; + $this->query->leftJoin('tag_transaction_journal', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'); + } + } + + /** + * @return EloquentBuilder + */ + private function startQuery(): EloquentBuilder + { + + $query = Transaction + ::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transaction_journals.transaction_currency_id') + ->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id') + ->leftJoin('bills', 'bills.id', 'transaction_journals.bill_id') + ->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id') + ->leftJoin('account_types', 'accounts.account_type_id', 'account_types.id') + ->whereNull('transactions.deleted_at') + ->whereNull('transaction_journals.deleted_at') + ->where('transaction_journals.user_id', $this->user->id) + ->orderBy('transaction_journals.date', 'DESC') + ->orderBy('transaction_journals.order', 'ASC') + ->orderBy('transaction_journals.id', 'DESC'); + + return $query; + + } + +} \ No newline at end of file diff --git a/app/Helpers/Help/Help.php b/app/Helpers/Help/Help.php index 4b19963bef..79e3e7e721 100644 --- a/app/Helpers/Help/Help.php +++ b/app/Helpers/Help/Help.php @@ -26,6 +26,8 @@ use Route; */ class Help implements HelpInterface { + /** @var string */ + protected $userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36'; /** * @param string $route @@ -35,7 +37,9 @@ class Help implements HelpInterface */ public function getFromCache(string $route, string $language): string { - return Cache::get('help.' . $route . '.' . $language); + $line = sprintf('help.%s.%s', $route, $language); + + return Cache::get($line); } /** @@ -49,9 +53,10 @@ class Help implements HelpInterface $uri = sprintf('https://raw.githubusercontent.com/firefly-iii/help/master/%s/%s.md', $language, $route); Log::debug(sprintf('Trying to get %s...', $uri)); + $opt = ['useragent' => $this->userAgent]; $content = ''; try { - $result = Requests::get($uri); + $result = Requests::get($uri, [], $opt); } catch (Requests_Exception $e) { Log::error($e); @@ -69,6 +74,9 @@ class Help implements HelpInterface $converter = new CommonMarkConverter(); $content = $converter->convertToHtml($content); } + if (strlen($content) === 0) { + Log::warning('Raw content length is zero.'); + } return $content; @@ -93,7 +101,8 @@ class Help implements HelpInterface */ public function inCache(string $route, string $language):bool { - $result = Cache::has('help.' . $route . '.' . $language); + $line = sprintf('help.%s.%s', $route, $language); + $result = Cache::has($line); if ($result) { Log::debug(sprintf('Cache has this entry: %s', 'help.' . $route . '.' . $language)); } @@ -115,8 +124,12 @@ class Help implements HelpInterface */ public function putInCache(string $route, string $language, string $content) { - $key = 'help.' . $route . '.' . $language; - Log::debug(sprintf('Will store entry in cache: %s', $key)); - Cache::put($key, $content, 10080); // a week. + $key = sprintf('help.%s.%s', $route, $language); + if (strlen($content) > 0) { + Log::debug(sprintf('Will store entry in cache: %s', $key)); + Cache::put($key, $content, 10080); // a week. + return; + } + Log::info(sprintf('Will not cache %s because content is empty.', $key)); } } diff --git a/app/Helpers/Help/HelpInterface.php b/app/Helpers/Help/HelpInterface.php index 7356ba0ba2..4187ddf1ce 100644 --- a/app/Helpers/Help/HelpInterface.php +++ b/app/Helpers/Help/HelpInterface.php @@ -49,7 +49,7 @@ interface HelpInterface * * @return bool */ - public function inCache(string $route, string $language ): bool; + public function inCache(string $route, string $language): bool; /** * @param string $route diff --git a/app/Helpers/Report/BudgetReportHelper.php b/app/Helpers/Report/BudgetReportHelper.php index 9c250e8560..4f370f89bb 100644 --- a/app/Helpers/Report/BudgetReportHelper.php +++ b/app/Helpers/Report/BudgetReportHelper.php @@ -15,13 +15,17 @@ namespace FireflyIII\Helpers\Report; use Carbon\Carbon; +use DB; use FireflyIII\Helpers\Collection\Budget as BudgetCollection; use FireflyIII\Helpers\Collection\BudgetLine; use FireflyIII\Models\Budget; use FireflyIII\Models\LimitRepetition; +use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Support\CacheProperties; +use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; +use stdClass; /** * Class BudgetReportHelper @@ -97,6 +101,66 @@ class BudgetReportHelper implements BudgetReportHelperInterface return $return; } + /** + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return array + */ + public function getBudgetMultiYear(Carbon $start, Carbon $end, Collection $accounts): array + { + $accountIds = $accounts->pluck('id')->toArray(); + $query = TransactionJournal + ::leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') + ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') + ->leftJoin( + 'transactions', function (JoinClause $join) { + $join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0); + } + ) + ->whereNull('transaction_journals.deleted_at') + ->whereNull('transactions.deleted_at') + ->where('transaction_types.type', 'Withdrawal') + ->where('transaction_journals.user_id', auth()->user()->id); + + if (count($accountIds) > 0) { + $query->whereIn('transactions.account_id', $accountIds); + } + $query->groupBy(['budget_transaction_journal.budget_id', 'the_year']); + $queryResult = $query->get( + [ + 'budget_transaction_journal.budget_id', + DB::raw('DATE_FORMAT(transaction_journals.date,"%Y") AS the_year'), + DB::raw('SUM(transactions.amount) as sum_of_period'), + ] + ); + + $data = []; + $budgets = $this->repository->getBudgets(); + $years = $this->listOfYears($start, $end); + + // do budget "zero" + $emptyBudget = new Budget; + $emptyBudget->id = 0; + $emptyBudget->name = strval(trans('firefly.no_budget')); + $budgets->push($emptyBudget); + + + // get all budgets and years. + foreach ($budgets as $budget) { + $data[$budget->id] = [ + 'name' => $budget->name, + 'entries' => $this->filterAmounts($queryResult, $budget->id, $years), + 'sum' => '0', + ]; + } + // filter out empty ones and fill sum: + $data = $this->getBudgetMultiYearMeta($data); + + return $data; + } + /** * @param Carbon $start * @param Carbon $end @@ -183,30 +247,21 @@ class BudgetReportHelper implements BudgetReportHelperInterface } /** - * Take the array as returned by CategoryRepositoryInterface::spentPerDay and CategoryRepositoryInterface::earnedByDay - * and sum up everything in the array in the given range. - * * @param Carbon $start * @param Carbon $end - * @param array $array * - * @return string + * @return array */ - protected function getSumOfRange(Carbon $start, Carbon $end, array $array) + public function listOfYears(Carbon $start, Carbon $end): array { - $sum = '0'; - $currentStart = clone $start; // to not mess with the original one - $currentEnd = clone $end; // to not mess with the original one - - while ($currentStart <= $currentEnd) { - $date = $currentStart->format('Y-m-d'); - if (isset($array[$date])) { - $sum = bcadd($sum, $array[$date]); - } - $currentStart->addDay(); + $begin = clone $start; + $years = []; + while ($begin < $end) { + $years[] = $begin->year; + $begin->addYear(); } - return $sum; + return $years; } /** @@ -247,6 +302,59 @@ class BudgetReportHelper implements BudgetReportHelperInterface return $headers; } + /** + * @param Collection $set + * @param int $budgetId + * @param array $years + * + * @return array + */ + private function filterAmounts(Collection $set, int $budgetId, array $years):array + { + $arr = []; + foreach ($years as $year) { + /** @var stdClass $object */ + $result = $set->filter( + function (TransactionJournal $object) use ($budgetId, $year) { + return intval($object->the_year) === $year && $budgetId === intval($object->budget_id); + } + ); + $amount = '0'; + if (!is_null($result->first())) { + $amount = $result->first()->sum_of_period; + } + + $arr[$year] = $amount; + } + + return $arr; + } + + /** + * @param array $data + * + * @return array + */ + private function getBudgetMultiYearMeta(array $data): array + { + /** + * @var int $budgetId + * @var array $set + */ + foreach ($data as $budgetId => $set) { + $sum = '0'; + foreach ($set['entries'] as $amount) { + $sum = bcadd($amount, $sum); + } + $data[$budgetId]['sum'] = $sum; + if (bccomp('0', $sum) === 0) { + unset($data[$budgetId]); + } + } + + return $data; + } + /** * @param Carbon $current * @param Carbon $end diff --git a/app/Helpers/Report/BudgetReportHelperInterface.php b/app/Helpers/Report/BudgetReportHelperInterface.php index 3660447c99..89c785b0a8 100644 --- a/app/Helpers/Report/BudgetReportHelperInterface.php +++ b/app/Helpers/Report/BudgetReportHelperInterface.php @@ -34,6 +34,15 @@ interface BudgetReportHelperInterface */ public function budgetYearOverview(Carbon $start, Carbon $end, Collection $accounts): Collection; + /** + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return array + */ + public function getBudgetMultiYear(Carbon $start, Carbon $end, Collection $accounts): array; + /** * @param Carbon $start * @param Carbon $end @@ -52,4 +61,12 @@ interface BudgetReportHelperInterface */ public function getBudgetsWithExpenses(Carbon $start, Carbon $end, Collection $accounts): Collection; + /** + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + public function listOfYears(Carbon $start, Carbon $end): array; + } diff --git a/app/Helpers/Report/ReportHelper.php b/app/Helpers/Report/ReportHelper.php index cd8520e29d..5110e3d6c5 100644 --- a/app/Helpers/Report/ReportHelper.php +++ b/app/Helpers/Report/ReportHelper.php @@ -14,18 +14,17 @@ declare(strict_types = 1); namespace FireflyIII\Helpers\Report; use Carbon\Carbon; -use DB; use FireflyIII\Helpers\Collection\Bill as BillCollection; use FireflyIII\Helpers\Collection\BillLine; use FireflyIII\Helpers\Collection\Category as CategoryCollection; use FireflyIII\Helpers\Collection\Expense; use FireflyIII\Helpers\Collection\Income; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Helpers\FiscalHelperInterface; use FireflyIII\Models\Bill; -use FireflyIII\Models\Budget; use FireflyIII\Models\Category; use FireflyIII\Models\Tag; -use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\Transaction; use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; @@ -79,7 +78,9 @@ class ReportHelper implements ReportHelperInterface /** @var BillRepositoryInterface $repository */ $repository = app(BillRepositoryInterface::class); $bills = $repository->getBillsForAccounts($accounts); - $journals = $repository->getAllJournalsInRange($bills, $start, $end); + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($accounts)->setRange($start, $end)->setBills($bills); + $journals = $collector->getJournals(); $collection = new BillCollection; /** @var Bill $bill */ @@ -93,14 +94,14 @@ class ReportHelper implements ReportHelperInterface // is hit in period? $entry = $journals->filter( - function (TransactionJournal $journal) use ($bill) { - return $journal->bill_id === $bill->id; + function (Transaction $transaction) use ($bill) { + return $transaction->bill_id === $bill->id; } ); $first = $entry->first(); if (!is_null($first)) { $billLine->setTransactionJournalId($first->id); - $billLine->setAmount($first->journalAmount); + $billLine->setAmount($first->transaction_amount); $billLine->setHit(true); } @@ -113,67 +114,6 @@ class ReportHelper implements ReportHelperInterface return $collection; } - /** - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return array - */ - public function getBudgetMultiYear(Carbon $start, Carbon $end, Collection $accounts): array - { - $accountIds = $accounts->pluck('id')->toArray(); - $query = TransactionJournal - ::leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') - ->leftJoin( - 'transactions', function (JoinClause $join) { - $join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0); - } - ) - ->whereNull('transaction_journals.deleted_at') - ->whereNull('transactions.deleted_at') - ->where('transaction_types.type', 'Withdrawal') - ->where('transaction_journals.user_id', auth()->user()->id); - - if (count($accountIds) > 0) { - $query->whereIn('transactions.account_id', $accountIds); - } - $query->groupBy(['budget_transaction_journal.budget_id', 'the_year']); - $queryResult = $query->get( - [ - 'budget_transaction_journal.budget_id', - DB::raw('DATE_FORMAT(transaction_journals.date,"%Y") AS the_year'), - DB::raw('SUM(transactions.amount) as sum_of_period'), - ] - ); - - $data = []; - $budgets = $this->budgetRepository->getBudgets(); - $years = $this->listOfYears($start, $end); - - // do budget "zero" - $emptyBudget = new Budget; - $emptyBudget->id = 0; - $emptyBudget->name = strval(trans('firefly.no_budget')); - $budgets->push($emptyBudget); - - - // get all budgets and years. - foreach ($budgets as $budget) { - $data[$budget->id] = [ - 'name' => $budget->name, - 'entries' => [], - ]; - foreach ($years as $year) { - // filter query result here! - $data[$budget->id]['entries'][$year] = $this->filterAmount($queryResult, $budget->id, $year); - } - } - - return $data; - } - /** * @param Carbon $start * @param Carbon $end @@ -294,24 +234,6 @@ class ReportHelper implements ReportHelperInterface return $months; } - /** - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function listOfYears(Carbon $start, Carbon $end): array - { - $begin = clone $start; - $years = []; - while ($begin < $end) { - $years[] = $begin->year; - $begin->addYear(); - } - - return $years; - } - /** * Returns an array of tags and their comparitive size with amounts bla bla. * @@ -386,56 +308,4 @@ class ReportHelper implements ReportHelperInterface return $collection; } - /** - * @param Collection $set - * @param int $budgetId - * @param int $year - * - * @return string - */ - protected function filterAmount(Collection $set, int $budgetId, int $year): string - { - /** @var stdClass $object */ - $result = $set->filter( - function (TransactionJournal $object) use ($budgetId, $year) { - return intval($object->the_year) === $year && $budgetId === intval($object->budget_id); - } - ); - $amount = '0'; - if (!is_null($result->first())) { - $amount = $result->first()->sum_of_period; - } - - return $amount; - - } - - /** - * Take the array as returned by CategoryRepositoryInterface::spentPerDay and CategoryRepositoryInterface::earnedByDay - * and sum up everything in the array in the given range. - * - * @param Carbon $start - * @param Carbon $end - * @param array $array - * - * @return string - */ - protected function getSumOfRange(Carbon $start, Carbon $end, array $array) - { - $sum = '0'; - $currentStart = clone $start; // to not mess with the original one - $currentEnd = clone $end; // to not mess with the original one - - while ($currentStart <= $currentEnd) { - $date = $currentStart->format('Y-m-d'); - if (isset($array[$date])) { - $sum = bcadd($sum, $array[$date]); - } - $currentStart->addDay(); - } - - return $sum; - } - - } diff --git a/app/Helpers/Report/ReportHelperInterface.php b/app/Helpers/Report/ReportHelperInterface.php index 6dec1b9c0e..34ffcc9e7b 100644 --- a/app/Helpers/Report/ReportHelperInterface.php +++ b/app/Helpers/Report/ReportHelperInterface.php @@ -28,16 +28,6 @@ use Illuminate\Support\Collection; interface ReportHelperInterface { - /** - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return array - */ - public function getBudgetMultiYear(Carbon $start, Carbon $end, Collection $accounts): array; - - /** * This method generates a full report for the given period on all * the users bills and their payments. @@ -90,14 +80,6 @@ interface ReportHelperInterface */ public function listOfMonths(Carbon $date): array; - /** - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function listOfYears(Carbon $start, Carbon $end): array; - /** * Returns an array of tags and their comparitive size with amounts bla bla. * diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 09fe795a2d..750e90d53a 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -16,6 +16,7 @@ namespace FireflyIII\Http\Controllers; use Carbon\Carbon; use ExpandedForm; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Http\Requests\AccountFormRequest; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; @@ -23,7 +24,6 @@ use FireflyIII\Models\Transaction; use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Support\CacheProperties; -use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Input; use Navigation; @@ -221,13 +221,13 @@ class AccountController extends Controller $start = session('start', Navigation::startOfPeriod(new Carbon, $range)); /** @var Carbon $end */ $end = session('end', Navigation::endOfPeriod(new Carbon, $range)); - $page = intval(Input::get('page')); - $pageSize = Preferences::get('transactionPageSize', 50)->data; - $offset = ($page - 1) * $pageSize; - $set = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end); - $count = $set->count(); - $subSet = $set->splice($offset, $pageSize); - $journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page); + $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); + + // replace with journal collector: + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page); + $journals = $collector->getPaginatedJournals(); $journals->setPath('accounts/show/' . $account->id); // grouped other months thing: @@ -275,27 +275,25 @@ class AccountController extends Controller } /** - * @param AccountTaskerInterface $tasker - * @param Account $account - * @param string $date + * @param Account $account + * @param string $date * * @return View */ - public function showWithDate(AccountTaskerInterface $tasker, Account $account, string $date) + public function showWithDate(Account $account, string $date) { $carbon = new Carbon($date); $range = Preferences::get('viewRange', '1M')->data; $start = Navigation::startOfPeriod($carbon, $range); $end = Navigation::endOfPeriod($carbon, $range); $subTitle = $account->name . ' (' . Navigation::periodShow($start, $range) . ')'; - $page = intval(Input::get('page')); - $page = $page === 0 ? 1 : $page; - $pageSize = Preferences::get('transactionPageSize', 50)->data; - $offset = ($page - 1) * $pageSize; - $set = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end); - $count = $set->count(); - $subSet = $set->splice($offset, $pageSize); - $journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page); + $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); + + // replace with journal collector: + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page); + $journals = $collector->getPaginatedJournals(); $journals->setPath('accounts/show/' . $account->id . '/' . $date); return view('accounts.show_with_date', compact('category', 'date', 'account', 'journals', 'subTitle', 'carbon')); diff --git a/app/Http/Controllers/BillController.php b/app/Http/Controllers/BillController.php index 31de91d724..4cccbdeba3 100644 --- a/app/Http/Controllers/BillController.php +++ b/app/Http/Controllers/BillController.php @@ -14,10 +14,12 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; use Carbon\Carbon; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Http\Requests\BillFormRequest; use FireflyIII\Models\Bill; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Bill\BillRepositoryInterface; +use Illuminate\Support\Collection; use Input; use Preferences; use Session; @@ -200,10 +202,15 @@ class BillController extends Controller $year = $date->year; $page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); - $journals = $repository->getJournals($bill, $page, $pageSize); $yearAverage = $repository->getYearAverage($bill, $date); $overallAverage = $repository->getOverallAverage($bill); + + // use collector: + $collector = new JournalCollector(auth()->user()); + $collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->setPage($page)->setLimit($pageSize); + $journals = $collector->getPaginatedJournals(); $journals->setPath('/bills/show/' . $bill->id); + $bill->nextExpectedMatch = $repository->nextExpectedMatch($bill, new Carbon); $hideBill = true; $subTitle = e($bill->name); diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index b2ed2af41f..d8f18646e4 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -17,13 +17,13 @@ use Amount; use Carbon\Carbon; use Config; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Http\Requests\BudgetFormRequest; use FireflyIII\Models\AccountType; use FireflyIII\Models\Budget; use FireflyIII\Models\LimitRepetition; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; -use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Input; use Log; @@ -247,31 +247,28 @@ class BudgetController extends Controller } /** - * @param BudgetRepositoryInterface $repository - * * @return View */ - public function noBudget(BudgetRepositoryInterface $repository) + public function noBudget() { /** @var Carbon $start */ $start = session('start', Carbon::now()->startOfMonth()); /** @var Carbon $end */ - $end = session('end', Carbon::now()->endOfMonth()); - + $end = session('end', Carbon::now()->endOfMonth()); $page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page')); - $pageSize = Preferences::get('transactionPageSize', 50)->data; - $offset = ($page - 1) * $pageSize; - $journals = $repository->journalsInPeriodWithoutBudget(new Collection, $start, $end); // budget - $count = $journals->count(); - $journals = $journals->slice($offset, $pageSize); - $list = new LengthAwarePaginator($journals, $count, $pageSize); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $subTitle = trans( 'firefly.without_budget_between', ['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)] ); - $list->setPath('/budgets/list/noBudget'); - return view('budgets.noBudget', compact('list', 'subTitle')); + // collector + $collector = new JournalCollector(auth()->user()); + $collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withoutBudget(); + $journals = $collector->getPaginatedJournals(); + $journals->setPath('/budgets/list/noBudget'); + + return view('budgets.no-budget', compact('journals', 'subTitle')); } /** @@ -305,14 +302,13 @@ class BudgetController extends Controller $start = session('first', Carbon::create()->startOfYear()); $end = new Carbon; $page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page')); - $pageSize = Preferences::get('transactionPageSize', 50)->data; - $offset = ($page - 1) * $pageSize; - $journals = $repository->journalsInPeriod(new Collection([$budget]), new Collection, $start, $end); // budget - $count = $journals->count(); - $journals = $journals->slice($offset, $pageSize); - $journals = new LengthAwarePaginator($journals, $count, $pageSize); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]); + // collector: + $collector = new JournalCollector(auth()->user()); + $collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page); + $journals = $collector->getPaginatedJournals(); $journals->setPath('/budgets/show/' . $budget->id); @@ -347,16 +343,15 @@ class BudgetController extends Controller $start = $repetition->startdate; $end = $repetition->enddate; $page = intval(Input::get('page')) == 0 ? 1 : intval(Input::get('page')); - $pageSize = Preferences::get('transactionPageSize', 50)->data; - $offset = ($page - 1) * $pageSize; - $journals = $repository->journalsInPeriod(new Collection([$budget]), new Collection, $start, $end); // budget - $count = $journals->count(); - $journals = $journals->slice($offset, $pageSize); - $journals = new LengthAwarePaginator($journals, $count, $pageSize); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $subTitle = trans('firefly.budget_in_month', ['name' => $budget->name, 'month' => $repetition->startdate->formatLocalized($this->monthFormat)]); $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]); + // collector: + $collector = new JournalCollector(auth()->user()); + $collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page); + $journals = $collector->getPaginatedJournals(); $journals->setPath('/budgets/show/' . $budget->id . '/' . $repetition->id); diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index f80d3a16bc..7068130116 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -14,13 +14,13 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; use Carbon\Carbon; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Http\Requests\CategoryFormRequest; use FireflyIII\Models\AccountType; use FireflyIII\Models\Category; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI; use FireflyIII\Support\CacheProperties; -use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Input; use Navigation; @@ -147,23 +147,25 @@ class CategoryController extends Controller } /** - * @param CRI $repository - * * @return View */ - public function noCategory(CRI $repository) + public function noCategory() { /** @var Carbon $start */ $start = session('start', Carbon::now()->startOfMonth()); /** @var Carbon $end */ - $end = session('end', Carbon::now()->startOfMonth()); - $list = $repository->journalsInPeriodWithoutCategory(new Collection(), [], $start, $end); // category + $end = session('end', Carbon::now()->startOfMonth()); + + // new collector: + $collector = new JournalCollector(auth()->user()); + $collector->setAllAssetAccounts()->setRange($start, $end)->withoutCategory();//->groupJournals(); + $journals = $collector->getJournals(); $subTitle = trans( 'firefly.without_category_between', ['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)] ); - return view('categories.noCategory', compact('list', 'subTitle')); + return view('categories.no-category', compact('journals', 'subTitle')); } /** @@ -180,16 +182,17 @@ class CategoryController extends Controller $start = session('start', Navigation::startOfPeriod(new Carbon, $range)); /** @var Carbon $end */ $end = session('end', Navigation::endOfPeriod(new Carbon, $range)); + $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); $hideCategory = true; // used in list. - $page = intval(Input::get('page')); - $pageSize = Preferences::get('transactionPageSize', 50)->data; - $offset = ($page - 1) * $pageSize; - $set = $repository->journalsInPeriod(new Collection([$category]), new Collection, [], $start, $end); // category - $count = $set->count(); - $subSet = $set->splice($offset, $pageSize); + $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $subTitle = $category->name; $subTitleIcon = 'fa-bar-chart'; - $journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page); + + // use journal collector + $collector = new JournalCollector(auth()->user()); + $collector->setPage($page)->setLimit($pageSize)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category); + $journals = $collector->getPaginatedJournals(); $journals->setPath('categories/show/' . $category->id); // oldest transaction in category: @@ -218,7 +221,7 @@ class CategoryController extends Controller $categoryCollection = new Collection([$category]); - $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); + while ($end >= $start) { $end = Navigation::startOfPeriod($end, $range); $currentEnd = Navigation::endOfPeriod($end, $range); @@ -233,18 +236,16 @@ class CategoryController extends Controller } $cache->store($entries); - return view('categories.show', compact('category', 'journals', 'entries', 'hideCategory', 'subTitle')); + return view('categories.show', compact('category', 'journals', 'entries', 'hideCategory', 'subTitle', 'subTitleIcon')); } /** - * @param CRI $repository - * @param Category $category - * - * @param $date + * @param Category $category + * @param $date * * @return View */ - public function showWithDate(CRI $repository, Category $category, string $date) + public function showWithDate(Category $category, string $date) { $carbon = new Carbon($date); $range = Preferences::get('viewRange', '1M')->data; @@ -252,15 +253,16 @@ class CategoryController extends Controller $end = Navigation::endOfPeriod($carbon, $range); $subTitle = $category->name; $hideCategory = true; // used in list. - $page = intval(Input::get('page')); - $pageSize = Preferences::get('transactionPageSize', 50)->data; - $offset = ($page - 1) * $pageSize; - $set = $repository->journalsInPeriod(new Collection([$category]), new Collection, [], $start, $end); // category - $count = $set->count(); - $subSet = $set->splice($offset, $pageSize); - $journals = new LengthAwarePaginator($subSet, $count, $pageSize, $page); + $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); + + // new collector: + $collector = new JournalCollector(auth()->user()); + $collector->setPage($page)->setLimit($pageSize)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category); + $journals = $collector->getPaginatedJournals(); $journals->setPath('categories/show/' . $category->id . '/' . $date); + return view('categories.show_with_date', compact('category', 'journals', 'hideCategory', 'subTitle', 'carbon')); } diff --git a/app/Http/Controllers/Chart/BillController.php b/app/Http/Controllers/Chart/BillController.php index 78385b915b..51ca561e75 100644 --- a/app/Http/Controllers/Chart/BillController.php +++ b/app/Http/Controllers/Chart/BillController.php @@ -15,11 +15,13 @@ namespace FireflyIII\Http\Controllers\Chart; use Carbon\Carbon; use FireflyIII\Generator\Chart\Bill\BillChartGeneratorInterface; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Bill; -use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\Transaction; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Support\CacheProperties; +use Illuminate\Support\Collection; use Response; /** @@ -64,12 +66,11 @@ class BillController extends Controller /** * Shows the overview for a bill. The min/max amount and matched journals. * - * @param BillRepositoryInterface $repository - * @param Bill $bill + * @param Bill $bill * * @return \Symfony\Component\HttpFoundation\Response */ - public function single(BillRepositoryInterface $repository, Bill $bill) + public function single(Bill $bill) { $cache = new CacheProperties; $cache->addProperty('single'); @@ -80,12 +81,14 @@ class BillController extends Controller } // get first transaction or today for start: - $results = $repository->getJournals($bill, 1, 200); + $collector = new JournalCollector(auth()->user()); + $collector->setAllAssetAccounts()->setBills(new Collection([$bill])); + $results = $collector->getJournals(); // resort: $results = $results->sortBy( - function (TransactionJournal $journal) { - return $journal->date->format('U'); + function (Transaction $transaction) { + return $transaction->date->format('U'); } ); diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index 3e6c5f1c83..43536e67cc 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -15,10 +15,12 @@ namespace FireflyIII\Http\Controllers\Chart; use Carbon\Carbon; use FireflyIII\Generator\Chart\Budget\BudgetChartGeneratorInterface; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Budget; use FireflyIII\Models\LimitRepetition; -use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\Transaction; +use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; @@ -175,7 +177,7 @@ class BudgetController extends Controller $allEntries = $allEntries->merge($collection); } - $entry = $this->spentInPeriodWithout($repository, $start, $end); + $entry = $this->spentInPeriodWithout($start, $end); $allEntries->push($entry); $data = $this->generator->frontpage($allEntries); $cache->store($data); @@ -319,19 +321,22 @@ class BudgetController extends Controller } /** - * @param BudgetRepositoryInterface $repository - * @param Carbon $start - * @param Carbon $end + * @param Carbon $start + * @param Carbon $end * * @return array */ - private function spentInPeriodWithout(BudgetRepositoryInterface $repository, Carbon $start, Carbon $end):array + private function spentInPeriodWithout(Carbon $start, Carbon $end):array { - $list = $repository->journalsInPeriodWithoutBudget(new Collection, $start, $end); // budget - $sum = '0'; - /** @var TransactionJournal $entry */ - foreach ($list as $entry) { - $sum = bcadd(TransactionJournal::amount($entry), $sum); + // collector + $collector = new JournalCollector(auth()->user()); + $types = [TransactionType::WITHDRAWAL]; + $collector->setAllAssetAccounts()->setTypes($types)->setRange($start, $end)->withoutBudget(); + $journals = $collector->getJournals(); + $sum = '0'; + /** @var Transaction $entry */ + foreach ($journals as $entry) { + $sum = bcadd($entry->transaction_amount, $sum); } return [trans('firefly.no_budget'), '0', '0', $sum, '0', '0']; diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 58917455dc..6109a79967 100755 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -13,7 +13,6 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; -use Carbon\Carbon; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; @@ -61,31 +60,4 @@ class Controller extends BaseController } - /** - * Take the array as returned by CategoryRepositoryInterface::spentPerDay and CategoryRepositoryInterface::earnedByDay - * and sum up everything in the array in the given range. - * - * @param Carbon $start - * @param Carbon $end - * @param array $array - * - * @return string - */ - protected function getSumOfRange(Carbon $start, Carbon $end, array $array) - { - $sum = '0'; - $currentStart = clone $start; // to not mess with the original one - $currentEnd = clone $end; // to not mess with the original one - - while ($currentStart <= $currentEnd) { - $date = $currentStart->format('Y-m-d'); - if (isset($array[$date])) { - $sum = bcadd($sum, $array[$date]); - } - $currentStart->addDay(); - } - - return $sum; - } - } diff --git a/app/Http/Controllers/CurrencyController.php b/app/Http/Controllers/CurrencyController.php index e1abc73e27..19ccf48434 100644 --- a/app/Http/Controllers/CurrencyController.php +++ b/app/Http/Controllers/CurrencyController.php @@ -129,7 +129,7 @@ class CurrencyController extends Controller Session::flash('success', trans('firefly.deleted_currency', ['name' => $currency->name])); if (auth()->user()->hasRole('owner')) { - $currency->delete(); + $currency->forceDelete(); } return redirect(session('currency.delete.url')); diff --git a/app/Http/Controllers/HelpController.php b/app/Http/Controllers/HelpController.php index bd1f34c581..24d2d75254 100644 --- a/app/Http/Controllers/HelpController.php +++ b/app/Http/Controllers/HelpController.php @@ -42,9 +42,8 @@ class HelpController extends Controller public function show(HelpInterface $help, string $route) { - $language = Preferences::get('language', config('firefly.default_language', 'en_US'))->data; - $content = '

' . strval(trans('firefly.route_has_no_help')) . '

'; - $alternative = false; + $language = Preferences::get('language', config('firefly.default_language', 'en_US'))->data; + $content = '

' . strval(trans('firefly.route_has_no_help')) . '

'; if (!$help->hasRoute($route)) { Log::error('No such route: ' . $route); @@ -54,7 +53,7 @@ class HelpController extends Controller if ($help->inCache($route, $language)) { $content = $help->getFromCache($route, $language); - Log::debug('Help text was in cache.'); + Log::debug(sprintf('Help text %s was in cache.', $language)); return Response::json($content); } @@ -63,19 +62,21 @@ class HelpController extends Controller // get backup language content (try English): if (strlen($content) === 0) { - $language = 'en_US'; - $content = $help->getFromGithub($language, $route); - $alternative = true; - } - - if ($alternative && strlen($content) > 0) { - $content = '

' . strval(trans('firefly.help_may_not_be_your_language')) . '

' . $content; + $language = 'en_US'; + if ($help->inCache($route, $language)) { + Log::debug(sprintf('Help text %s was in cache.', $language)); + $content = $help->getFromCache($route, $language); + } + if (!$help->inCache($route, $language)) { + $content = $help->getFromGithub($language, $route); + $content = '

' . strval(trans('firefly.help_may_not_be_your_language')) . '

' . $content; + } } if (strlen($content) === 0) { $content = '

' . strval(trans('firefly.route_has_no_help')) . '

'; } - + $help->putInCache($route, $language, $content); return Response::json($content); diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 05f3daa755..8bf0a6c6e6 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -15,10 +15,10 @@ namespace FireflyIII\Http\Controllers; use Artisan; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Models\AccountType; use FireflyIII\Models\Tag; use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; -use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use Illuminate\Http\Request; use Illuminate\Support\Collection; @@ -115,12 +115,11 @@ class HomeController extends Controller } /** - * @param ARI $repository - * @param AccountTaskerInterface $tasker + * @param ARI $repository * - * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View */ - public function index(ARI $repository, AccountTaskerInterface $tasker) + public function index(ARI $repository) { $types = config('firefly.accountTypesByIdentifier.asset'); @@ -144,8 +143,9 @@ class HomeController extends Controller $showDepositsFrontpage = Preferences::get('showDepositsFrontpage', false)->data; foreach ($accounts as $account) { - $set = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end); - $set = $set->splice(0, 10); + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit(10)->setPage(1); + $set = $collector->getJournals(); if (count($set) > 0) { $transactions[] = [$set, $account]; diff --git a/app/Http/Controllers/JsonController.php b/app/Http/Controllers/JsonController.php index 5eea913966..33beceec79 100644 --- a/app/Http/Controllers/JsonController.php +++ b/app/Http/Controllers/JsonController.php @@ -15,12 +15,12 @@ namespace FireflyIII\Http\Controllers; use Amount; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Models\AccountType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI; -use FireflyIII\Repositories\Journal\JournalTaskerInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Support\CacheProperties; use Input; @@ -270,17 +270,20 @@ class JsonController extends Controller } /** - * @param JournalTaskerInterface $tasker - * @param $what + * @param $what * - * @return \Symfony\Component\HttpFoundation\Response + * @return \Illuminate\Http\JsonResponse */ - public function transactionJournals(JournalTaskerInterface $tasker, $what) + public function transactionJournals($what) { $descriptions = []; $type = config('firefly.transactionTypesByWhat.' . $what); $types = [$type]; - $journals = $tasker->getJournals($types, 1, 50); + + // use journal collector instead: + $collector = new JournalCollector(auth()->user()); + $collector->setTypes($types)->setLimit(100)->setPage(1); + $journals = $collector->getJournals(); foreach ($journals as $j) { $descriptions[] = $j->description; } diff --git a/app/Http/Controllers/PiggyBankController.php b/app/Http/Controllers/PiggyBankController.php index 15c98f6e5e..7239ba309a 100644 --- a/app/Http/Controllers/PiggyBankController.php +++ b/app/Http/Controllers/PiggyBankController.php @@ -107,6 +107,12 @@ class PiggyBankController extends Controller $subTitle = trans('firefly.new_piggy_bank'); $subTitleIcon = 'fa-plus'; + if (count($accounts) === 0) { + Session::flash('error', strval(trans('firefly.need_at_least_one_account'))); + + return redirect(route('new-user.index')); + } + // put previous url in session if not redirect from store (not "create another"). if (session('piggy-banks.create.fromStore') !== true) { Session::put('piggy-banks.create.url', URL::previous()); diff --git a/app/Http/Controllers/Popup/ReportController.php b/app/Http/Controllers/Popup/ReportController.php index 0b4df645ba..c9fd9afe8c 100644 --- a/app/Http/Controllers/Popup/ReportController.php +++ b/app/Http/Controllers/Popup/ReportController.php @@ -17,12 +17,12 @@ namespace FireflyIII\Http\Controllers\Popup; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collection\BalanceLine; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Support\Binder\AccountList; @@ -98,20 +98,39 @@ class ReportController extends Controller $repository = app(AccountRepositoryInterface::class); $account = $repository->find(intval($attributes['accountId'])); + $types = [TransactionType::WITHDRAWAL]; switch (true) { case ($role === BalanceLine::ROLE_DEFAULTROLE && !is_null($budget->id)): - $journals = $budgetRepository->journalsInPeriod( - new Collection([$budget]), new Collection([$account]), $attributes['startDate'], $attributes['endDate'] - ); + $collector = new JournalCollector(auth()->user()); + $collector + ->setAccounts(new Collection([$account])) + ->setRange($attributes['startDate'], $attributes['endDate']) + ->setBudget($budget); + $journals = $collector->getJournals(); + break; case ($role === BalanceLine::ROLE_DEFAULTROLE && is_null($budget->id)): $budget->name = strval(trans('firefly.no_budget')); - $journals = $budgetRepository->journalsInPeriodWithoutBudget($attributes['accounts'], $attributes['startDate'], $attributes['endDate']); + // collector + $collector = new JournalCollector(auth()->user()); + $collector + ->setAccounts(new Collection([$account])) + ->setTypes($types) + ->setRange($attributes['startDate'], $attributes['endDate']) + ->withoutBudget(); + $journals = $collector->getJournals(); break; case ($role === BalanceLine::ROLE_DIFFROLE): // journals no budget, not corrected by a tag. - $journals = $budgetRepository->journalsInPeriodWithoutBudget($attributes['accounts'], $attributes['startDate'], $attributes['endDate']); + $collector = new JournalCollector(auth()->user()); + $collector + ->setAccounts(new Collection([$account])) + ->setTypes($types) + ->setRange($attributes['startDate'], $attributes['endDate']) + ->withoutBudget(); + $journals = $collector->getJournals(); + $budget->name = strval(trans('firefly.leftUnbalanced')); $journals = $journals->filter( function (TransactionJournal $journal) { @@ -148,14 +167,21 @@ class ReportController extends Controller /** @var BudgetRepositoryInterface $repository */ $repository = app(BudgetRepositoryInterface::class); $budget = $repository->find(intval($attributes['budgetId'])); - if (is_null($budget->id)) { - $journals = $repository->journalsInPeriodWithoutBudget($attributes['accounts'], $attributes['startDate'], $attributes['endDate']); - } else { - // get all expenses in budget in period: - $journals = $repository->journalsInPeriod(new Collection([$budget]), $attributes['accounts'], $attributes['startDate'], $attributes['endDate']); - } + $collector = new JournalCollector(auth()->user()); - $view = view('popup.report.budget-spent-amount', compact('journals', 'budget'))->render(); + $collector + ->setAccounts($attributes['accounts']) + ->setRange($attributes['startDate'], $attributes['endDate']); + + if (is_null($budget->id)) { + $collector->setTypes([TransactionType::WITHDRAWAL])->withoutBudget(); + } + if (!is_null($budget->id)) { + // get all expenses in budget in period: + $collector->setBudget($budget); + } + $journals = $collector->getJournals(); + $view = view('popup.report.budget-spent-amount', compact('journals', 'budget'))->render(); return $view; } @@ -174,10 +200,14 @@ class ReportController extends Controller $repository = app(CategoryRepositoryInterface::class); $category = $repository->find(intval($attributes['categoryId'])); $types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]; - $journals = $repository->journalsInPeriod( - new Collection([$category]), $attributes['accounts'], $types, $attributes['startDate'], $attributes['endDate'] - ); - $view = view('popup.report.category-entry', compact('journals', 'category'))->render(); + // get journal collector instead: + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($attributes['accounts'])->setTypes($types) + ->setRange($attributes['startDate'], $attributes['endDate']) + ->setCategory($category); + $journals = $collector->getJournals(); // 7193 + + $view = view('popup.report.category-entry', compact('journals', 'category'))->render(); return $view; } @@ -192,14 +222,14 @@ class ReportController extends Controller */ private function expenseEntry(array $attributes): string { - /** @var AccountTaskerInterface $tasker */ - $tasker = app(AccountTaskerInterface::class); /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); - $account = $repository->find(intval($attributes['accountId'])); - $types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]; - $journals = $tasker->getJournalsInPeriod(new Collection([$account]), $types, $attributes['startDate'], $attributes['endDate']); + $account = $repository->find(intval($attributes['accountId'])); + $types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]; + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setTypes($types); + $journals = $collector->getJournals(); $report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report // filter for transfers and withdrawals TO the given $account @@ -228,14 +258,14 @@ class ReportController extends Controller */ private function incomeEntry(array $attributes): string { - /** @var AccountTaskerInterface $tasker */ - $tasker = app(AccountTaskerInterface::class); /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); $account = $repository->find(intval($attributes['accountId'])); $types = [TransactionType::DEPOSIT, TransactionType::TRANSFER]; - $journals = $tasker->getJournalsInPeriod(new Collection([$account]), $types, $attributes['startDate'], $attributes['endDate']); - $report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setTypes($types); + $journals = $collector->getJournals(); + $report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report // filter the set so the destinations outside of $attributes['accounts'] are not included. $journals = $journals->filter( diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 23c42d3c1e..c10c1701ac 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -13,7 +13,6 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; -use FireflyIII\Events\DeletedUser; use FireflyIII\Http\Requests\DeleteAccountFormRequest; use FireflyIII\Http\Requests\ProfileFormRequest; use FireflyIII\User; diff --git a/app/Http/Controllers/Report/BudgetController.php b/app/Http/Controllers/Report/BudgetController.php index 1d139c23ca..c000f86a24 100644 --- a/app/Http/Controllers/Report/BudgetController.php +++ b/app/Http/Controllers/Report/BudgetController.php @@ -28,6 +28,35 @@ use Illuminate\Support\Collection; class BudgetController extends Controller { + /** + * @param BudgetReportHelperInterface $helper + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return mixed|string + */ + public function budgetMultiYear(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts) + { + $cache = new CacheProperties; + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('budget-mult-year-report'); + $cache->addProperty($accounts->pluck('id')->toArray()); + if ($cache->has()) { + return $cache->get(); + } + + + $years = $helper->listOfYears($start, $end); + $budgetMultiYear = $helper->getBudgetMultiYear($start, $end, $accounts); + + $result = view('reports.partials.budget-multi-year', compact('budgetMultiYear', 'years'))->render(); + $cache->store($result); + + return $result; + } + /** * @param BudgetReportHelperInterface $helper * @param Carbon $start diff --git a/app/Http/Controllers/Report/InOutController.php b/app/Http/Controllers/Report/InOutController.php index abc23c55fb..83ce241de1 100644 --- a/app/Http/Controllers/Report/InOutController.php +++ b/app/Http/Controllers/Report/InOutController.php @@ -19,7 +19,6 @@ use FireflyIII\Helpers\Report\ReportHelperInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; -use Response; /** * Class InOutController @@ -37,29 +36,83 @@ class InOutController extends Controller * * @return \Illuminate\Http\JsonResponse */ - public function inOutReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts) + public function expenseReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts) { // chart properties for cache: $cache = new CacheProperties; $cache->addProperty($start); $cache->addProperty($end); - $cache->addProperty('in-out-report'); + $cache->addProperty('expense-report'); $cache->addProperty($accounts->pluck('id')->toArray()); if ($cache->has()) { - return Response::json($cache->get()); + return $cache->get(); + } + + $expenses = $helper->getExpenseReport($start, $end, $accounts); + + $result = view('reports.partials.expenses', compact('expenses'))->render(); + $cache->store($result); + + return $result; + + } + + /** + * @param ReportHelperInterface $helper + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return \Illuminate\Http\JsonResponse + */ + public function incExpReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts) + { + // chart properties for cache: + $cache = new CacheProperties; + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('inc-exp-report'); + $cache->addProperty($accounts->pluck('id')->toArray()); + if ($cache->has()) { + return $cache->get(); } $incomes = $helper->getIncomeReport($start, $end, $accounts); $expenses = $helper->getExpenseReport($start, $end, $accounts); - $result = [ - 'income' => view('reports.partials.income', compact('incomes'))->render(), - 'expenses' => view('reports.partials.expenses', compact('expenses'))->render(), - 'incomes_expenses' => view('reports.partials.income-vs-expenses', compact('expenses', 'incomes'))->render(), - ]; + $result = view('reports.partials.income-vs-expenses', compact('expenses', 'incomes'))->render(); $cache->store($result); - return Response::json($result); + return $result; + + } + + /** + * @param ReportHelperInterface $helper + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return \Illuminate\Http\JsonResponse + */ + public function incomeReport(ReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts) + { + // chart properties for cache: + $cache = new CacheProperties; + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('income-report'); + $cache->addProperty($accounts->pluck('id')->toArray()); + if ($cache->has()) { + return $cache->get(); + } + + $incomes = $helper->getIncomeReport($start, $end, $accounts); + + $result = view('reports.partials.income', compact('incomes'))->render(); + $cache->store($result); + + return $result; } diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index 7878830f15..a92648b398 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -15,12 +15,12 @@ namespace FireflyIII\Http\Controllers; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Helpers\Report\ReportHelperInterface; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use FireflyIII\Models\Transaction; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Account\AccountTaskerInterface; use Illuminate\Support\Collection; use Preferences; use Session; @@ -150,8 +150,6 @@ class ReportController extends Controller */ private function auditReport(Carbon $start, Carbon $end, Collection $accounts) { - /** @var AccountTaskerInterface $tasker */ - $tasker = app(AccountTaskerInterface::class); $auditData = []; $dayBefore = clone $start; $dayBefore->subDay(); @@ -160,9 +158,11 @@ class ReportController extends Controller // balance the day before: $id = $account->id; $dayBeforeBalance = Steam::balance($account, $dayBefore); - $journals = $tasker->getJournalsInPeriod(new Collection([$account]), [], $start, $end); - $journals = $journals->reverse(); - $startBalance = $dayBeforeBalance; + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts(new Collection([$account]))->setRange($start, $end); + $journals = $collector->getJournals(); + $journals = $journals->reverse(); + $startBalance = $dayBeforeBalance; /** @var Transaction $journal */ @@ -244,8 +244,6 @@ class ReportController extends Controller { // need all budgets // need all years. - $years = $this->helper->listOfYears($start, $end); - $budgetMultiYear = $this->helper->getBudgetMultiYear($start, $end, $accounts); // and some id's, joined: @@ -259,8 +257,7 @@ class ReportController extends Controller return view( 'reports.default.multi-year', compact( - 'accounts', 'start', 'end', 'accountIds', 'reportType', - 'years', 'budgetMultiYear' + 'accounts', 'start', 'end', 'accountIds', 'reportType' ) ); } diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 3f55f44e35..98443574d2 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -306,7 +306,7 @@ class RuleController extends Controller } // Return json response - $view = view('list.journals-tiny', ['transactions' => $matchingTransactions])->render(); + $view = view('list.journals-tiny-tasker', ['transactions' => $matchingTransactions])->render(); return Response::json(['html' => $view, 'warning' => $warning]); } diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index bb602229d3..e9b63f4b17 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -14,7 +14,7 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; use FireflyIII\Support\Search\SearchInterface; -use Input; +use Illuminate\Http\Request; /** * Class SearchController @@ -30,12 +30,6 @@ class SearchController extends Controller { parent::__construct(); - $this->middleware( - function ($request, $next) { - - return $next($request); - } - ); } /** @@ -45,16 +39,21 @@ class SearchController extends Controller * * @return $this */ - public function index(SearchInterface $searcher) + public function index(Request $request, SearchInterface $searcher) { - + $minSearchLen = 1; $subTitle = null; $query = null; $result = []; $title = trans('firefly.search'); + $limit = 20; $mainTitleIcon = 'fa-search'; - if (!is_null(Input::get('q')) && strlen(Input::get('q')) > 0) { - $query = trim(Input::get('q')); + + // set limit for search: + $searcher->setLimit($limit); + + if (!is_null($request->get('q')) && strlen($request->get('q')) >= $minSearchLen) { + $query = trim(strtolower($request->get('q'))); $words = explode(' ', $query); $subTitle = trans('firefly.search_results_for', ['query' => $query]); @@ -67,7 +66,7 @@ class SearchController extends Controller } - return view('search.index', compact('title', 'subTitle', 'mainTitleIcon', 'query', 'result')); + return view('search.index', compact('title', 'subTitle', 'limit', 'mainTitleIcon', 'query', 'result')); } } diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index fc7013d41a..7a790c284a 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -13,10 +13,11 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Http\Requests\TagFormRequest; use FireflyIII\Models\Preference; use FireflyIII\Models\Tag; -use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\Transaction; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use Illuminate\Support\Collection; use Input; @@ -226,19 +227,27 @@ class TagController extends Controller } /** - * @param Tag $tag - * @param TagRepositoryInterface $repository + * @param Tag $tag * * @return View */ - public function show(Tag $tag, TagRepositoryInterface $repository) + public function show(Tag $tag) { $subTitle = $tag->tag; $subTitleIcon = 'fa-tag'; - $journals = $repository->getJournals($tag); - $sum = $journals->sum( - function (TransactionJournal $journal) { - return TransactionJournal::amount($journal); + $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); + + // use collector: + // replace with journal collector: + $collector = new JournalCollector(auth()->user()); + $collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page)->setTag($tag); + $journals = $collector->getPaginatedJournals(); + $journals->setPath('tags/show/' . $tag->id); + + $sum = $journals->sum( + function (Transaction $transaction) { + return $transaction->transaction_amount; } ); diff --git a/app/Http/Controllers/Transaction/ConvertController.php b/app/Http/Controllers/Transaction/ConvertController.php index 851fbfe2c5..f00fb809ad 100644 --- a/app/Http/Controllers/Transaction/ConvertController.php +++ b/app/Http/Controllers/Transaction/ConvertController.php @@ -206,7 +206,6 @@ class ConvertController extends Controller $sourceAccount = TransactionJournal::sourceAccountList($journal)->first(); $destinationAccount = TransactionJournal::destinationAccountList($journal)->first(); $sourceType = $journal->transactionType; - $source = new Account; $joined = $sourceType->type . '-' . $destinationType->type; switch ($joined) { default: diff --git a/app/Http/Controllers/Transaction/SingleController.php b/app/Http/Controllers/Transaction/SingleController.php index dc005002ad..18f4d672eb 100644 --- a/app/Http/Controllers/Transaction/SingleController.php +++ b/app/Http/Controllers/Transaction/SingleController.php @@ -171,7 +171,6 @@ class SingleController extends Controller $assetAccounts = ExpandedForm::makeSelectList($this->accounts->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])); $budgetList = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets()); - $piggyBankList = ExpandedForm::makeSelectListWithEmpty($this->piggyBanks->getPiggyBanks()); // view related code $subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]); @@ -188,7 +187,6 @@ class SingleController extends Controller 'process_date' => TransactionJournal::dateAsString($journal, 'process_date'), 'category' => TransactionJournal::categoryAsString($journal), 'budget_id' => TransactionJournal::budgetId($journal), - 'piggy_bank_id' => TransactionJournal::piggyBankId($journal), 'tags' => join(',', $journal->tags->pluck('tag')->toArray()), 'source_account_id' => $sourceAccounts->first()->id, 'source_account_name' => $sourceAccounts->first()->name, @@ -225,7 +223,7 @@ class SingleController extends Controller return view( 'transactions.edit', - compact('journal', 'optionalFields', 'assetAccounts', 'what', 'budgetList', 'piggyBankList', 'subTitle') + compact('journal', 'optionalFields', 'assetAccounts', 'what', 'budgetList', 'subTitle') )->with('data', $preFilled); } diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index bd2a3d4c1e..264524d20e 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -14,6 +14,7 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; use Carbon\Carbon; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalTaskerInterface; @@ -49,21 +50,22 @@ class TransactionController extends Controller } /** - * @param Request $request - * @param JournalTaskerInterface $tasker - * @param string $what + * @param Request $request + * @param string $what * * @return View */ - public function index(Request $request, JournalTaskerInterface $tasker, string $what) + public function index(Request $request, string $what) { $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $subTitleIcon = config('firefly.transactionIconsByWhat.' . $what); $types = config('firefly.transactionTypesByWhat.' . $what); $subTitle = trans('firefly.title_' . $what); - $page = intval($request->get('page')); - $journals = $tasker->getJournals($types, $page, $pageSize); + $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); + $collector = new JournalCollector(auth()->user()); + $collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts(); + $journals = $collector->getPaginatedJournals(); $journals->setPath('transactions/' . $what); return view('transactions.index', compact('subTitle', 'what', 'subTitleIcon', 'journals')); diff --git a/app/Jobs/ExecuteRuleGroupOnExistingTransactions.php b/app/Jobs/ExecuteRuleGroupOnExistingTransactions.php index 8048fe9cd7..fec910a6b3 100644 --- a/app/Jobs/ExecuteRuleGroupOnExistingTransactions.php +++ b/app/Jobs/ExecuteRuleGroupOnExistingTransactions.php @@ -14,8 +14,8 @@ declare(strict_types = 1); namespace FireflyIII\Jobs; use Carbon\Carbon; +use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Models\RuleGroup; -use FireflyIII\Repositories\Journal\JournalTaskerInterface; use FireflyIII\Rules\Processor; use FireflyIII\User; use Illuminate\Contracts\Queue\ShouldQueue; @@ -129,16 +129,16 @@ class ExecuteRuleGroupOnExistingTransactions extends Job implements ShouldQueue public function handle() { // Lookup all journals that match the parameters specified - $journals = $this->collectJournals(); + $transactions = $this->collectJournals(); // Find processors for each rule within the current rule group $processors = $this->collectProcessors(); // Execute the rules for each transaction - foreach ($journals as $journal) { + foreach ($transactions as $transaction) { /** @var Processor $processor */ foreach ($processors as $processor) { - $processor->handleTransactionJournal($journal); + $processor->handleTransaction($transaction); // Stop processing this group if the rule specifies 'stop_processing' if ($processor->getRule()->stop_processing) { @@ -155,10 +155,10 @@ class ExecuteRuleGroupOnExistingTransactions extends Job implements ShouldQueue */ protected function collectJournals() { - /** @var JournalTaskerInterface $tasker */ - $tasker = app(JournalTaskerInterface::class); + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($this->accounts)->setRange($this->startDate, $this->endDate); - return $tasker->getJournalsInRange($this->accounts, $this->startDate, $this->endDate); + return $collector->getJournals(); } /** diff --git a/app/Models/Note.php b/app/Models/Note.php index c811fcc7cd..c26a397421 100644 --- a/app/Models/Note.php +++ b/app/Models/Note.php @@ -46,13 +46,12 @@ class Note extends Model /** - * @param $value - * * @return string */ public function getMarkdownAttribute(): string { - $converter = new CommonMarkConverter; + $converter = new CommonMarkConverter; + return $converter->convertToHtml($this->text); } diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index 3d5e5da150..9a57917b26 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -23,22 +23,22 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\PiggyBank * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property \Carbon\Carbon $deleted_at - * @property integer $account_id - * @property string $name - * @property float $targetamount - * @property \Carbon\Carbon $startdate - * @property \Carbon\Carbon $targetdate - * @property integer $order - * @property boolean $encrypted - * @property-read Account $account - * @property-read \Illuminate\Database\Eloquent\Collection|PiggyBankRepetition[] $piggyBankRepetitions - * @property-read \Illuminate\Database\Eloquent\Collection|PiggyBankEvent[] $piggyBankEvents - * @property string $reminder - * @property PiggyBankRepetition $currentRep + * @property integer $id + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at + * @property \Carbon\Carbon $deleted_at + * @property integer $account_id + * @property string $name + * @property float $targetamount + * @property \Carbon\Carbon $startdate + * @property \Carbon\Carbon $targetdate + * @property integer $order + * @property boolean $encrypted + * @property-read Account $account + * @property-read \Illuminate\Database\Eloquent\Collection|PiggyBankRepetition[] $piggyBankRepetitions + * @property-read \Illuminate\Database\Eloquent\Collection|PiggyBankEvent[] $piggyBankEvents + * @property string $reminder + * @property PiggyBankRepetition $currentRep * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereId($value) * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereCreatedAt($value) * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereUpdatedAt($value) @@ -54,7 +54,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereDeletedAt($value) * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereEncrypted($value) * @mixin \Eloquent - * @property boolean $active + * @property boolean $active * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereActive($value) * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Note[] $notes */ diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 6cfb886b4c..b256815c13 100755 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -70,7 +70,6 @@ class EventServiceProvider extends ServiceProvider 'FireflyIII\Events\UpdatedTransactionJournal' => // is a Transaction Journal related event. [ 'FireflyIII\Handlers\Events\UpdatedJournalEventHandler@scanBills', - 'FireflyIII\Handlers\Events\UpdatedJournalEventHandler@connectToPiggyBank', 'FireflyIII\Handlers\Events\UpdatedJournalEventHandler@processRules', ], diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php index b1530b08ca..91122f0d5f 100644 --- a/app/Providers/FireflyServiceProvider.php +++ b/app/Providers/FireflyServiceProvider.php @@ -94,7 +94,6 @@ class FireflyServiceProvider extends ServiceProvider ); $this->app->bind('FireflyIII\Repositories\Currency\CurrencyRepositoryInterface', 'FireflyIII\Repositories\Currency\CurrencyRepository'); - $this->app->bind('FireflyIII\Support\Search\SearchInterface', 'FireflyIII\Support\Search\Search'); $this->app->bind('FireflyIII\Repositories\User\UserRepositoryInterface', 'FireflyIII\Repositories\User\UserRepository'); $this->app->bind('FireflyIII\Helpers\Attachments\AttachmentHelperInterface', 'FireflyIII\Helpers\Attachments\AttachmentHelper'); $this->app->bind( diff --git a/app/Providers/SearchServiceProvider.php b/app/Providers/SearchServiceProvider.php new file mode 100644 index 0000000000..a7be767e5e --- /dev/null +++ b/app/Providers/SearchServiceProvider.php @@ -0,0 +1,59 @@ +app->bind( + 'FireflyIII\Support\Search\SearchInterface', + function (Application $app, array $arguments) { + if (!isset($arguments[0]) && $app->auth->check()) { + return app('FireflyIII\Support\Search\Search', [auth()->user()]); + } + if (!isset($arguments[0]) && !$app->auth->check()) { + throw new FireflyException('There is no user present.'); + } + + return app('FireflyIII\Support\Search\Search', $arguments); + } + ); + } +} diff --git a/app/Repositories/Account/AccountTasker.php b/app/Repositories/Account/AccountTasker.php index 59b46c1b88..1711fed6bb 100644 --- a/app/Repositories/Account/AccountTasker.php +++ b/app/Repositories/Account/AccountTasker.php @@ -190,77 +190,6 @@ class AccountTasker implements AccountTaskerInterface return $object; } - /** - * It might be worth it to expand this query to include all account information required. - * - * @param Collection $accounts - * @param array $types - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function getJournalsInPeriod(Collection $accounts, array $types, Carbon $start, Carbon $end): Collection - { - $accountIds = $accounts->pluck('id')->toArray(); - $query = Transaction - ::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transaction_journals.transaction_currency_id') - ->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id') - ->leftJoin('bills', 'bills.id', 'transaction_journals.bill_id') - ->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id') - ->leftJoin('account_types', 'accounts.account_type_id', 'account_types.id') - ->whereIn('transactions.account_id', $accountIds) - ->whereNull('transactions.deleted_at') - ->whereNull('transaction_journals.deleted_at') - ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) - ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) - ->where('transaction_journals.user_id', $this->user->id) - ->orderBy('transaction_journals.date', 'DESC') - ->orderBy('transaction_journals.order', 'ASC') - ->orderBy('transaction_journals.id', 'DESC'); - - if (count($types) > 0) { - $query->whereIn('transaction_types.type', $types); - } - - $set = $query->get( - [ - 'transaction_journals.id as journal_id', - 'transaction_journals.description', - 'transaction_journals.date', - 'transaction_journals.encrypted', - //'transaction_journals.transaction_currency_id', - 'transaction_currencies.code as transaction_currency_code', - //'transaction_currencies.symbol as transaction_currency_symbol', - 'transaction_types.type as transaction_type_type', - 'transaction_journals.bill_id', - 'bills.name as bill_name', - 'transactions.id as id', - 'transactions.amount as transaction_amount', - 'transactions.description as transaction_description', - 'transactions.account_id', - 'transactions.identifier', - 'transactions.transaction_journal_id', - 'accounts.name as account_name', - 'accounts.encrypted as account_encrypted', - 'account_types.type as account_type', - - ] - ); - - // loop for decryption. - $set->each( - function (Transaction $transaction) { - $transaction->date = new Carbon($transaction->date); - $transaction->description = intval($transaction->encrypted) === 1 ? Crypt::decrypt($transaction->description) : $transaction->description; - $transaction->bill_name = !is_null($transaction->bill_name) ? Crypt::decrypt($transaction->bill_name) : ''; - } - ); - - return $set; - } - /** * @param Collection $accounts * @param Collection $excluded @@ -328,7 +257,6 @@ class AccountTasker implements AccountTaskerInterface $join->on('transaction_journals.id', '=', 'other_side.transaction_journal_id')->where('other_side.amount', $joinModifier, 0); } ) - ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) ->where('transaction_journals.user_id', $this->user->id) @@ -384,7 +312,7 @@ class AccountTasker implements AccountTaskerInterface } ) ->leftJoin('accounts as other_account', 'other_account.id', '=', 'other_side.account_id') - ->where('transaction_types.type','!=', TransactionType::OPENING_BALANCE) + ->where('transaction_types.type', '!=', TransactionType::OPENING_BALANCE) ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) ->where('transaction_journals.user_id', $this->user->id) diff --git a/app/Repositories/Account/AccountTaskerInterface.php b/app/Repositories/Account/AccountTaskerInterface.php index dcb8c10416..dbf1892667 100644 --- a/app/Repositories/Account/AccountTaskerInterface.php +++ b/app/Repositories/Account/AccountTaskerInterface.php @@ -71,18 +71,6 @@ interface AccountTaskerInterface */ public function getAccountReport(Carbon $start, Carbon $end, Collection $accounts): AccountCollection; - /** - * Experimental getJournals method. - * - * @param Collection $accounts - * @param array $types - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function getJournalsInPeriod(Collection $accounts, array $types, Carbon $start, Carbon $end): Collection; - /** * @param Collection $accounts * @param Collection $excluded diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index f086a4b19b..e41196b81d 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -22,7 +22,6 @@ use FireflyIII\Models\TransactionType; use FireflyIII\Support\CacheProperties; use FireflyIII\User; use Illuminate\Database\Query\JoinClause; -use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Log; use Navigation; @@ -116,41 +115,6 @@ class BillRepository implements BillRepositoryInterface return $set; } - /** - * Returns all journals connected to these bills in the given range. Amount paid - * is stored in "journalAmount" as a negative number. - * - * @param Collection $bills - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function getAllJournalsInRange(Collection $bills, Carbon $start, Carbon $end): Collection - { - $ids = $bills->pluck('id')->toArray(); - - $set = $this->user->transactionJournals() - ->leftJoin( - 'transactions', function (JoinClause $join) { - $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0); - } - ) - ->whereIn('bill_id', $ids) - ->before($end) - ->after($start) - ->groupBy(['transaction_journals.bill_id', 'transaction_journals.id']) - ->get( - [ - 'transaction_journals.bill_id', - 'transaction_journals.id', - DB::raw('SUM(transactions.amount) AS journalAmount'), - ] - ); - - return $set; - } - /** * @return Collection */ @@ -287,30 +251,6 @@ class BillRepository implements BillRepositoryInterface return $sum; } - /** - * This method also returns the amount of the journal in "journalAmount" - * for easy access. - * - * @param Bill $bill - * - * @param int $page - * @param int $pageSize - * - * @return LengthAwarePaginator|Collection - */ - public function getJournals(Bill $bill, int $page, int $pageSize = 50): LengthAwarePaginator - { - $offset = ($page - 1) * $pageSize; - $query = $bill->transactionJournals() - ->expanded() - ->sortCorrectly(); - $count = $query->count(); - $set = $query->take($pageSize)->offset($offset)->get(TransactionJournal::queryFields()); - $paginator = new LengthAwarePaginator($set, $count, $pageSize, $page); - - return $paginator; - } - /** * @param Bill $bill * diff --git a/app/Repositories/Bill/BillRepositoryInterface.php b/app/Repositories/Bill/BillRepositoryInterface.php index 26e5f22aef..cf71d133bc 100644 --- a/app/Repositories/Bill/BillRepositoryInterface.php +++ b/app/Repositories/Bill/BillRepositoryInterface.php @@ -16,7 +16,6 @@ namespace FireflyIII\Repositories\Bill; use Carbon\Carbon; use FireflyIII\Models\Bill; use FireflyIII\Models\TransactionJournal; -use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; /** @@ -57,18 +56,6 @@ interface BillRepositoryInterface */ public function getActiveBills(): Collection; - /** - * Returns all journals connected to these bills in the given range. Amount paid - * is stored in "journalAmount" as a negative number. - * - * @param Collection $bills - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function getAllJournalsInRange(Collection $bills, Carbon $start, Carbon $end): Collection; - /** * @return Collection */ @@ -103,16 +90,6 @@ interface BillRepositoryInterface */ public function getBillsUnpaidInRange(Carbon $start, Carbon $end): string; - /** - * @param Bill $bill - * - * @param int $page - * @param int $pageSize - * - * @return LengthAwarePaginator - */ - public function getJournals(Bill $bill, int $page, int $pageSize = 50): LengthAwarePaginator; - /** * @param Bill $bill * diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 5210d63ec8..6726287704 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -24,7 +24,6 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\User; use Illuminate\Database\Eloquent\Builder; -use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; use Log; @@ -210,135 +209,6 @@ class BudgetRepository implements BudgetRepositoryInterface return $set; } - /** - * @param Collection $budgets - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function journalsInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): Collection - { - $return = new Collection; - $accountIds = []; - // expand the number of grabbed fields: - $fields = TransactionJournal::queryFields(); - $fields[] = 'source.account_id'; - if ($accounts->count() > 0) { - $accountIds = $accounts->pluck('id')->toArray(); - } - - // first get all journals for all budget(s): - $journalQuery = $this->user->transactionJournals() - ->expanded() - ->sortCorrectly() - ->before($end) - ->after($start) - ->leftJoin( - 'transactions as source', - function (JoinClause $join) { - $join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', '0'); - } - ) - ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->whereIn('budget_transaction_journal.budget_id', $budgets->pluck('id')->toArray()); - // add account id's, if relevant: - if (count($accountIds) > 0) { - $journalQuery->whereIn('source.account_id', $accountIds); - } - // get them: - $journals = $journalQuery->get(TransactionJournal::queryFields()); - - // then get transactions themselves. - $transactionQuery = $this->user->transactionJournals() - ->expanded() - ->before($end) - ->sortCorrectly() - ->after($start) - ->leftJoin('transactions as related', 'related.transaction_journal_id', '=', 'transaction_journals.id') - ->leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'related.id') - ->leftJoin( - 'transactions as source', - function (JoinClause $join) { - $join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', '0'); - } - ) - ->groupBy(['source.account_id']) - ->whereIn('budget_transaction.budget_id', $budgets->pluck('id')->toArray()); - - if (count($accountIds) > 0) { - $transactionQuery->whereIn('source.account_id', $accountIds); - } - - $transactions = $transactionQuery->get($fields); - - // return complete set: - $return = $return->merge($transactions); - $return = $return->merge($journals); - - return $return; - } - - /** - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function journalsInPeriodWithoutBudget(Collection $accounts, Carbon $start, Carbon $end): Collection - { - $accountIds = []; - if ($accounts->count() > 0) { - $accountIds = $accounts->pluck('id')->toArray(); - } - - /** @var Collection $set */ - $query = $this->user - ->transactionJournals() - ->expanded() - ->sortCorrectly() - ->transactionTypes([TransactionType::WITHDRAWAL]) - ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->whereNull('budget_transaction_journal.id') - ->leftJoin( - 'transactions as source', - function (JoinClause $join) { - $join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', '0'); - } - ) - ->before($end) - ->after($start)->with( - [ - 'transactions' => function (HasMany $query) { - $query->where('transactions.amount', '<', 0); - }, - 'transactions.budgets', - ] - ); - - // add account id's, if relevant: - if (count($accountIds) > 0) { - $query->whereIn('source.account_id', $accountIds); - } - - $set = $query->get(TransactionJournal::queryFields()); - $set = $set->filter( - function (TransactionJournal $journal) { - foreach ($journal->transactions as $t) { - if ($t->budgets->count() === 0) { - return true; - } - } - - return false; - } - ); - - return $set; - } - /** * @param Collection $budgets * @param Collection $accounts @@ -350,8 +220,8 @@ class BudgetRepository implements BudgetRepositoryInterface public function spentInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end) : string { // collect amount of transaction journals, which is easy: - $budgetIds = $budgets->pluck('id')->toArray(); - $accountIds = $accounts->pluck('id')->toArray(); + $budgetIds = $budgets->pluck('id')->toArray(); + $accountIds = $accounts->pluck('id')->toArray(); Log::debug('spentInPeriod: Now in spentInPeriod for these budgets: ', $budgetIds); Log::debug('spentInPeriod: and these accounts: ', $accountIds); diff --git a/app/Repositories/Budget/BudgetRepositoryInterface.php b/app/Repositories/Budget/BudgetRepositoryInterface.php index 0f6aba141e..b7116dcbd2 100644 --- a/app/Repositories/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetRepositoryInterface.php @@ -89,25 +89,6 @@ interface BudgetRepositoryInterface */ public function getInactiveBudgets(): Collection; - /** - * @param Collection $budgets - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function journalsInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): Collection; - - /** - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function journalsInPeriodWithoutBudget(Collection $accounts, Carbon $start, Carbon $end): Collection; - /** * @param Collection $budgets * @param Collection $accounts diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index cc0ec6b192..cbb2927814 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -20,7 +20,6 @@ use FireflyIII\Models\TransactionType; use FireflyIII\User; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Query\JoinClause; -use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; /** @@ -174,176 +173,6 @@ class CategoryRepository implements CategoryRepositoryInterface return $set; } - /** - * @param Category $category - * @param int $page - * @param int $pageSize - * - * @return LengthAwarePaginator - */ - public function getJournals(Category $category, int $page, int $pageSize): LengthAwarePaginator - { - $complete = new Collection; - // first collect actual transaction journals (fairly easy) - $query = $this->user->transactionJournals()->expanded()->sortCorrectly(); - $query->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'); - $query->where('category_transaction_journal.category_id', $category->id); - $first = $query->get(TransactionJournal::queryFields()); - - // then collection transactions (harder) - $query = $this->user->transactionJournals()->distinct() - ->leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->leftJoin('category_transaction', 'category_transaction.transaction_id', '=', 'transactions.id') - ->where('category_transaction.category_id', $category->id); - $second = $query->get(['transaction_journals.*']); - - $complete = $complete->merge($first); - $complete = $complete->merge($second); - - // sort: - /** @var Collection $complete */ - $complete = $complete->sortByDesc( - function ($model) { - $date = new Carbon($model->date); - - return intval($date->format('U')); - } - ); - // create paginator - $offset = ($page - 1) * $pageSize; - $subSet = $complete->slice($offset, $pageSize)->all(); - $paginator = new LengthAwarePaginator($subSet, $complete->count(), $pageSize, $page); - - return $paginator; - } - - /** - * Get all transactions in a category in a range. - * - * @param Collection $categories - * @param Collection $accounts - * @param array $types - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function journalsInPeriod(Collection $categories, Collection $accounts, array $types, Carbon $start, Carbon $end): Collection - { - $complete = new Collection; - // first collect actual transaction journals (fairly easy) - $query = $this->user->transactionJournals()->expanded()->sortCorrectly(); - - if ($end >= $start) { - $query->before($end)->after($start); - } - - if (count($types) > 0) { - $query->transactionTypes($types); - } - - if ($accounts->count() > 0) { - $accountIds = $accounts->pluck('id')->toArray(); - $query->leftJoin('transactions as t', 't.transaction_journal_id', '=', 'transaction_journals.id'); - $query->whereIn('t.account_id', $accountIds); - } - if ($categories->count() > 0) { - $categoryIds = $categories->pluck('id')->toArray(); - $query->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'); - $query->whereIn('category_transaction_journal.category_id', $categoryIds); - } - - // that should do it: - $first = $query->get(TransactionJournal::queryFields()); - - - // then collection transactions (harder) - $query = $this->user->transactionJournals()->distinct() - ->leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->leftJoin('category_transaction', 'category_transaction.transaction_id', '=', 'transactions.id') - ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'); - if (count($types) > 0) { - $query->whereIn('transaction_types.type', $types); - } - if ($accounts->count() > 0) { - $accountIds = $accounts->pluck('id')->toArray(); - $query->whereIn('transactions.account_id', $accountIds); - } - if ($categories->count() > 0) { - $categoryIds = $categories->pluck('id')->toArray(); - $query->whereIn('category_transaction.category_id', $categoryIds); - } - - - $second = $query->get(['transaction_journals.*', 'transaction_types.type as transaction_type_type']); - - $complete = $complete->merge($first); - $complete = $complete->merge($second); - - return $complete; - } - - /** - * @param Collection $accounts - * @param array $types - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function journalsInPeriodWithoutCategory(Collection $accounts, array $types, Carbon $start, Carbon $end) : Collection - { - /** @var Collection $set */ - $query = $this->user - ->transactionJournals(); - if (count($types) > 0) { - $query->transactionTypes($types); - } - - $query->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->whereNull('category_transaction_journal.id') - ->before($end) - ->after($start); - - if ($accounts->count() > 0) { - $accountIds = $accounts->pluck('id')->toArray(); - $query->leftJoin('transactions as t', 't.transaction_journal_id', '=', 'transaction_journals.id'); - $query->whereIn('t.account_id', $accountIds); - } - - $set = $query->get(['transaction_journals.*']); - - if ($set->count() == 0) { - return new Collection; - } - - // grab all the transactions from this set. - // take only the journals with transactions that all have no category. - // select transactions left join journals where id in this set - // and left join transaction-category where null category - $journalIds = $set->pluck('id')->toArray(); - $secondQuery = $this->user->transactions() - ->leftJoin('category_transaction', 'category_transaction.transaction_id', '=', 'transactions.id') - ->whereNull('category_transaction.id') - ->whereIn('transaction_journals.id', $journalIds); - - if ($accounts->count() > 0) { - $accountIds = $accounts->pluck('id')->toArray(); - $secondQuery->whereIn('transactions.account_id', $accountIds); - } - - // this second set REALLY doesn't have any categories. - $secondSet = $secondQuery->get(['transactions.transaction_journal_id']); - $allIds = $secondSet->pluck('transaction_journal_id')->toArray(); - $return = $this->user->transactionJournals()->sortCorrectly()->expanded()->whereIn('transaction_journals.id', $allIds)->get( - TransactionJournal::queryFields() - ); - - return $return; - - - } - /** * @param Category $category * @param Collection $accounts diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index d5aaff5bc2..d088eebe5d 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -15,7 +15,6 @@ namespace FireflyIII\Repositories\Category; use Carbon\Carbon; use FireflyIII\Models\Category; -use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; /** @@ -84,36 +83,6 @@ interface CategoryRepositoryInterface */ public function getCategories(): Collection; - /** - * @param Category $category - * @param int $page - * @param int $pageSize - * - * @return LengthAwarePaginator - */ - public function getJournals(Category $category, int $page, int $pageSize): LengthAwarePaginator; - - /** - * @param Collection $categories - * @param Collection $accounts - * @param array $types - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function journalsInPeriod(Collection $categories, Collection $accounts, array $types, Carbon $start, Carbon $end): Collection; - - /** - * @param Collection $accounts - * @param array $types - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function journalsInPeriodWithoutCategory(Collection $accounts, array $types, Carbon $start, Carbon $end) : Collection; - /** * Return most recent transaction(journal) date. * diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php index 5d81adf1a6..6b3e476b50 100644 --- a/app/Repositories/Journal/JournalRepository.php +++ b/app/Repositories/Journal/JournalRepository.php @@ -458,7 +458,7 @@ class JournalRepository implements JournalRepositoryInterface */ private function storeBudgetWithJournal(TransactionJournal $journal, int $budgetId) { - if (intval($budgetId) > 0 && $journal->transactionType->type !== TransactionType::TRANSFER) { + if (intval($budgetId) > 0 && $journal->transactionType->type === TransactionType::WITHDRAWAL) { /** @var \FireflyIII\Models\Budget $budget */ $budget = Budget::find($budgetId); $journal->budgets()->save($budget); diff --git a/app/Repositories/Journal/JournalRepositoryInterface.php b/app/Repositories/Journal/JournalRepositoryInterface.php index d1c2825bb5..8a25f542b9 100644 --- a/app/Repositories/Journal/JournalRepositoryInterface.php +++ b/app/Repositories/Journal/JournalRepositoryInterface.php @@ -26,6 +26,16 @@ use Illuminate\Support\MessageBag; interface JournalRepositoryInterface { + /** + * @param TransactionJournal $journal + * @param TransactionType $type + * @param Account $source + * @param Account $destination + * + * @return MessageBag + */ + public function convert(TransactionJournal $journal, TransactionType $type, Account $source, Account $destination): MessageBag; + /** * Deletes a journal. * @@ -35,15 +45,6 @@ interface JournalRepositoryInterface */ public function delete(TransactionJournal $journal): bool; - /** - * @param TransactionJournal $journal - * @param TransactionType $type - * @param array $data - * - * @return MessageBag - */ - public function convert(TransactionJournal $journal, TransactionType $type, Account $source, Account $destination): MessageBag; - /** * Find a specific journal * diff --git a/app/Repositories/Journal/JournalTasker.php b/app/Repositories/Journal/JournalTasker.php index 26e3c63274..862bafadb8 100644 --- a/app/Repositories/Journal/JournalTasker.php +++ b/app/Repositories/Journal/JournalTasker.php @@ -46,73 +46,6 @@ class JournalTasker implements JournalTaskerInterface $this->user = $user; } - /** - * Returns a page of a specific type(s) of journal. - * - * @param array $types - * @param int $page - * @param int $pageSize - * - * @return LengthAwarePaginator - */ - public function getJournals(array $types, int $page, int $pageSize = 50): LengthAwarePaginator - { - $offset = ($page - 1) * $pageSize; - $query = $this->user->transactionJournals()->expanded()->sortCorrectly(); - $query->where('transaction_journals.completed', 1); - if (count($types) > 0) { - $query->transactionTypes($types); - } - $count = $this->user->transactionJournals()->transactionTypes($types)->count(); - $set = $query->take($pageSize)->offset($offset)->get(TransactionJournal::queryFields()); - $journals = new LengthAwarePaginator($set, $count, $pageSize, $page); - - return $journals; - } - - /** - * Returns a collection of ALL journals, given a specific account and a date range. - * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function getJournalsInRange(Collection $accounts, Carbon $start, Carbon $end): Collection - { - $query = $this->user->transactionJournals()->expanded()->sortCorrectly(); - $query->where('transaction_journals.completed', 1); - $query->before($end); - $query->after($start); - - if ($accounts->count() > 0) { - $ids = $accounts->pluck('id')->toArray(); - // join source and destination: - $query->leftJoin( - 'transactions as source', function (JoinClause $join) { - $join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', 0); - } - ); - $query->leftJoin( - 'transactions as destination', function (JoinClause $join) { - $join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')->where('destination.amount', '>', 0); - } - ); - - $query->where( - function (Builder $q) use ($ids) { - $q->whereIn('destination.account_id', $ids); - $q->orWhereIn('source.account_id', $ids); - } - ); - } - - $set = $query->get(TransactionJournal::queryFields()); - - return $set; - } - /** * @param TransactionJournal $journal * diff --git a/app/Repositories/Journal/JournalTaskerInterface.php b/app/Repositories/Journal/JournalTaskerInterface.php index 89ae1c8289..09ad5813a7 100644 --- a/app/Repositories/Journal/JournalTaskerInterface.php +++ b/app/Repositories/Journal/JournalTaskerInterface.php @@ -26,27 +26,6 @@ use Illuminate\Support\Collection; */ interface JournalTaskerInterface { - /** - * Returns a page of a specific type(s) of journal. - * - * @param array $types - * @param int $page - * @param int $pageSize - * - * @return LengthAwarePaginator - */ - public function getJournals(array $types, int $page, int $pageSize = 50): LengthAwarePaginator; - - /** - * Returns a collection of ALL journals, given a specific account and a date range. - * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return Collection - */ - public function getJournalsInRange(Collection $accounts, Carbon $start, Carbon $end): Collection; /** * @param TransactionJournal $journal diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index 6c29921d88..c71d031d72 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -133,24 +133,6 @@ class TagRepository implements TagRepositoryInterface return $tags; } - /** - * @param Tag $tag - * - * @return Collection - */ - public function getJournals(Tag $tag) : Collection - { - /** @var Collection $journals */ - $journals = $tag - ->transactionJournals() - ->sortCorrectly() - ->expanded() - ->groupBy(['tag_transaction_journal.tag_id', 'tag_transaction_journal.transaction_journal_id']) - ->get(TransactionJournal::queryFields()); - - return $journals; - } - /** * @param array $data * diff --git a/app/Repositories/Tag/TagRepositoryInterface.php b/app/Repositories/Tag/TagRepositoryInterface.php index b1676b2f03..dd1966bd51 100644 --- a/app/Repositories/Tag/TagRepositoryInterface.php +++ b/app/Repositories/Tag/TagRepositoryInterface.php @@ -65,13 +65,6 @@ interface TagRepositoryInterface */ public function get(): Collection; - /** - * @param Tag $tag - * - * @return Collection - */ - public function getJournals(Tag $tag) : Collection; - /** * This method stores a tag. * diff --git a/app/Rules/Actions/ClearCategory.php b/app/Rules/Actions/ClearCategory.php index 5af30a1811..d60afcf77a 100644 --- a/app/Rules/Actions/ClearCategory.php +++ b/app/Rules/Actions/ClearCategory.php @@ -16,6 +16,7 @@ namespace FireflyIII\Rules\Actions; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; +use Log; /** * Class ClearCategory diff --git a/app/Rules/Processor.php b/app/Rules/Processor.php index d7192cddf9..9b0daf0ae9 100644 --- a/app/Rules/Processor.php +++ b/app/Rules/Processor.php @@ -16,6 +16,7 @@ namespace FireflyIII\Rules; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleTrigger; +use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Rules\Actions\ActionInterface; use FireflyIII\Rules\Factory\ActionFactory; @@ -144,6 +145,36 @@ final class Processor return $this->rule; } + /** + * This method will scan the given transaction journal and check if it matches the triggers found in the Processor + * If so, it will also attempt to run the given actions on the journal. It returns a bool indicating if the transaction journal + * matches all of the triggers (regardless of whether the Processor could act on it). + * + * @param Transaction $transaction + * + * @return bool + */ + public function handleTransaction(Transaction $transaction): bool + { + Log::debug(sprintf('handleTransactionJournal for journal #%d (transaction #%d)', $transaction->transaction_journal_id, $transaction->id)); + + // grab the actual journal. + $journal = $transaction->transactionJournal()->first(); + $this->journal = $journal; + // get all triggers: + $triggered = $this->triggered(); + if ($triggered) { + if ($this->actions->count() > 0) { + $this->actions(); + } + + return true; + } + + return false; + + } + /** * This method will scan the given transaction journal and check if it matches the triggers found in the Processor * If so, it will also attempt to run the given actions on the journal. It returns a bool indicating if the transaction journal diff --git a/app/Rules/TransactionMatcher.php b/app/Rules/TransactionMatcher.php index 7491bbbb88..0e63c06085 100644 --- a/app/Rules/TransactionMatcher.php +++ b/app/Rules/TransactionMatcher.php @@ -13,7 +13,8 @@ declare(strict_types = 1); namespace FireflyIII\Rules; -use FireflyIII\Models\TransactionJournal; +use FireflyIII\Helpers\Collector\JournalCollector; +use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Journal\JournalTaskerInterface; use Illuminate\Support\Collection; @@ -62,7 +63,7 @@ class TransactionMatcher if (count($this->triggers) === 0) { return new Collection; } - $pagesize = min($this->range / 2, $this->limit * 2); + $pageSize = min($this->range / 2, $this->limit * 2); // Variables used within the loop $processed = 0; @@ -76,31 +77,42 @@ class TransactionMatcher // - the maximum number of transactions to search in have been searched do { // Fetch a batch of transactions from the database - $paginator = $this->tasker->getJournals($this->transactionTypes, $page, $pagesize); - $set = $paginator->getCollection(); - + $collector = new JournalCollector(auth()->user()); + $collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page)->setTypes($this->transactionTypes); + $set = $collector->getPaginatedJournals(); + Log::debug(sprintf('Found %d journals to check. ', $set->count())); // Filter transactions that match the given triggers. $filtered = $set->filter( - function (TransactionJournal $journal) use ($processor) { - Log::debug(sprintf('Test these triggers on #%d', $journal->id)); + function (Transaction $transaction) use ($processor) { + Log::debug(sprintf('Test these triggers on journal #%d (transaction #%d)', $transaction->transaction_journal_id, $transaction->id)); - return $processor->handleTransactionJournal($journal); + return $processor->handleTransaction($transaction); } ); + Log::debug(sprintf('Found %d journals that match.', $filtered->count())); + // merge: /** @var Collection $result */ $result = $result->merge($filtered); + Log::debug(sprintf('Total count is now %d', $result->count())); // Update counters $page++; $processed += count($set); + Log::debug(sprintf('Page is now %d, processed is %d', $page, $processed)); + // Check for conditions to finish the loop - $reachedEndOfList = $set->count() < $pagesize; + $reachedEndOfList = $set->count() < 1; $foundEnough = $result->count() >= $this->limit; $searchedEnough = ($processed >= $this->range); + + Log::debug(sprintf('reachedEndOfList: %s', var_export($reachedEndOfList, true))); + Log::debug(sprintf('foundEnough: %s', var_export($foundEnough, true))); + Log::debug(sprintf('searchedEnough: %s', var_export($searchedEnough, true))); + } while (!$reachedEndOfList && !$foundEnough && !$searchedEnough); // If the list of matchingTransactions is larger than the maximum number of results diff --git a/app/Support/Amount.php b/app/Support/Amount.php index 7937ae148a..c283289933 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -59,14 +59,14 @@ class Amount if ($coloured === true) { if ($amount > 0) { - return '' . $result . ''; + return sprintf('%s', $result); } else { if ($amount < 0) { - return '' . $result . ''; + return sprintf('%s', $result); } } - return '' . $result . ''; + return sprintf('%s', $result); } @@ -139,14 +139,14 @@ class Amount if ($coloured === true) { if ($amount > 0) { - return '' . $result . ''; + return sprintf('%s', $result); } else { if ($amount < 0) { - return '' . $result . ''; + return sprintf('%s', $result); } } - return '' . $result . ''; + return sprintf('%s', $result); } diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php index 4ea2318acb..665873596e 100644 --- a/app/Support/Navigation.php +++ b/app/Support/Navigation.php @@ -54,7 +54,7 @@ class Navigation ]; if (!isset($functionMap[$repeatFreq])) { - throw new FireflyException('Cannot do addPeriod for $repeat_freq "' . $repeatFreq . '"'); + throw new FireflyException(sprintf('Cannot do addPeriod for $repeat_freq "%s"', $repeatFreq)); } if (isset($modifierMap[$repeatFreq])) { $add = $add * $modifierMap[$repeatFreq]; @@ -108,7 +108,7 @@ class Navigation } if (!isset($functionMap[$repeatFreq])) { - throw new FireflyException('Cannot do endOfPeriod for $repeat_freq "' . $repeatFreq . '"'); + throw new FireflyException(sprintf('Cannot do endOfPeriod for $repeat_freq "%s"', $repeatFreq)); } $function = $functionMap[$repeatFreq]; if (isset($modifierMap[$repeatFreq])) { @@ -202,7 +202,7 @@ class Navigation if (isset($formatMap[$repeatFrequency])) { return $date->formatLocalized(strval($formatMap[$repeatFrequency])); } - throw new FireflyException('No date formats for frequency "' . $repeatFrequency . '"!'); + throw new FireflyException(sprintf('No date formats for frequency "%s"!', $repeatFrequency)); } /** @@ -252,7 +252,7 @@ class Navigation } - throw new FireflyException('Cannot do startOfPeriod for $repeat_freq "' . $repeatFreq . '"'); + throw new FireflyException(sprintf('Cannot do startOfPeriod for $repeat_freq "%s"', $repeatFreq)); } /** @@ -313,7 +313,7 @@ class Navigation return $date; } - throw new FireflyException('Cannot do subtractPeriod for $repeat_freq "' . $repeatFreq . '"'); + throw new FireflyException(sprintf('Cannot do subtractPeriod for $repeat_freq "%s"', $repeatFreq)); } /** @@ -350,7 +350,7 @@ class Navigation return $end; } - throw new FireflyException('updateEndDate cannot handle $range "' . $range . '"'); + throw new FireflyException(sprintf('updateEndDate cannot handle range "%s"', $range)); } /** @@ -387,7 +387,7 @@ class Navigation } - throw new FireflyException('updateStartDate cannot handle $range "' . $range . '"'); + throw new FireflyException(sprintf('updateStartDate cannot handle range "%s"', $range)); } diff --git a/app/Support/Preferences.php b/app/Support/Preferences.php index 8471745dbc..55ed08c6dc 100644 --- a/app/Support/Preferences.php +++ b/app/Support/Preferences.php @@ -32,7 +32,7 @@ class Preferences */ public function delete($name): bool { - $fullName = 'preference' . auth()->user()->id . $name; + $fullName = sprintf('preference%s%s', auth()->user()->id, $name); if (Cache::has($fullName)) { Cache::forget($fullName); } @@ -66,7 +66,7 @@ class Preferences */ public function getForUser(User $user, $name, $default = null) { - $fullName = 'preference' . $user->id . $name; + $fullName = sprintf('preference%s%s', $user->id, $name); if (Cache::has($fullName)) { return Cache::get($fullName); } @@ -138,7 +138,7 @@ class Preferences */ public function setForUser(User $user, $name, $value): Preference { - $fullName = 'preference' . $user->id . $name; + $fullName = sprintf('preference%s%s', $user->id, $name); Cache::forget($fullName); $pref = Preference::where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data']); diff --git a/app/Support/Search/Search.php b/app/Support/Search/Search.php index d20e83413f..c5e1da96c7 100644 --- a/app/Support/Search/Search.php +++ b/app/Support/Search/Search.php @@ -14,11 +14,15 @@ declare(strict_types = 1); namespace FireflyIII\Support\Search; +use FireflyIII\Helpers\Collector\JournalCollector; +use FireflyIII\Models\Account; use FireflyIII\Models\Budget; use FireflyIII\Models\Category; -use FireflyIII\Models\TransactionJournal; -use Illuminate\Database\Eloquent\Builder as EloquentBuilder; +use FireflyIII\Models\Tag; +use FireflyIII\Models\Transaction; +use FireflyIII\User; use Illuminate\Support\Collection; +use Log; /** * Class Search @@ -27,20 +31,46 @@ use Illuminate\Support\Collection; */ class Search implements SearchInterface { + /** @var int */ + private $limit = 100; + /** @var User */ + private $user; + /** + * AttachmentRepository constructor. + * + * @param User $user + */ + public function __construct(User $user) + { + $this->user = $user; + } + + /** + * The search will assume that the user does not have so many accounts + * that this search should be paginated. + * * @param array $words * * @return Collection */ public function searchAccounts(array $words): Collection { - return auth()->user()->accounts()->with('accounttype')->where( - function (EloquentBuilder $q) use ($words) { - foreach ($words as $word) { - $q->orWhere('name', 'LIKE', '%' . e($word) . '%'); + $accounts = $this->user->accounts()->get(); + /** @var Collection $result */ + $result = $accounts->filter( + function (Account $account) use ($words) { + if ($this->strpos_arr(strtolower($account->name), $words)) { + return $account; } + + return false; } - )->get(); + ); + + $result = $result->slice(0, $this->limit); + + return $result; } /** @@ -51,46 +81,46 @@ class Search implements SearchInterface public function searchBudgets(array $words): Collection { /** @var Collection $set */ - $set = auth()->user()->budgets()->get(); - $newSet = $set->filter( - function (Budget $b) use ($words) { - $found = 0; - foreach ($words as $word) { - if (!(strpos(strtolower($b->name), strtolower($word)) === false)) { - $found++; - } + $set = auth()->user()->budgets()->get(); + /** @var Collection $result */ + $result = $set->filter( + function (Budget $budget) use ($words) { + if ($this->strpos_arr(strtolower($budget->name), $words)) { + return $budget; } - return $found > 0; + return false; } ); - return $newSet; + $result = $result->slice(0, $this->limit); + + return $result; } /** + * Search assumes the user does not have that many categories. So no paginated search. + * * @param array $words * * @return Collection */ public function searchCategories(array $words): Collection { - /** @var Collection $set */ - $set = auth()->user()->categories()->get(); - $newSet = $set->filter( - function (Category $c) use ($words) { - $found = 0; - foreach ($words as $word) { - if (!(strpos(strtolower($c->name), strtolower($word)) === false)) { - $found++; - } + $categories = $this->user->categories()->get(); + /** @var Collection $result */ + $result = $categories->filter( + function (Category $category) use ($words) { + if ($this->strpos_arr(strtolower($category->name), $words)) { + return $category; } - return $found > 0; + return false; } ); + $result = $result->slice(0, $this->limit); - return $newSet; + return $result; } /** @@ -101,7 +131,21 @@ class Search implements SearchInterface */ public function searchTags(array $words): Collection { - return new Collection; + $tags = $this->user->tags()->get(); + + /** @var Collection $result */ + $result = $tags->filter( + function (Tag $tag) use ($words) { + if ($this->strpos_arr(strtolower($tag->tag), $words)) { + return $tag; + } + + return false; + } + ); + $result = $result->slice(0, $this->limit); + + return $result; } /** @@ -111,40 +155,86 @@ class Search implements SearchInterface */ public function searchTransactions(array $words): Collection { - // decrypted transaction journals: - $decrypted = auth()->user()->transactionJournals()->expanded()->where('transaction_journals.encrypted', 0)->where( - function (EloquentBuilder $q) use ($words) { - foreach ($words as $word) { - $q->orWhere('transaction_journals.description', 'LIKE', '%' . e($word) . '%'); - } - } - )->get(TransactionJournal::queryFields()); + $pageSize = 100; + $processed = 0; + $page = 1; + $result = new Collection(); + do { + $collector = new JournalCollector($this->user); + $collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page); + $set = $collector->getPaginatedJournals(); + Log::debug(sprintf('Found %d journals to check. ', $set->count())); - // encrypted - $all = auth()->user()->transactionJournals()->expanded()->where('transaction_journals.encrypted', 1)->get(TransactionJournal::queryFields()); - $set = $all->filter( - function (TransactionJournal $journal) use ($words) { - foreach ($words as $word) { - $haystack = strtolower($journal->description); - $word = strtolower($word); - if (!(strpos($haystack, $word) === false)) { - return $journal; + // Filter transactions that match the given triggers. + $filtered = $set->filter( + function (Transaction $transaction) use ($words) { + // check descr of journal: + if ($this->strpos_arr(strtolower(strval($transaction->description)), $words)) { + return $transaction; } + + // check descr of transaction + if ($this->strpos_arr(strtolower(strval($transaction->transaction_description)), $words)) { + return $transaction; + } + + // return false: + return false; } + ); - return null; + Log::debug(sprintf('Found %d journals that match.', $filtered->count())); + // merge: + /** @var Collection $result */ + $result = $result->merge($filtered); + Log::debug(sprintf('Total count is now %d', $result->count())); + + // Update counters + $page++; + $processed += count($set); + + Log::debug(sprintf('Page is now %d, processed is %d', $page, $processed)); + + // Check for conditions to finish the loop + $reachedEndOfList = $set->count() < 1; + $foundEnough = $result->count() >= $this->limit; + + Log::debug(sprintf('reachedEndOfList: %s', var_export($reachedEndOfList, true))); + Log::debug(sprintf('foundEnough: %s', var_export($foundEnough, true))); + + } while (!$reachedEndOfList && !$foundEnough); + + $result = $result->slice(0, $this->limit); + + return $result; + } + + /** + * @param int $limit + */ + public function setLimit(int $limit) + { + $this->limit = $limit; + } + + /** + * @param string $haystack + * @param array $needle + * + * @return bool + */ + private function strpos_arr(string $haystack, array $needle) + { + if (strlen($haystack) === 0) { + return false; + } + foreach ($needle as $what) { + if (($pos = strpos($haystack, $what)) !== false) { + return true; } - ); - $filtered = $set->merge($decrypted); - $filtered = $filtered->sortBy( - function (TransactionJournal $journal) { - return intval($journal->date->format('U')); - } - ); + } - $filtered = $filtered->reverse(); - - return $filtered; + return false; } } diff --git a/app/Support/Twig/Journal.php b/app/Support/Twig/Journal.php index 62f99d89a6..e98f0ee070 100644 --- a/app/Support/Twig/Journal.php +++ b/app/Support/Twig/Journal.php @@ -146,7 +146,7 @@ class Journal extends Twig_Extension $array[] = '(cash)'; continue; } - $array[] = '' . e($entry->name) . ''; + $array[] = sprintf('%1$s', e($entry->name), route('accounts.show', $entry->id)); } $array = array_unique($array); $result = join(', ', $array); @@ -221,7 +221,7 @@ class Journal extends Twig_Extension $array[] = '(cash)'; continue; } - $array[] = '' . e($entry->name) . ''; + $array[] = sprintf('%1$s', e($entry->name), route('accounts.show', $entry->id)); } $array = array_unique($array); $result = join(', ', $array); @@ -253,12 +253,12 @@ class Journal extends Twig_Extension $budgets = []; // get all budgets: foreach ($journal->budgets as $budget) { - $budgets[] = '' . e($budget->name) . ''; + $budgets[] = sprintf('%1$s', e($budget->name), route('accounts.show', $budget->id)); } // and more! foreach ($journal->transactions as $transaction) { foreach ($transaction->budgets as $budget) { - $budgets[] = '' . e($budget->name) . ''; + $budgets[] = sprintf('%1$s', e($budget->name), route('accounts.show', $budget->id)); } } $string = join(', ', array_unique($budgets)); @@ -288,7 +288,7 @@ class Journal extends Twig_Extension $categories = []; // get all categories for the journal itself (easy): foreach ($journal->categories as $category) { - $categories[] = '' . e($category->name) . ''; + $categories[] = sprintf('%1$s', e($category->name), route('accounts.show', $category->id)); } if (count($categories) === 0) { $set = Category::distinct()->leftJoin('category_transaction', 'categories.id', '=', 'category_transaction.category_id') @@ -299,8 +299,7 @@ class Journal extends Twig_Extension ->get(['categories.*']); /** @var Category $category */ foreach ($set as $category) { - $categories[] = '' . e($category->name) - . ''; + $categories[] = sprintf('%1$s', e($category->name), route('accounts.show', $category->id)); } } @@ -324,16 +323,16 @@ class Journal extends Twig_Extension switch (true) { case $journal->isWithdrawal(): - $txt = ''; + $txt = sprintf('', trans('firefly.withdrawal')); break; case $journal->isDeposit(): - $txt = ''; + $txt = sprintf('', trans('firefly.deposit')); break; case $journal->isTransfer(): - $txt = ''; + $txt = sprintf('', trans('firefly.transfer')); break; case $journal->isOpeningBalance(): - $txt = ''; + $txt = sprintf('', trans('firefly.openingBalance')); break; default: $txt = ''; diff --git a/app/Support/Twig/Transaction.php b/app/Support/Twig/Transaction.php index ceb5e7695b..65e76090ac 100644 --- a/app/Support/Twig/Transaction.php +++ b/app/Support/Twig/Transaction.php @@ -125,7 +125,7 @@ class Transaction extends Twig_Extension && bccomp($amount, bcmul($transactionAmount, '-1')) !== 0 ) { // not equal? - return ' (' . Amount::formatWithCode($code, $amount, true) . ')'; + return sprintf(' (%s)', Amount::formatWithCode($code, $amount, true)); } return ''; @@ -144,7 +144,7 @@ class Transaction extends Twig_Extension 'splitJournalIndicator', function (int $journalId) { $count = TransactionModel::where('transaction_journal_id', $journalId)->whereNull('deleted_at')->count(); if ($count > 2) { - return ''; + return ''; } return ''; @@ -210,7 +210,7 @@ class Transaction extends Twig_Extension return '(cash)'; } - return '' . e($name) . ''; + return sprintf('%1$s', e($name), route('accounts.show', [$id])); }, ['is_safe' => ['html']] ); @@ -276,7 +276,7 @@ class Transaction extends Twig_Extension return '(cash)'; } - return '' . e($name) . ''; + return sprintf('%1$s', e($name), route('accounts.show', [$id])); }, ['is_safe' => ['html']] ); @@ -294,16 +294,16 @@ class Transaction extends Twig_Extension switch ($transaction->transaction_type_type) { case TransactionType::WITHDRAWAL: - $txt = ''; + $txt = sprintf('', trans('firefly.withdrawal')); break; case TransactionType::DEPOSIT: - $txt = ''; + $txt = sprintf('', trans('firefly.deposit')); break; case TransactionType::TRANSFER: - $txt = ''; + $txt = sprintf('', trans('firefly.transfer')); break; case TransactionType::OPENING_BALANCE: - $txt = ''; + $txt = sprintf('', trans('firefly.openingBalance')); break; default: $txt = ''; diff --git a/app/Support/Twig/Translation.php b/app/Support/Twig/Translation.php index f9401eee52..cb84f85c8e 100644 --- a/app/Support/Twig/Translation.php +++ b/app/Support/Twig/Translation.php @@ -35,7 +35,7 @@ class Translation extends Twig_Extension $filters[] = new Twig_SimpleFilter( '_', function ($name) { - return trans('firefly.' . $name); + return strval(trans(sprintf('firefly.%s', $name))); }, ['is_safe' => ['html']] ); diff --git a/composer.json b/composer.json index b443593700..feb695bcff 100755 --- a/composer.json +++ b/composer.json @@ -71,6 +71,7 @@ ], "post-install-cmd": [ "Illuminate\\Foundation\\ComposerScripts::postInstall", + "php artisan firefly:github-move", "php artisan optimize" ], "post-update-cmd": [ @@ -78,6 +79,7 @@ "php artisan firefly:upgrade-instructions", "php artisan firefly:upgrade-database", "php artisan firefly:verify", + "php artisan firefly:github-move", "php artisan optimize" ] }, diff --git a/composer.lock b/composer.lock index 2e8a6bc01f..193f6cd621 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "cc3d23620e727ee1f4741b2e83f8685f", + "hash": "12c9e9450c824d192b8c049989188b8e", "content-hash": "473d3c681e5c41989e9dced651a939df", "packages": [ { diff --git a/config/app.php b/config/app.php index 0611c68b7f..478fae8b8d 100755 --- a/config/app.php +++ b/config/app.php @@ -208,6 +208,7 @@ return [ FireflyIII\Providers\PiggyBankServiceProvider::class, FireflyIII\Providers\RuleServiceProvider::class, FireflyIII\Providers\RuleGroupServiceProvider::class, + FireflyIII\Providers\SearchServiceProvider::class, FireflyIII\Providers\TagServiceProvider::class, diff --git a/config/firefly.php b/config/firefly.php index 97b843110a..40f65d62d5 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -22,7 +22,7 @@ return [ 'single_user_mode' => true, ], 'chart' => 'chartjs', - 'version' => '4.1.5', + 'version' => '4.1.6', 'csv_import_enabled' => true, 'maxUploadSize' => 5242880, 'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'], diff --git a/public/js/ff/reports/default/all.js b/public/js/ff/reports/default/all.js index a76d6be0ec..9ab3c6c8d7 100644 --- a/public/js/ff/reports/default/all.js +++ b/public/js/ff/reports/default/all.js @@ -1,4 +1,4 @@ -/* globals startDate, showOnlyTop, showFullList, endDate, reportType, accountIds, inOutReportUrl, accountReportUrl */ +/* globals startDate, showOnlyTop, showFullList, endDate, reportType, expenseReportUri, accountIds, incExpReportUri,accountReportUri, incomeReportUri */ /* * all.js * Copyright (C) 2016 thegrumpydictator@gmail.com @@ -11,25 +11,20 @@ $(function () { "use strict"; - // load the account report, which this report shows: - loadAccountReport(); + loadAjaxPartial('accountReport', accountReportUri); - // load income / expense / difference: - loadInOutReport(); - - // trigger info click - triggerInfoClick(); - - // trigger list length things: - listLengthInitial(); + // load income and expense reports: + loadAjaxPartial('incomeReport', incomeReportUri); + loadAjaxPartial('expenseReport', expenseReportUri); + loadAjaxPartial('incomeVsExpenseReport', incExpReportUri); }); function triggerInfoClick() { "use strict"; // find the little info buttons and respond to them. - $('.firefly-info-button').unbind('clicl').click(clickInfoButton); + $('.firefly-info-button').unbind('click').click(clickInfoButton); } function listLengthInitial() { @@ -58,44 +53,6 @@ function triggerList(e) { return false; } -function loadInOutReport() { - "use strict"; - console.log('Going to grab ' + inOutReportUrl); - $.get(inOutReportUrl).done(placeInOutReport).fail(failInOutReport); -} - -function placeInOutReport(data) { - "use strict"; - $('#incomeReport').removeClass('loading').html(data.income); - $('#expenseReport').removeClass('loading').html(data.expenses); - $('#incomeVsExpenseReport').removeClass('loading').html(data.incomes_expenses); - listLengthInitial(); - triggerInfoClick(); -} - -function failInOutReport() { - "use strict"; - console.log('Fail in/out report data!'); - $('#incomeReport').removeClass('loading').addClass('general-chart-error'); - $('#expenseReport').removeClass('loading').addClass('general-chart-error'); - $('#incomeVsExpenseReport').removeClass('loading').addClass('general-chart-error'); -} - -function loadAccountReport() { - "use strict"; - $.get(accountReportUrl).done(placeAccountReport).fail(failAccountReport); -} - -function placeAccountReport(data) { - "use strict"; - $('#accountReport').removeClass('loading').html(data); -} - -function failAccountReport(data) { - "use strict"; - $('#accountReport').removeClass('loading').addClass('general-chart-error'); -} - function clickInfoButton(e) { "use strict"; // find all data tags, regardless of what they are: @@ -125,7 +82,111 @@ function respondInfoButton(data) { "use strict"; // remove wait cursor $('body').removeClass('waiting'); - $('#defaultModal').empty().html(data.html); - $('#defaultModal').modal('show'); + $('#defaultModal').empty().html(data.html).modal('show'); +} + +function loadAjaxPartial(holder, uri) { + "use strict"; + console.log('Going to grab URI ' + uri); + $.get(uri).done(function (data) { + displayAjaxPartial(data, holder); + }).fail(function () { + failAjaxPartial(uri, holder); + }); +} + +function displayAjaxPartial(data, holder) { + "use strict"; + console.log('Display stuff in ' + holder); + var obj = $('#' + holder); + obj.removeClass('loading').html(data); + + // call some often needed recalculations and what-not: + + // find a sortable table and make it sortable: + if (typeof $.bootstrapSortable === "function") { + $.bootstrapSortable(true); + } + + // find the info click things and respond to them: + triggerInfoClick(); + + // trigger list thing + listLengthInitial(); + + // budget thing + $('.budget-chart-activate').unbind('click').on('click', clickBudgetChart); +} + +function failAjaxPartial(uri, holder) { + "use strict"; + console.log('Failed to load' + uri); + $('#' + holder).removeClass('loading').addClass('general-chart-error'); + +} + +function clickBudgetChart(e) { + "use strict"; + var link = $(e.target); + var budgetId = link.data('budget'); + var URL = 'chart/budget/period/' + budgetId + '/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds; + var container = 'budget_chart'; + // if chart drawn is false, draw the first one, then + // set to true + if (chartDrawn == false) { + // do new chart: + + + $.getJSON(URL).done(function (data) { + console.log('Will draw new columnChart(' + URL + ')'); + + var ctx = document.getElementById(container).getContext("2d"); + var newData = {}; + newData.datasets = []; + + for (var i = 0; i < data.count; i++) { + newData.labels = data.labels; + var dataset = data.datasets[i]; + dataset.backgroundColor = fillColors[i]; + newData.datasets.push(dataset); + } + // completely new chart. + budgetChart = new Chart(ctx, { + type: 'bar', + data: data, + options: defaultColumnOptions + }); + + }).fail(function () { + $('#' + container).addClass('general-chart-error'); + }); + console.log('URL for column chart : ' + URL); + chartDrawn = true; + } else { + console.log('Will now handle remove data and add new!'); + $.getJSON(URL).done(function (data) { + console.log('Will draw updated columnChart(' + URL + ')'); + var newData = {}; + newData.datasets = []; + + for (var i = 0; i < data.count; i++) { + newData.labels = data.labels; + var dataset = data.datasets[i]; + dataset.backgroundColor = fillColors[i]; + newData.datasets.push(dataset); + } + // update the chart + console.log('Now update chart thing.'); + budgetChart.data.datasets = newData.datasets; + budgetChart.update(); + + }).fail(function () { + $('#' + container).addClass('general-chart-error'); + }); + + + } + + return false; } \ No newline at end of file diff --git a/public/js/ff/reports/default/month.js b/public/js/ff/reports/default/month.js index 7c15f922b6..4e34d95032 100644 --- a/public/js/ff/reports/default/month.js +++ b/public/js/ff/reports/default/month.js @@ -1,74 +1,15 @@ -/* globals google, budgetReportUrl, startDate ,reportURL, endDate , reportType ,accountIds, lineChart, categoryReportUrl, balanceReportUrl */ +/* globals google, categoryReportUri, budgetReportUri, balanceReportUri */ $(function () { "use strict"; drawChart(); - loadCategoryReport(); - loadBalanceReport(); - loadBudgetReport(); + loadAjaxPartial('categoryReport', categoryReportUri); + loadAjaxPartial('budgetReport', budgetReportUri); + loadAjaxPartial('balanceReport',balanceReportUri); }); -function loadCategoryReport() { - "use strict"; - console.log('Going to grab ' + categoryReportUrl); - $.get(categoryReportUrl).done(placeCategoryReport).fail(failCategoryReport); -} - -function loadBudgetReport() { - "use strict"; - console.log('Going to grab ' + budgetReportUrl); - $.get(budgetReportUrl).done(placeBudgetReport).fail(failBudgetReport); -} - - -function loadBalanceReport() { - "use strict"; - console.log('Going to grab ' + categoryReportUrl); - $.get(balanceReportUrl).done(placeBalanceReport).fail(failBalanceReport); -} - -function placeBudgetReport(data) { - "use strict"; - $('#budgetReport').removeClass('loading').html(data); - listLengthInitial(); - triggerInfoClick(); -} - -function placeBalanceReport(data) { - "use strict"; - $('#balanceReport').removeClass('loading').html(data); - listLengthInitial(); - triggerInfoClick(); -} - -function placeCategoryReport(data) { - "use strict"; - $('#categoryReport').removeClass('loading').html(data); - listLengthInitial(); - triggerInfoClick(); -} - -function failBudgetReport() { - "use strict"; - console.log('Fail budget report data!'); - $('#budgetReport').removeClass('loading').addClass('general-chart-error'); -} - -function failBalanceReport() { - "use strict"; - console.log('Fail balance report data!'); - $('#balanceReport').removeClass('loading').addClass('general-chart-error'); -} - -function failCategoryReport() { - "use strict"; - console.log('Fail category report data!'); - $('#categoryReport').removeClass('loading').addClass('general-chart-error'); -} - - function drawChart() { "use strict"; diff --git a/public/js/ff/reports/default/multi-year.js b/public/js/ff/reports/default/multi-year.js index 04b9ab3ddb..93ecb50039 100644 --- a/public/js/ff/reports/default/multi-year.js +++ b/public/js/ff/reports/default/multi-year.js @@ -1,13 +1,13 @@ -/* globals google, startDate ,reportURL, endDate , reportType ,accountIds , picker:true, minDate, year, month, columnChart, lineChart, stackedColumnChart */ +/* globals budgetMultiUri, accountIds */ $(function () { "use strict"; drawChart(); + loadAjaxPartial('budgetMultiYear', budgetMultiUri); }); - function drawChart() { "use strict"; diff --git a/public/js/ff/reports/default/year.js b/public/js/ff/reports/default/year.js index e0cb494772..0609105bf6 100644 --- a/public/js/ff/reports/default/year.js +++ b/public/js/ff/reports/default/year.js @@ -1,4 +1,4 @@ -/* globals google, accountIds, budgetYearOverviewUrl */ +/* globals google, accountIds, budgetYearOverviewUri */ var chartDrawn; var budgetChart; @@ -7,30 +7,9 @@ $(function () { chartDrawn = false; drawChart(); - // - loadBudgetOverview(); + loadAjaxPartial('budgetOverview',budgetYearOverviewUri); }); -function loadBudgetOverview() { - "use strict"; - console.log('Going to grab ' + budgetYearOverviewUrl); - $.get(budgetYearOverviewUrl).done(placeBudgetOverview).fail(failBudgetOverview); -} - -function placeBudgetOverview(data) { - "use strict"; - $('#budgetOverview').removeClass('loading').html(data); - $('.budget-chart-activate').on('click', clickBudgetChart); -} - -function failBudgetOverview() { - "use strict"; - console.log('Fail budget overview data!'); - $('#budgetOverview').removeClass('loading').addClass('general-chart-error'); -} - - - function drawChart() { "use strict"; @@ -40,68 +19,3 @@ function drawChart() { } - -function clickBudgetChart(e) { - "use strict"; - var link = $(e.target); - var budgetId = link.data('budget'); - var URL = 'chart/budget/period/' + budgetId + '/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds; - var container = 'budget_chart'; - // if chart drawn is false, draw the first one, then - // set to true - if (chartDrawn == false) { - // do new chart: - - - $.getJSON(URL).done(function (data) { - console.log('Will draw new columnChart(' + URL + ')'); - - var ctx = document.getElementById(container).getContext("2d"); - var newData = {}; - newData.datasets = []; - - for (var i = 0; i < data.count; i++) { - newData.labels = data.labels; - var dataset = data.datasets[i]; - dataset.backgroundColor = fillColors[i]; - newData.datasets.push(dataset); - } - // completely new chart. - budgetChart = new Chart(ctx, { - type: 'bar', - data: data, - options: defaultColumnOptions - }); - - }).fail(function () { - $('#' + container).addClass('general-chart-error'); - }); - console.log('URL for column chart : ' + URL); - chartDrawn = true; - } else { - console.log('Will now handle remove data and add new!'); - $.getJSON(URL).done(function (data) { - console.log('Will draw updated columnChart(' + URL + ')'); - var newData = {}; - newData.datasets = []; - - for (var i = 0; i < data.count; i++) { - newData.labels = data.labels; - var dataset = data.datasets[i]; - dataset.backgroundColor = fillColors[i]; - newData.datasets.push(dataset); - } - // update the chart - console.log('Now update chart thing.'); - budgetChart.data.datasets = newData.datasets; - budgetChart.update(); - - }).fail(function () { - $('#' + container).addClass('general-chart-error'); - }); - - - } - - return false; -} \ No newline at end of file diff --git a/resources/lang/de_DE/csv.php b/resources/lang/de_DE/csv.php index 9fdab7ad2f..ca1d7dc2ae 100644 --- a/resources/lang/de_DE/csv.php +++ b/resources/lang/de_DE/csv.php @@ -15,7 +15,7 @@ return [ 'import_configure_title' => 'Konfigurieren Sie Ihren Import', 'import_configure_intro' => 'Es gibt einige Optionen für Ihren CSV-Import. Bitte geben Sie an, ob Ihre CSV-Datei Überschriften in der ersten Spalte enthält und was das Datumsformat in Ihrem Datumsfeld ist. Dieses kann einige Experimente erfordern. Das Trennzeichen ist in der Regel ein ",", könnte aber auch ein "." sein. Bitte überprüfen Sie dieses sorgfältig.', - 'import_configure_form' => 'Formular', + 'import_configure_form' => 'Basic CSV import options', 'header_help' => 'Hier auswählen, wenn die ersten Zeilen der CSV-Datei die Spaltenüberschriften sind', 'date_help' => 'Datumsformat in ihrer CSV-Datei. Geben Sie das Format so an, wie es diese Seite zeigt. Die Standardeinstellung ergibt Daten die so aussehen: :dateExample.', 'delimiter_help' => 'Wählen Sie das Trennzeichen, welches in ihrer Datei genutzt wird. Wenn Sie nicht sicher sind ist Komma die sicherste Option.', @@ -24,7 +24,6 @@ return [ // roles 'column_roles_title' => 'Define column roles', - 'column_roles_text' => '

Firefly III cannot guess what data each column contains. You must tell Firefly which kinds of data to expect. The example data can guide you into picking the correct type from the dropdown. If a column cannot be matched to a useful data type, please let me know by creating an issue.

Some values in your CSV file, such as account names or categories, may already exist in your Firefly III database. If you select "map these values" Firefly will not attempt to search for matching values itself but allow you to match the CSV values against the values in your database. This allows you to fine-tune the import.

', 'column_roles_table' => 'Tabelle', 'column_name' => 'Name der Spalte', 'column_example' => 'Column example data', diff --git a/resources/lang/de_DE/firefly.php b/resources/lang/de_DE/firefly.php index 4e9bbd7489..eedc8f4c45 100644 --- a/resources/lang/de_DE/firefly.php +++ b/resources/lang/de_DE/firefly.php @@ -67,11 +67,25 @@ return [ 'warning_much_data' => ':days Tage an Daten können eine Weile dauern zu laden.', 'registered' => 'Sie haben sich erfolgreich registriert!', 'search' => 'Suche', + 'search_found_accounts' => 'Found :count account(s) for your query.', + 'search_found_categories' => 'Found :count category(ies) for your query.', + 'search_found_budgets' => 'Found :count budget(s) for your query.', + 'search_found_tags' => 'Found :count tag(s) for your query.', + 'search_found_transactions' => 'Found :count transaction(s) for your query.', + 'results_limited' => 'The results are limited to :count entries.', + 'tagbalancingAct' => 'Balancing act', + 'tagadvancePayment' => 'Advance payment', + 'tagnothing' => '', + 'Default asset account' => 'Default asset account', 'no_budget_pointer' => 'Sie scheinen keine Budgets festgelegt zu haben. Sie sollten welche auf der Budget-Seite erstellen. Budgets können Ihnen helfen ihre Ausgaben zu verfolgen.', + 'Savings account' => 'Savings account', + 'Credit card' => 'Credit card', 'source_accounts' => 'Herkunftskonto', 'destination_accounts' => 'Zielkonto', 'user_id_is' => 'Your user id is :user', 'field_supports_markdown' => 'This field supports Markdown.', + 'need_more_help' => 'If you need more help using Firefly III, please open a ticker on Github.', + 'nothing_to_display' => 'There are no transactions to show you', // repeat frequencies: 'repeat_freq_yearly' => 'yearly', @@ -842,14 +856,9 @@ return [ 'import_complete_text' => 'Der Import ist bereit zu starten. Alle Einstellungen wurden von Ihnen erledigt. Bitte laden Sie die Konfigurationsdatei herunter. Diese wird Ihnen beim Import helfen, sollte dieser nicht wie gewünscht verlaufen. Um den Import tatsächlich zu starten führen Sie den folgenden Befehl in der Konsole aus oder nutzen Sie den Web-basierten Import. Abhängig von ihrer Konfiguration wird Ihnen der Konsolenimport mehr Rückmeldungen geben.', 'import_download_config' => 'Download configuration', 'import_start_import' => 'Start import', - 'import_intro_beta' => 'Die Importfunktion von Firefly III ist im Beta-Stadium. Viele unterschiedliche Nutzer von Firefly III haben viele verschiedene Datei getestet. Auch wenn die einzelnen Komponenten des Imports funktionieren (wirklich), können Fehler auftreten. Wenn ihre Datei nicht in Firefly importiert werden kann lesen Sie bitte diese Wiki-Seite, damit ich den aufgetretenen Fehler beheben kann.', 'import_data' => 'Daten importieren', 'import_data_full' => 'Importieren Sie Daten in Firefly III', 'import' => 'Import', - 'import_intro_what_it_does' => 'Diese Seite erlaubt Ihnen das Importieren von Daten in Firefly III. Exportieren Sie dazu Daten von ihrer Bank oder anderen Finanzverwaltungen. Laden Sie die Dateien anschließend hier hoch. Firefly III wird die Daten daraufhin konvertieren. Sie müssen dafür einige Informationen angeben. Bitte wählen Sie nun eine Datei aus folgen Sie den Anweisungen.', - 'import_intro_import_conf_title' => 'Importkonfiguration', - 'import_intro_beta_warning' => 'Achtung', - 'import_intro_import_conf_text' => 'Wie Sie auf den nächsten Seite feststellen werden, hat die Importfunktion viele Einstellungen. Diese Einstellungen sind vor allem von ihrer Bank (oder Finanzverwaltungssoftware), von der ihre Daten stammen, abhängig. Es ist sehr wahrscheinlich, dass bereits eine andere Person solch eine Datei importiert hat und die jeweile Konfigurationsdatei bereitstellt. Bitte schauen Sie daher in der Übersicht der Import-Einstellungen nach, ob bereits eine Konfiguratiosdatei für ihre Bank vorhanden ist. Wenn eine Datei existiert sollten Sie diese herunterladen und hier hochladen. Es wird Ihnen eine Menge Zeit sparen!', 'import_file_help' => 'Select your file', 'import_status_settings_complete' => 'The import is ready to start.', 'import_status_import_complete' => 'The import has completed.', @@ -865,11 +874,9 @@ return [ 'import_double' => 'Row #:row: This row has been imported before, and is stored in :description.', 'import_finished_all' => 'The import has finished. Please check out the results below.', 'import_with_key' => 'Import with key \':key\'', - - 'import_share_configuration' => 'Bitte denken Sie darüber nach ihre Konfiguration herunterzuladen und in der Übersicht der Import-Einstellungen zu teilen. Dieses erlaubt es anderen Nutzern von Firefly III ihre Daten unkomplizierter zu importieren.', - - 'import_finished_report' => 'The import has finished. Please note any errors in the block above this line. All transactions imported during this particular session have been tagged, and you can check them out below. ', - 'import_finished_link' => 'The transactions imported can be found in tag :tag.', - - + 'import_share_configuration' => 'Bitte denken Sie darüber nach ihre Konfiguration herunterzuladen und in der Übersicht der Import-Einstellungen zu teilen. Dieses erlaubt es anderen Nutzern von Firefly III ihre Daten unkomplizierter zu importieren.', + 'import_finished_report' => 'The import has finished. Please note any errors in the block above this line. All transactions imported during this particular session have been tagged, and you can check them out below. ', + 'import_finished_link' => 'The transactions imported can be found in tag :tag.', + 'need_at_least_one_account' => 'You need at least one asset account to be able to create piggy banks', + 'see_help_top_right' => 'For more information, please check out the help pages using the icon in the top right corner of the page.', ]; \ No newline at end of file diff --git a/resources/lang/en_US/csv.php b/resources/lang/en_US/csv.php index fe83034aa2..6b5abd3431 100644 --- a/resources/lang/en_US/csv.php +++ b/resources/lang/en_US/csv.php @@ -15,7 +15,7 @@ return [ 'import_configure_title' => 'Configure your import', 'import_configure_intro' => 'There are some options for your CSV import. Please indicate if your CSV file contains headers on the first column, and what the date format of your date-fields is. That might require some experimentation. The field delimiter is usually a ",", but could also be a ";". Check this carefully.', - 'import_configure_form' => 'Form', + 'import_configure_form' => 'Basic CSV import options', 'header_help' => 'Check this if the first row of your CSV file are the column titles', 'date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', 'delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', @@ -24,7 +24,6 @@ return [ // roles 'column_roles_title' => 'Define column roles', - 'column_roles_text' => '

Firefly III cannot guess what data each column contains. You must tell Firefly which kinds of data to expect. The example data can guide you into picking the correct type from the dropdown. If a column cannot be matched to a useful data type, please let me know by creating an issue.

Some values in your CSV file, such as account names or categories, may already exist in your Firefly III database. If you select "map these values" Firefly will not attempt to search for matching values itself but allow you to match the CSV values against the values in your database. This allows you to fine-tune the import.

', 'column_roles_table' => 'Table', 'column_name' => 'Name of column', 'column_example' => 'Column example data', diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 435ab3f18b..b7643c4751 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -67,11 +67,25 @@ return [ 'warning_much_data' => ':days days of data may take a while to load.', 'registered' => 'You have registered successfully!', 'search' => 'Search', + 'search_found_accounts' => 'Found :count account(s) for your query.', + 'search_found_categories' => 'Found :count category(ies) for your query.', + 'search_found_budgets' => 'Found :count budget(s) for your query.', + 'search_found_tags' => 'Found :count tag(s) for your query.', + 'search_found_transactions' => 'Found :count transaction(s) for your query.', + 'results_limited' => 'The results are limited to :count entries.', + 'tagbalancingAct' => 'Balancing act', + 'tagadvancePayment' => 'Advance payment', + 'tagnothing' => '', + 'Default asset account' => 'Default asset account', 'no_budget_pointer' => 'You seem to have no budgets yet. You should create some on the budgets-page. Budgets can help you keep track of expenses.', + 'Savings account' => 'Savings account', + 'Credit card' => 'Credit card', 'source_accounts' => 'Source account(s)', 'destination_accounts' => 'Destination account(s)', 'user_id_is' => 'Your user id is :user', 'field_supports_markdown' => 'This field supports Markdown.', + 'need_more_help' => 'If you need more help using Firefly III, please open a ticker on Github.', + 'nothing_to_display' => 'There are no transactions to show you', // repeat frequencies: 'repeat_freq_yearly' => 'yearly', @@ -842,14 +856,9 @@ return [ 'import_complete_text' => 'The import is ready to start. All the configuration you needed to do has been done. Please download the configuration file. It will help you with the import should it not go as planned. To actually run the import, you can either execute the following command in your console, or run the web-based import. Depending on your configuration, the console import will give you more feedback.', 'import_download_config' => 'Download configuration', 'import_start_import' => 'Start import', - 'import_intro_beta' => 'The import function of Firefly III is in beta. Many users of Firefly III have tried many different files. Although each individual compontent of this import routine works (really), the combination might break. If your file cannot be imported by Firefly, please read this wiki page so I can fix the problem you have run into.', 'import_data' => 'Import data', 'import_data_full' => 'Import data into Firefly III', 'import' => 'Import', - 'import_intro_what_it_does' => 'This page allows you to import data into Firefly III. To do so, export data from your bank, or from another financial management system. Upload that file here. Firefly III will convert the data. You need to give it some directions. Please select a file and follow the instructions.', - 'import_intro_import_conf_title' => 'Import "configuration"', - 'import_intro_beta_warning' => 'Warning', - 'import_intro_import_conf_text' => 'As you will discover over the next few pages, this import routine has a lot of settings. These settings are mainly dependent on the bank (or financial management software) your file comes from. There is a good chance somebody else already imported such a file and has shared their configuration file. Please visit the import configuration center to see if there already is a configuration available for your bank or system. If there is, you should download this configuration file and upload it here as well. It will save you a lot of time!', 'import_file_help' => 'Select your file', 'import_status_settings_complete' => 'The import is ready to start.', 'import_status_import_complete' => 'The import has completed.', @@ -865,11 +874,9 @@ return [ 'import_double' => 'Row #:row: This row has been imported before, and is stored in :description.', 'import_finished_all' => 'The import has finished. Please check out the results below.', 'import_with_key' => 'Import with key \':key\'', - - 'import_share_configuration' => 'Please consider downloading your configuration and sharing it at the import configuration center. This will allow other users of Firefly III to import their files more easily.', - - 'import_finished_report' => 'The import has finished. Please note any errors in the block above this line. All transactions imported during this particular session have been tagged, and you can check them out below. ', - 'import_finished_link' => 'The transactions imported can be found in tag :tag.', - - + 'import_share_configuration' => 'Please consider downloading your configuration and sharing it at the import configuration center. This will allow other users of Firefly III to import their files more easily.', + 'import_finished_report' => 'The import has finished. Please note any errors in the block above this line. All transactions imported during this particular session have been tagged, and you can check them out below. ', + 'import_finished_link' => 'The transactions imported can be found in tag :tag.', + 'need_at_least_one_account' => 'You need at least one asset account to be able to create piggy banks', + 'see_help_top_right' => 'For more information, please check out the help pages using the icon in the top right corner of the page.', ]; diff --git a/resources/lang/fr_FR/csv.php b/resources/lang/fr_FR/csv.php index b1038b5bca..37f1a5728d 100644 --- a/resources/lang/fr_FR/csv.php +++ b/resources/lang/fr_FR/csv.php @@ -15,7 +15,7 @@ return [ 'import_configure_title' => 'Configurer l\'import', 'import_configure_intro' => 'Il y a des options pour l\'import CSV. Veuillez indiquer si votre fichier CSV contient les en-têtes dans la première colonne, et quel est le format de la date de votre champs date. Cela peut nécessiter quelques essais. Le délimiteur de champ est généralement un «, », mais pourrait également être un « ; ». Cochez cette case avec soin.', - 'import_configure_form' => 'Formulaire', + 'import_configure_form' => 'Basic CSV import options', 'header_help' => 'Cochez cette case si la première ligne de votre fichier CSV contient les entêtes des colonnes', 'date_help' => 'Le format de la date et de l’heure dans votre fichier CSV. Utiliser les formats comme indiqué sur cette page. La valeur par défaut va extraire les dates qui ressemblent à ceci: :dateExample.', 'delimiter_help' => 'Choisissez le délimiteur de champ qui est utilisé dans votre fichier d’entrée. Si vous n’êtes pas certain, la virgule est l’option la plus sûre.', @@ -24,7 +24,6 @@ return [ // roles 'column_roles_title' => 'Définir le rôle des colonnes', - 'column_roles_text' => '

Firefly III cannot guess what data each column contains. You must tell Firefly which kinds of data to expect. The example data can guide you into picking the correct type from the dropdown. If a column cannot be matched to a useful data type, please let me know by creating an issue.

Some values in your CSV file, such as account names or categories, may already exist in your Firefly III database. If you select "map these values" Firefly will not attempt to search for matching values itself but allow you to match the CSV values against the values in your database. This allows you to fine-tune the import.

', 'column_roles_table' => 'Tableau', 'column_name' => 'Nom de colonne', 'column_example' => 'Exemple', diff --git a/resources/lang/fr_FR/firefly.php b/resources/lang/fr_FR/firefly.php index 1aa1d2cd30..8a44ed050b 100644 --- a/resources/lang/fr_FR/firefly.php +++ b/resources/lang/fr_FR/firefly.php @@ -67,11 +67,25 @@ return [ 'warning_much_data' => ':days de données peuvent prendre un certain temps à charger.', 'registered' => 'Vous avez été enregistré avec succès !', 'search' => 'Rechercher', + 'search_found_accounts' => 'Found :count account(s) for your query.', + 'search_found_categories' => 'Found :count category(ies) for your query.', + 'search_found_budgets' => 'Found :count budget(s) for your query.', + 'search_found_tags' => 'Found :count tag(s) for your query.', + 'search_found_transactions' => 'Found :count transaction(s) for your query.', + 'results_limited' => 'The results are limited to :count entries.', + 'tagbalancingAct' => 'Balancing act', + 'tagadvancePayment' => 'Advance payment', + 'tagnothing' => '', + 'Default asset account' => 'Default asset account', 'no_budget_pointer' => 'Vous semblez n’avoir encore aucun budget. Vous devez en créer sur la page des budgets. Les budgets peuvent vous aider à garder une trace des dépenses.', + 'Savings account' => 'Savings account', + 'Credit card' => 'Credit card', 'source_accounts' => 'Compte(s) source', 'destination_accounts' => 'Compte(s) de destination', 'user_id_is' => 'Your user id is :user', 'field_supports_markdown' => 'This field supports Markdown.', + 'need_more_help' => 'If you need more help using Firefly III, please open a ticker on Github.', + 'nothing_to_display' => 'There are no transactions to show you', // repeat frequencies: 'repeat_freq_yearly' => 'yearly', @@ -842,14 +856,9 @@ return [ 'import_complete_text' => 'The import is ready to start. All the configuration you needed to do has been done. Please download the configuration file. It will help you with the import should it not go as planned. To actually run the import, you can either execute the following command in your console, or run the web-based import. Depending on your configuration, the console import will give you more feedback.', 'import_download_config' => 'Download configuration', 'import_start_import' => 'Start import', - 'import_intro_beta' => 'The import function of Firefly III is in beta. Many users of Firefly III have tried many different files. Although each individual compontent of this import routine works (really), the combination might break. If your file cannot be imported by Firefly, please read this wiki page so I can fix the problem you have run into.', 'import_data' => 'Import data', 'import_data_full' => 'Import data into Firefly III', 'import' => 'Import', - 'import_intro_what_it_does' => 'This page allows you to import data into Firefly III. To do so, export data from your bank, or from another financial management system. Upload that file here. Firefly III will convert the data. You need to give it some directions. Please select a file and follow the instructions.', - 'import_intro_import_conf_title' => 'Import "configuration"', - 'import_intro_beta_warning' => 'Warning', - 'import_intro_import_conf_text' => 'As you will discover over the next few pages, this import routine has a lot of settings. These settings are mainly dependent on the bank (or financial management software) your file comes from. There is a good chance somebody else already imported such a file and has shared their configuration file. Please visit the import configuration center to see if there already is a configuration available for your bank or system. If there is, you should download this configuration file and upload it here as well. It will save you a lot of time!', 'import_file_help' => 'Select your file', 'import_status_settings_complete' => 'The import is ready to start.', 'import_status_import_complete' => 'The import has completed.', @@ -865,11 +874,9 @@ return [ 'import_double' => 'Row #:row: This row has been imported before, and is stored in :description.', 'import_finished_all' => 'The import has finished. Please check out the results below.', 'import_with_key' => 'Import with key \':key\'', - - 'import_share_configuration' => 'Please consider downloading your configuration and sharing it at the import configuration center. This will allow other users of Firefly III to import their files more easily.', - - 'import_finished_report' => 'The import has finished. Please note any errors in the block above this line. All transactions imported during this particular session have been tagged, and you can check them out below. ', - 'import_finished_link' => 'The transactions imported can be found in tag :tag.', - - + 'import_share_configuration' => 'Please consider downloading your configuration and sharing it at the import configuration center. This will allow other users of Firefly III to import their files more easily.', + 'import_finished_report' => 'The import has finished. Please note any errors in the block above this line. All transactions imported during this particular session have been tagged, and you can check them out below. ', + 'import_finished_link' => 'The transactions imported can be found in tag :tag.', + 'need_at_least_one_account' => 'You need at least one asset account to be able to create piggy banks', + 'see_help_top_right' => 'For more information, please check out the help pages using the icon in the top right corner of the page.', ]; \ No newline at end of file diff --git a/resources/lang/hr_HR/csv.php b/resources/lang/hr_HR/csv.php index 576c132144..4acb52efdc 100644 --- a/resources/lang/hr_HR/csv.php +++ b/resources/lang/hr_HR/csv.php @@ -15,7 +15,7 @@ return [ 'import_configure_title' => 'Configure your import', 'import_configure_intro' => 'There are some options for your CSV import. Please indicate if your CSV file contains headers on the first column, and what the date format of your date-fields is. That might require some experimentation. The field delimiter is usually a ",", but could also be a ";". Check this carefully.', - 'import_configure_form' => 'Form', + 'import_configure_form' => 'Basic CSV import options', 'header_help' => 'Check this if the first row of your CSV file are the column titles', 'date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', 'delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', @@ -24,7 +24,6 @@ return [ // roles 'column_roles_title' => 'Define column roles', - 'column_roles_text' => '

Firefly III cannot guess what data each column contains. You must tell Firefly which kinds of data to expect. The example data can guide you into picking the correct type from the dropdown. If a column cannot be matched to a useful data type, please let me know by creating an issue.

Some values in your CSV file, such as account names or categories, may already exist in your Firefly III database. If you select "map these values" Firefly will not attempt to search for matching values itself but allow you to match the CSV values against the values in your database. This allows you to fine-tune the import.

', 'column_roles_table' => 'Table', 'column_name' => 'Name of column', 'column_example' => 'Column example data', diff --git a/resources/lang/hr_HR/firefly.php b/resources/lang/hr_HR/firefly.php index 7b14be178c..495bfab429 100644 --- a/resources/lang/hr_HR/firefly.php +++ b/resources/lang/hr_HR/firefly.php @@ -67,11 +67,25 @@ return [ 'warning_much_data' => ':days days of data may take a while to load.', 'registered' => 'You have registered successfully!', 'search' => 'Search', + 'search_found_accounts' => 'Found :count account(s) for your query.', + 'search_found_categories' => 'Found :count category(ies) for your query.', + 'search_found_budgets' => 'Found :count budget(s) for your query.', + 'search_found_tags' => 'Found :count tag(s) for your query.', + 'search_found_transactions' => 'Found :count transaction(s) for your query.', + 'results_limited' => 'The results are limited to :count entries.', + 'tagbalancingAct' => 'Balancing act', + 'tagadvancePayment' => 'Advance payment', + 'tagnothing' => '', + 'Default asset account' => 'Default asset account', 'no_budget_pointer' => 'You seem to have no budgets yet. You should create some on the budgets-page. Budgets can help you keep track of expenses.', + 'Savings account' => 'Savings account', + 'Credit card' => 'Credit card', 'source_accounts' => 'Source account(s)', 'destination_accounts' => 'Destination account(s)', 'user_id_is' => 'Your user id is :user', 'field_supports_markdown' => 'This field supports Markdown.', + 'need_more_help' => 'If you need more help using Firefly III, please open a ticker on Github.', + 'nothing_to_display' => 'There are no transactions to show you', // repeat frequencies: 'repeat_freq_yearly' => 'yearly', @@ -842,14 +856,9 @@ return [ 'import_complete_text' => 'The import is ready to start. All the configuration you needed to do has been done. Please download the configuration file. It will help you with the import should it not go as planned. To actually run the import, you can either execute the following command in your console, or run the web-based import. Depending on your configuration, the console import will give you more feedback.', 'import_download_config' => 'Download configuration', 'import_start_import' => 'Start import', - 'import_intro_beta' => 'The import function of Firefly III is in beta. Many users of Firefly III have tried many different files. Although each individual compontent of this import routine works (really), the combination might break. If your file cannot be imported by Firefly, please read this wiki page so I can fix the problem you have run into.', 'import_data' => 'Import data', 'import_data_full' => 'Import data into Firefly III', 'import' => 'Import', - 'import_intro_what_it_does' => 'This page allows you to import data into Firefly III. To do so, export data from your bank, or from another financial management system. Upload that file here. Firefly III will convert the data. You need to give it some directions. Please select a file and follow the instructions.', - 'import_intro_import_conf_title' => 'Import "configuration"', - 'import_intro_beta_warning' => 'Warning', - 'import_intro_import_conf_text' => 'As you will discover over the next few pages, this import routine has a lot of settings. These settings are mainly dependent on the bank (or financial management software) your file comes from. There is a good chance somebody else already imported such a file and has shared their configuration file. Please visit the import configuration center to see if there already is a configuration available for your bank or system. If there is, you should download this configuration file and upload it here as well. It will save you a lot of time!', 'import_file_help' => 'Select your file', 'import_status_settings_complete' => 'The import is ready to start.', 'import_status_import_complete' => 'The import has completed.', @@ -865,11 +874,9 @@ return [ 'import_double' => 'Row #:row: This row has been imported before, and is stored in :description.', 'import_finished_all' => 'The import has finished. Please check out the results below.', 'import_with_key' => 'Import with key \':key\'', - - 'import_share_configuration' => 'Please consider downloading your configuration and sharing it at the import configuration center. This will allow other users of Firefly III to import their files more easily.', - - 'import_finished_report' => 'The import has finished. Please note any errors in the block above this line. All transactions imported during this particular session have been tagged, and you can check them out below. ', - 'import_finished_link' => 'The transactions imported can be found in tag :tag.', - - + 'import_share_configuration' => 'Please consider downloading your configuration and sharing it at the import configuration center. This will allow other users of Firefly III to import their files more easily.', + 'import_finished_report' => 'The import has finished. Please note any errors in the block above this line. All transactions imported during this particular session have been tagged, and you can check them out below. ', + 'import_finished_link' => 'The transactions imported can be found in tag :tag.', + 'need_at_least_one_account' => 'You need at least one asset account to be able to create piggy banks', + 'see_help_top_right' => 'For more information, please check out the help pages using the icon in the top right corner of the page.', ]; \ No newline at end of file diff --git a/resources/lang/nl_NL/csv.php b/resources/lang/nl_NL/csv.php index f4fcefc62d..876ca796a4 100644 --- a/resources/lang/nl_NL/csv.php +++ b/resources/lang/nl_NL/csv.php @@ -15,7 +15,7 @@ return [ 'import_configure_title' => 'Import configureren', 'import_configure_intro' => 'Hier zie je enkele opties voor jouw CSV bestand. Geef aan of je CSV bestand kolomtitels bevat, en hoe het datumveld is opgebouwd. Hier moet je wellicht wat experimenteren. Het scheidingsteken is meestal een ",", maar dat kan ook een ";" zijn. Controleer dit zorgvuldig.', - 'import_configure_form' => 'Formulier', + 'import_configure_form' => 'Standaard CSV import opties', 'header_help' => 'Vink hier als de eerste rij kolomtitels bevat', 'date_help' => 'Datum/tijd formaat in jouw CSV bestand. Volg het formaat zoals ze het op deze pagina uitleggen. Het standaardformaat ziet er zo uit: :dateExample.', 'delimiter_help' => 'Kies het veldscheidingsteken dat in jouw bestand wordt gebruikt. Als je het niet zeker weet, is de komma de beste optie.', @@ -24,7 +24,6 @@ return [ // roles 'column_roles_title' => 'Bepaal de inhoud van elke kolom', - 'column_roles_text' => '

Firefly III kan niet raden welke soort gegevens er in elke kolom staan. Dat moet je er bij vertellen. Gebruik de voorbeeldgegevens en kies het juiste type uit de dropdown. Staat jouw type er niet bij? Open dan een ticket.

Sommige waarden, zoals rekeningnummers en namen staan wellicht al in jouw Firefly III database. Als je dan het vinkje vinkt, kan je zelf de link leggen tussen wat er in het CSV bestand staat en wat er in de database staat. Hiermee kan je de import sturen.

', 'column_roles_table' => 'Tabel', 'column_name' => 'Kolomnaam', 'column_example' => 'Voorbeeldgegevens', diff --git a/resources/lang/nl_NL/firefly.php b/resources/lang/nl_NL/firefly.php index 6bdfcbe186..d872491d34 100644 --- a/resources/lang/nl_NL/firefly.php +++ b/resources/lang/nl_NL/firefly.php @@ -67,11 +67,25 @@ return [ 'warning_much_data' => 'Het kan even duren voor :days dagen aan gegevens geladen zijn.', 'registered' => 'Je bent geregistreerd!', 'search' => 'Zoeken', + 'search_found_accounts' => ':count rekening(en) gevonden bij je zoekopdracht.', + 'search_found_categories' => ':count categorie(en) gevonden bij je zoekopdracht.', + 'search_found_budgets' => ':count budget(ten) gevonden bij je zoekopdracht.', + 'search_found_tags' => ':count tag(s) gevonden bij je zoekopdracht.', + 'search_found_transactions' => ':count transactie(s) gevonden bij je zoekopdracht.', + 'results_limited' => 'Er worden maximaal :count resultaten getoond.', + 'tagbalancingAct' => 'Balancerende tag', + 'tagadvancePayment' => 'Vooruitbetaalde tag', + 'tagnothing' => '', + 'Default asset account' => 'Standaard betaalrekening', 'no_budget_pointer' => 'Je hebt nog geen budgetten. Maak er een aantal op de budgetten-pagina. Met budgetten kan je je uitgaven beter bijhouden.', + 'Savings account' => 'Spaarrekening', + 'Credit card' => 'Credit card', 'source_accounts' => 'Bronrekening(en)', 'destination_accounts' => 'Doelrekening(en)', 'user_id_is' => 'Je gebruikersnummer is :user', 'field_supports_markdown' => 'Dit veld ondersteunt Markdown.', + 'need_more_help' => 'Als je meer hulp nodig hebt met Firefly III, open dan een ticket op Github.', + 'nothing_to_display' => 'Er zijn hier geen transacties te zien', // repeat frequencies: 'repeat_freq_yearly' => 'jaarlijks', @@ -842,14 +856,9 @@ return [ 'import_complete_text' => 'De import kan beginnen. Alle configuratie is opgeslagen. Download dit bestand. Het kan schelen als je de import opnieuw moet doen. Om daadwerkelijk te beginnen, gebruik je of het commando in je console, of de website. Afhankelijk van hoe je Firefly III hebt ingesteld, geeft de console-methode meer feedback.', 'import_download_config' => 'Download importconfiguratie', 'import_start_import' => 'Import starten', - 'import_intro_beta' => 'De importfunctie van Firefly III is in bèta. Er zijn al veel bestanden geprobeerd, en elk individueel component zou moeten werken. Gecombineerd echter, kan er wat stuk gaan. Als jouw bestand problemen geeft, lees dan deze wikipagina zodat ik eventuele bugs kan fixen.', 'import_data' => 'Importeer data', 'import_data_full' => 'Gegevens importeren in Firefly III', 'import' => 'Import', - 'import_intro_what_it_does' => 'Vanaf deze pagina kan je gegevens importeren in Firefly III. Exporteer ze eerst vanuit je internetbankieren of financiële software. Upload dat bestandje hier. Firefly III zal de transacties omzetten. Je zult wel wat hints moeten geven. Selecteer alsjeblieft een bestand en volg de instructies.', - 'import_intro_import_conf_title' => 'Importconfiguratie', - 'import_intro_beta_warning' => 'Waarschuwing', - 'import_intro_import_conf_text' => 'Zoals je wel zult ontdekken heeft de import-routine veel opties. Deze opties verschillen voornamelijk per bank (of softwarepakket). Grote kans dat iemand anders je al voor was en zijn of haar configuratiebestand heeft gedeeld. Kijk vlug op de configuratiebestand-wiki of er al een bestand is voor jouw bank (of softwarepakket). Als die er is, download dit bestand dan en selecteer deze. Het kan veel tijd schelen!', 'import_file_help' => 'Selecteer je bestand', 'import_status_settings_complete' => 'De import is klaar om te beginnen.', 'import_status_import_complete' => 'Het importeren is voltooid.', @@ -865,11 +874,9 @@ return [ 'import_double' => 'Rij: #:row: Deze rij is al geimporteerd en is opgeslagen als :description.', 'import_finished_all' => 'Het importeren is voltooid. Hieronder zie je de resultaten.', 'import_with_key' => 'Import met code \':key\'', - - 'import_share_configuration' => 'Overweeg om je configuratiebestand te downloaden en te delen op de configuratiebestand-wiki. Hiermee kan je het andere Firefly III gebruikers weer makkelijker maken.', - - 'import_finished_report' => 'Het importeren is voltooid. Kijk naar eventuele fouten in het blok hierboven. Alle geimporteerde transacties hebben een tag, en die kan je hieronder bekijken. ', - 'import_finished_link' => 'De geimporteerde transacties kan je vinden onder tag :tag.', - - + 'import_share_configuration' => 'Overweeg om je configuratiebestand te downloaden en te delen op de configuratiebestand-wiki. Hiermee kan je het andere Firefly III gebruikers weer makkelijker maken.', + 'import_finished_report' => 'Het importeren is voltooid. Kijk naar eventuele fouten in het blok hierboven. Alle geimporteerde transacties hebben een tag, en die kan je hieronder bekijken. ', + 'import_finished_link' => 'De geimporteerde transacties kan je vinden onder tag :tag.', + 'need_at_least_one_account' => 'Je moet minstens één betaalrekening hebben voor je spaarpotjes kan maken', + 'see_help_top_right' => 'Meer informatie vind je in de help pagina\'s. Gebruik daarvoor het icoontje rechtsboven in de hoek.', ]; \ No newline at end of file diff --git a/resources/lang/pt_BR/csv.php b/resources/lang/pt_BR/csv.php index 40a5975dc5..fbc8866ea4 100644 --- a/resources/lang/pt_BR/csv.php +++ b/resources/lang/pt_BR/csv.php @@ -15,7 +15,7 @@ return [ 'import_configure_title' => 'Configure sua importação', 'import_configure_intro' => 'There are some options for your CSV import. Please indicate if your CSV file contains headers on the first column, and what the date format of your date-fields is. That might require some experimentation. The field delimiter is usually a ",", but could also be a ";". Check this carefully.', - 'import_configure_form' => 'Formulário', + 'import_configure_form' => 'Basic CSV import options', 'header_help' => 'Verifique se a primeira linha do seu arquivo CSV está com os títulos de coluna', 'date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', 'delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', @@ -24,7 +24,6 @@ return [ // roles 'column_roles_title' => 'Definir papeis da coluna', - 'column_roles_text' => '

Firefly III cannot guess what data each column contains. You must tell Firefly which kinds of data to expect. The example data can guide you into picking the correct type from the dropdown. If a column cannot be matched to a useful data type, please let me know by creating an issue.

Some values in your CSV file, such as account names or categories, may already exist in your Firefly III database. If you select "map these values" Firefly will not attempt to search for matching values itself but allow you to match the CSV values against the values in your database. This allows you to fine-tune the import.

', 'column_roles_table' => 'Tabela', 'column_name' => 'Nome da coluna', 'column_example' => 'Dados de exemplo da coluna', diff --git a/resources/lang/pt_BR/firefly.php b/resources/lang/pt_BR/firefly.php index 4c35a1f9b4..7bc6445767 100644 --- a/resources/lang/pt_BR/firefly.php +++ b/resources/lang/pt_BR/firefly.php @@ -67,11 +67,25 @@ return [ 'warning_much_data' => ':days dias de dados podem demorar um pouco para carregar.', 'registered' => 'Você se registrou com sucesso!', 'search' => 'Pesquisa', + 'search_found_accounts' => 'Found :count account(s) for your query.', + 'search_found_categories' => 'Found :count category(ies) for your query.', + 'search_found_budgets' => 'Found :count budget(s) for your query.', + 'search_found_tags' => 'Found :count tag(s) for your query.', + 'search_found_transactions' => 'Found :count transaction(s) for your query.', + 'results_limited' => 'The results are limited to :count entries.', + 'tagbalancingAct' => 'Balancing act', + 'tagadvancePayment' => 'Advance payment', + 'tagnothing' => '', + 'Default asset account' => 'Default asset account', 'no_budget_pointer' => 'Parece que não há orçamentos ainda. Você deve criar alguns na página orçamentos. Orçamentos podem ajudá-lo a manter o controle de despesas.', + 'Savings account' => 'Savings account', + 'Credit card' => 'Credit card', 'source_accounts' => 'Conta(s) de origem', 'destination_accounts' => 'Conta(s) de destino', 'user_id_is' => 'Your user id is :user', 'field_supports_markdown' => 'This field supports Markdown.', + 'need_more_help' => 'If you need more help using Firefly III, please open a ticker on Github.', + 'nothing_to_display' => 'There are no transactions to show you', // repeat frequencies: 'repeat_freq_yearly' => 'yearly', @@ -842,14 +856,9 @@ return [ 'import_complete_text' => 'The import is ready to start. All the configuration you needed to do has been done. Please download the configuration file. It will help you with the import should it not go as planned. To actually run the import, you can either execute the following command in your console, or run the web-based import. Depending on your configuration, the console import will give you more feedback.', 'import_download_config' => 'Download da configuração', 'import_start_import' => 'Iniciar importação', - 'import_intro_beta' => 'A função de importação do Firefly III está em beta. Muitos usuários do Firefly III têm tentado diferentes arquivos. Embora cada componente individual desta rotina de importação funcione (realmente), a combinação pode quebrar. Se seu arquivo não puder ser importado pelo Firefly, por favor leia esta página da wiki para resolver o problema que temos para executar.', 'import_data' => 'Importar dados', 'import_data_full' => 'Importar dados para o Firefly III', 'import' => 'Importar', - 'import_intro_what_it_does' => 'This page allows you to import data into Firefly III. To do so, export data from your bank, or from another financial management system. Upload that file here. Firefly III will convert the data. You need to give it some directions. Please select a file and follow the instructions.', - 'import_intro_import_conf_title' => 'Import "configuration"', - 'import_intro_beta_warning' => 'Warning', - 'import_intro_import_conf_text' => 'As you will discover over the next few pages, this import routine has a lot of settings. These settings are mainly dependent on the bank (or financial management software) your file comes from. There is a good chance somebody else already imported such a file and has shared their configuration file. Please visit the import configuration center to see if there already is a configuration available for your bank or system. If there is, you should download this configuration file and upload it here as well. It will save you a lot of time!', 'import_file_help' => 'Selecione seu arquivo', 'import_status_settings_complete' => 'The import is ready to start.', 'import_status_import_complete' => 'The import has completed.', @@ -865,11 +874,9 @@ return [ 'import_double' => 'Row #:row: This row has been imported before, and is stored in :description.', 'import_finished_all' => 'The import has finished. Please check out the results below.', 'import_with_key' => 'Import with key \':key\'', - - 'import_share_configuration' => 'Please consider downloading your configuration and sharing it at the import configuration center. This will allow other users of Firefly III to import their files more easily.', - - 'import_finished_report' => 'The import has finished. Please note any errors in the block above this line. All transactions imported during this particular session have been tagged, and you can check them out below. ', - 'import_finished_link' => 'The transactions imported can be found in tag :tag.', - - + 'import_share_configuration' => 'Please consider downloading your configuration and sharing it at the import configuration center. This will allow other users of Firefly III to import their files more easily.', + 'import_finished_report' => 'The import has finished. Please note any errors in the block above this line. All transactions imported during this particular session have been tagged, and you can check them out below. ', + 'import_finished_link' => 'The transactions imported can be found in tag :tag.', + 'need_at_least_one_account' => 'You need at least one asset account to be able to create piggy banks', + 'see_help_top_right' => 'For more information, please check out the help pages using the icon in the top right corner of the page.', ]; \ No newline at end of file diff --git a/resources/lang/zh_HK/csv.php b/resources/lang/zh_HK/csv.php index 576c132144..4acb52efdc 100644 --- a/resources/lang/zh_HK/csv.php +++ b/resources/lang/zh_HK/csv.php @@ -15,7 +15,7 @@ return [ 'import_configure_title' => 'Configure your import', 'import_configure_intro' => 'There are some options for your CSV import. Please indicate if your CSV file contains headers on the first column, and what the date format of your date-fields is. That might require some experimentation. The field delimiter is usually a ",", but could also be a ";". Check this carefully.', - 'import_configure_form' => 'Form', + 'import_configure_form' => 'Basic CSV import options', 'header_help' => 'Check this if the first row of your CSV file are the column titles', 'date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', 'delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', @@ -24,7 +24,6 @@ return [ // roles 'column_roles_title' => 'Define column roles', - 'column_roles_text' => '

Firefly III cannot guess what data each column contains. You must tell Firefly which kinds of data to expect. The example data can guide you into picking the correct type from the dropdown. If a column cannot be matched to a useful data type, please let me know by creating an issue.

Some values in your CSV file, such as account names or categories, may already exist in your Firefly III database. If you select "map these values" Firefly will not attempt to search for matching values itself but allow you to match the CSV values against the values in your database. This allows you to fine-tune the import.

', 'column_roles_table' => 'Table', 'column_name' => 'Name of column', 'column_example' => 'Column example data', diff --git a/resources/lang/zh_HK/firefly.php b/resources/lang/zh_HK/firefly.php index 7b14be178c..495bfab429 100644 --- a/resources/lang/zh_HK/firefly.php +++ b/resources/lang/zh_HK/firefly.php @@ -67,11 +67,25 @@ return [ 'warning_much_data' => ':days days of data may take a while to load.', 'registered' => 'You have registered successfully!', 'search' => 'Search', + 'search_found_accounts' => 'Found :count account(s) for your query.', + 'search_found_categories' => 'Found :count category(ies) for your query.', + 'search_found_budgets' => 'Found :count budget(s) for your query.', + 'search_found_tags' => 'Found :count tag(s) for your query.', + 'search_found_transactions' => 'Found :count transaction(s) for your query.', + 'results_limited' => 'The results are limited to :count entries.', + 'tagbalancingAct' => 'Balancing act', + 'tagadvancePayment' => 'Advance payment', + 'tagnothing' => '', + 'Default asset account' => 'Default asset account', 'no_budget_pointer' => 'You seem to have no budgets yet. You should create some on the budgets-page. Budgets can help you keep track of expenses.', + 'Savings account' => 'Savings account', + 'Credit card' => 'Credit card', 'source_accounts' => 'Source account(s)', 'destination_accounts' => 'Destination account(s)', 'user_id_is' => 'Your user id is :user', 'field_supports_markdown' => 'This field supports Markdown.', + 'need_more_help' => 'If you need more help using Firefly III, please open a ticker on Github.', + 'nothing_to_display' => 'There are no transactions to show you', // repeat frequencies: 'repeat_freq_yearly' => 'yearly', @@ -842,14 +856,9 @@ return [ 'import_complete_text' => 'The import is ready to start. All the configuration you needed to do has been done. Please download the configuration file. It will help you with the import should it not go as planned. To actually run the import, you can either execute the following command in your console, or run the web-based import. Depending on your configuration, the console import will give you more feedback.', 'import_download_config' => 'Download configuration', 'import_start_import' => 'Start import', - 'import_intro_beta' => 'The import function of Firefly III is in beta. Many users of Firefly III have tried many different files. Although each individual compontent of this import routine works (really), the combination might break. If your file cannot be imported by Firefly, please read this wiki page so I can fix the problem you have run into.', 'import_data' => 'Import data', 'import_data_full' => 'Import data into Firefly III', 'import' => 'Import', - 'import_intro_what_it_does' => 'This page allows you to import data into Firefly III. To do so, export data from your bank, or from another financial management system. Upload that file here. Firefly III will convert the data. You need to give it some directions. Please select a file and follow the instructions.', - 'import_intro_import_conf_title' => 'Import "configuration"', - 'import_intro_beta_warning' => 'Warning', - 'import_intro_import_conf_text' => 'As you will discover over the next few pages, this import routine has a lot of settings. These settings are mainly dependent on the bank (or financial management software) your file comes from. There is a good chance somebody else already imported such a file and has shared their configuration file. Please visit the import configuration center to see if there already is a configuration available for your bank or system. If there is, you should download this configuration file and upload it here as well. It will save you a lot of time!', 'import_file_help' => 'Select your file', 'import_status_settings_complete' => 'The import is ready to start.', 'import_status_import_complete' => 'The import has completed.', @@ -865,11 +874,9 @@ return [ 'import_double' => 'Row #:row: This row has been imported before, and is stored in :description.', 'import_finished_all' => 'The import has finished. Please check out the results below.', 'import_with_key' => 'Import with key \':key\'', - - 'import_share_configuration' => 'Please consider downloading your configuration and sharing it at the import configuration center. This will allow other users of Firefly III to import their files more easily.', - - 'import_finished_report' => 'The import has finished. Please note any errors in the block above this line. All transactions imported during this particular session have been tagged, and you can check them out below. ', - 'import_finished_link' => 'The transactions imported can be found in tag :tag.', - - + 'import_share_configuration' => 'Please consider downloading your configuration and sharing it at the import configuration center. This will allow other users of Firefly III to import their files more easily.', + 'import_finished_report' => 'The import has finished. Please note any errors in the block above this line. All transactions imported during this particular session have been tagged, and you can check them out below. ', + 'import_finished_link' => 'The transactions imported can be found in tag :tag.', + 'need_at_least_one_account' => 'You need at least one asset account to be able to create piggy banks', + 'see_help_top_right' => 'For more information, please check out the help pages using the icon in the top right corner of the page.', ]; \ No newline at end of file diff --git a/resources/lang/zh_TW/auth.php b/resources/lang/zh_TW/auth.php index 5d833b3d68..7a20761eea 100644 --- a/resources/lang/zh_TW/auth.php +++ b/resources/lang/zh_TW/auth.php @@ -22,7 +22,7 @@ return [ | */ - 'failed' => 'These credentials do not match our records.', - 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', + 'failed' => '帳號或密碼錯誤。', + 'throttle' => '登入失敗次數過多,請等待 :seconds 秒後再試。', ]; \ No newline at end of file diff --git a/resources/lang/zh_TW/csv.php b/resources/lang/zh_TW/csv.php index 1e02a3e0a2..e94574c27b 100644 --- a/resources/lang/zh_TW/csv.php +++ b/resources/lang/zh_TW/csv.php @@ -15,7 +15,7 @@ return [ 'import_configure_title' => '匯入設定', 'import_configure_intro' => '這裡有一些 CSV 匯入選項。請檢查你的 CSV 檔的第一列是否包含欄位名稱,和你的日期格式是什麼。你可能需要嘗試幾次來調整正確。欄位分隔符號是通常 ",",但也可能是";";仔細檢查這一點。', - 'import_configure_form' => '表單', + 'import_configure_form' => 'Basic CSV import options', 'header_help' => 'CSV 檔的第一行是標題', 'date_help' => 'CSV 內的日期格式。請跟從這頁內的格式來填寫。 系統預設能夠解析像這樣的日期: :dateExample 。', 'delimiter_help' => '請選擇你的檔案中所使用的欄位分隔符號。如果不肯定的話,逗號是最安全的選項。', @@ -24,7 +24,6 @@ return [ // roles 'column_roles_title' => '定義欄的內容', - 'column_roles_text' => '

Firefly III 猜不出每一欄中儲存了什麼資料。你必須告訴 Firefly 每一欄中有什麼資料。 下列的示範資料可以幫助你從列表中選擇正確類型。如果有欄位不能配對到有用的類型,請告訴我 (只有英語版本)

你的 CSV 檔中某些欄位可能已經存在於 Firefly III 的資料庫內,例如帳號名稱,或類別。如果你選擇「配對這些資料」, Firefly 會請你手動配對 CSV 檔和資料庫內的資料。這容許你微調你的匯入設定。

', 'column_roles_table' => '表格', 'column_name' => '欄位名稱', 'column_example' => '欄的示例資料', diff --git a/resources/lang/zh_TW/firefly.php b/resources/lang/zh_TW/firefly.php index 7f15e68f64..76a0c0f2f8 100644 --- a/resources/lang/zh_TW/firefly.php +++ b/resources/lang/zh_TW/firefly.php @@ -51,8 +51,8 @@ return [ 'flash_info_multiple' => '有一個訊息|有 :count 個訊息', 'flash_error_multiple' => '出現了一個錯誤|出現了 :count 個錯誤', 'net_worth' => '淨值', - 'route_has_no_help' => 'There is no help for this route.', - 'help_may_not_be_your_language' => 'This help text is in English. It is not yet available in your language', + 'route_has_no_help' => '目前還沒有說明。', + 'help_may_not_be_your_language' => '這個說明還沒有中文版本,將會顯示英文版本。', 'two_factor_welcome' => '哈囉, :user !', 'two_factor_enter_code' => '若要繼續,請輸入你的雙重身份驗證 (2FA) 應用程序內顯示的驗證代碼。', 'two_factor_code_here' => '在此輸入代碼', @@ -67,11 +67,25 @@ return [ 'warning_much_data' => ':days 天的資料需要一點時間載入。', 'registered' => '您已成功註冊 !', 'search' => '搜尋', + 'search_found_accounts' => 'Found :count account(s) for your query.', + 'search_found_categories' => 'Found :count category(ies) for your query.', + 'search_found_budgets' => 'Found :count budget(s) for your query.', + 'search_found_tags' => 'Found :count tag(s) for your query.', + 'search_found_transactions' => 'Found :count transaction(s) for your query.', + 'results_limited' => 'The results are limited to :count entries.', + 'tagbalancingAct' => 'Balancing act', + 'tagadvancePayment' => 'Advance payment', + 'tagnothing' => '', + 'Default asset account' => 'Default asset account', 'no_budget_pointer' => '你還沒有預算。你可以在預算頁來建立預算。預算可以幫助你跟蹤支出情況。', + 'Savings account' => 'Savings account', + 'Credit card' => 'Credit card', 'source_accounts' => '來源帳戶', 'destination_accounts' => '目標帳戶', 'user_id_is' => 'Your user id is :user', 'field_supports_markdown' => 'This field supports Markdown.', + 'need_more_help' => 'If you need more help using Firefly III, please open a ticker on Github.', + 'nothing_to_display' => 'There are no transactions to show you', // repeat frequencies: 'repeat_freq_yearly' => 'yearly', @@ -239,8 +253,8 @@ return [ 'rule_action_set_description_choice' => '把描述設置為…', 'rule_action_append_description_choice' => '描述後加上…', 'rule_action_prepend_description_choice' => '描述前加上…', - 'rule_action_set_source_account_choice' => 'Set source account to...', - 'rule_action_set_source_account' => 'Set source account to :action_value', + 'rule_action_set_source_account_choice' => '把來源帳戶設置為...', + 'rule_action_set_source_account' => '將來源帳戶設置為 :action_value', 'rule_action_set_destination_account_choice' => 'Set destination account to...', 'rule_action_set_destination_account' => 'Set destination account to :action_value', @@ -379,7 +393,7 @@ return [ 'convert_please_set_expense_destination' => 'Please pick the expense account where the money will go to.', 'convert_please_set_asset_source' => 'Please pick the asset account where the money will come from.', 'convert_explanation_withdrawal_deposit' => 'If you convert this withdrawal into a deposit, :amount will be deposited into :sourceName instead of taken from it.', - 'convert_explanation_withdrawal_transfer' => 'If you convert this withdrawal into a transfer, :amount will be transferred from :sourceName to a new asset account, instead of being paid to :destinationName.', + 'convert_explanation_withdrawal_transfer' => '如果你把這個提款(支出)轉換為轉帳, :amount 會從 :sourceName 轉帳到新的資產帳戶,而不會付款到 :destinationName 。', 'convert_explanation_deposit_withdrawal' => 'If you convert this deposit into a withdrawal, :amount will be removed from :destinationName instead of added to it.', 'convert_explanation_deposit_transfer' => 'If you convert this deposit into a transfer, :amount will be transferred from an asset account of your choice into :destinationName.', 'convert_explanation_transfer_withdrawal' => 'If you convert this transfer into a withdrawal, :amount will go from :sourceName to a new destination as an expense, instead of to :destinationName as a transfer.', @@ -842,14 +856,9 @@ return [ 'import_complete_text' => '匯入程序已準備妥當。你已完成所有設定。請下載設定檔,當你的匯入出現問題時它將幫上忙。若要執行匯入程序,你可以在您的伺服器上執行以下命令,或運行網頁導入程序。根據您的配置,在伺服器上執行命令或會給你更多的資訊。', 'import_download_config' => 'Download configuration', 'import_start_import' => '開始匯入', - 'import_intro_beta' => 'Firefly III 的匯入功能依然在測試階段。很多 Firefly III 的用戶已經成功使用這個功能匯入不同的檔案。儘管這個匯入功能的每個部分都能夠正常運作(我說真的),但是整個功能或者會有點問題。如果你的檔案不能匯入到 Firefly 內,請閱讀這個維基頁面,以方便我修復你所遇到的問題。', 'import_data' => '匯入資料', 'import_data_full' => '匯入資料到 Firefly III', 'import' => '匯入', - 'import_intro_what_it_does' => 'This page allows you to import data into Firefly III. To do so, export data from your bank, or from another financial management system. Upload that file here. Firefly III will convert the data. You need to give it some directions. Please select a file and follow the instructions.', - 'import_intro_import_conf_title' => 'Import "configuration"', - 'import_intro_beta_warning' => 'Warning', - 'import_intro_import_conf_text' => 'As you will discover over the next few pages, this import routine has a lot of settings. These settings are mainly dependent on the bank (or financial management software) your file comes from. There is a good chance somebody else already imported such a file and has shared their configuration file. Please visit the import configuration center to see if there already is a configuration available for your bank or system. If there is, you should download this configuration file and upload it here as well. It will save you a lot of time!', 'import_file_help' => 'Select your file', 'import_status_settings_complete' => '匯入已準備妥當,可以開始。', 'import_status_import_complete' => '匯入已完成。', @@ -865,11 +874,9 @@ return [ 'import_double' => '行 #:row: 這行曾被匯入過,並已儲存在:description。', 'import_finished_all' => '匯入已完成。請檢查下列的結果。', 'import_with_key' => '以鍵 \':key\' 作匯入', - - 'import_share_configuration' => 'Please consider downloading your configuration and sharing it at the import configuration center. This will allow other users of Firefly III to import their files more easily.', - - 'import_finished_report' => '匯入已完成。請留意在這行上面的錯誤記錄。這次所匯入的所有交易都已經進行標記,你可以在下面查看。 ', - 'import_finished_link' => '匯入成功的所有交易都可以在標籤 :tag 內找到。', - - + 'import_share_configuration' => 'Please consider downloading your configuration and sharing it at the import configuration center. This will allow other users of Firefly III to import their files more easily.', + 'import_finished_report' => '匯入已完成。請留意在這行上面的錯誤記錄。這次所匯入的所有交易都已經進行標記,你可以在下面查看。 ', + 'import_finished_link' => '匯入成功的所有交易都可以在標籤 :tag 內找到。', + 'need_at_least_one_account' => 'You need at least one asset account to be able to create piggy banks', + 'see_help_top_right' => 'For more information, please check out the help pages using the icon in the top right corner of the page.', ]; \ No newline at end of file diff --git a/resources/lang/zh_TW/form.php b/resources/lang/zh_TW/form.php index 63dbda5b31..cafa3fb6a2 100644 --- a/resources/lang/zh_TW/form.php +++ b/resources/lang/zh_TW/form.php @@ -31,13 +31,13 @@ return [ 'journal_source_account_id' => '資產帳戶 (源頭)', 'account_from_id' => '從帳戶', 'account_to_id' => '到帳戶', - 'source_account' => 'Source account', - 'destination_account' => 'Destination account', + 'source_account' => '來源帳戶', + 'destination_account' => '目標帳戶', 'journal_destination_account_id' => '資產帳戶 (目標)', 'asset_destination_account' => '資產帳戶 (目標)', 'asset_source_account' => '資產帳戶 (來源)', 'journal_description' => '描述', - 'note' => 'Notes', + 'note' => '備註', 'split_journal' => '分割此交易', 'split_journal_explanation' => '分割這個交易為幾個部分', 'currency' => '貨幣', @@ -61,11 +61,11 @@ return [ 'expense_account' => '支出帳戶', 'revenue_account' => '收入帳戶', - 'revenue_account_source' => 'Revenue account (source)', - 'source_account_asset' => 'Source account (asset account)', + 'revenue_account_source' => '收入帳戶 (源頭)', + 'source_account_asset' => '來源帳戶 (資產帳戶)', 'destination_account_expense' => 'Destination account (expense account)', 'destination_account_asset' => 'Destination account (asset account)', - 'source_account_revenue' => 'Source account (revenue account)', + 'source_account_revenue' => '來源帳戶 (收入帳戶)', 'type' => 'Type', 'convert_Withdrawal' => 'Convert withdrawal', 'convert_Deposit' => 'Convert deposit', diff --git a/resources/lang/zh_TW/list.php b/resources/lang/zh_TW/list.php index a3af2b8579..104a8cba1c 100644 --- a/resources/lang/zh_TW/list.php +++ b/resources/lang/zh_TW/list.php @@ -25,7 +25,7 @@ return [ 'matchedOn' => '匹配於', 'matchesOn' => '匹配於', 'account_type' => '帳戶類型', - 'created_at' => 'Created at', + 'created_at' => '建立於', 'new_balance' => '新餘額', 'account' => '帳戶', 'matchingAmount' => '金額', @@ -38,7 +38,7 @@ return [ 'repeat_freq' => '重複', 'description' => '描述', 'amount' => '金額', - 'internal_reference' => 'Internal reference', + 'internal_reference' => '內部參考', 'date' => '日期', 'interest_date' => '付息日', 'book_date' => 'Book date', @@ -59,7 +59,7 @@ return [ 'transfer' => '轉帳', 'type' => '類型', 'completed' => '已完成', - 'iban' => 'IBAN', + 'iban' => '國際銀行賬戶號碼(IBAN)', 'paid_current_period' => '在這期間已付', 'email' => '電子郵件', 'registered_at' => '註冊於', @@ -72,7 +72,7 @@ return [ 'blocked_code' => 'Block code', 'domain' => 'Domain', 'registration_attempts' => 'Registration attempts', - 'source_account' => 'Source account', + 'source_account' => '來源帳戶', 'destination_account' => 'Destination account', 'accounts_count' => 'Number of accounts', diff --git a/resources/views/accounts/create.twig b/resources/views/accounts/create.twig index cc48551a42..b068c909c4 100644 --- a/resources/views/accounts/create.twig +++ b/resources/views/accounts/create.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, what) }} {% endblock %} diff --git a/resources/views/accounts/delete.twig b/resources/views/accounts/delete.twig index ac4771189c..3fe4850f90 100644 --- a/resources/views/accounts/delete.twig +++ b/resources/views/accounts/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute().getName(), account) }} diff --git a/resources/views/accounts/edit.twig b/resources/views/accounts/edit.twig index 5e31575226..4ccd0f3a22 100644 --- a/resources/views/accounts/edit.twig +++ b/resources/views/accounts/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute().getName(), account) }} diff --git a/resources/views/accounts/index.twig b/resources/views/accounts/index.twig index af0a8d246d..f433cddf8b 100644 --- a/resources/views/accounts/index.twig +++ b/resources/views/accounts/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, what) }} @@ -26,7 +26,7 @@
- {% include 'list/accounts.twig' %} + {% include 'list.accounts' %}
diff --git a/resources/views/accounts/show.twig b/resources/views/accounts/show.twig index 61d41da0c5..ac18d8a0cd 100644 --- a/resources/views/accounts/show.twig +++ b/resources/views/accounts/show.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, account) }} diff --git a/resources/views/accounts/show_with_date.twig b/resources/views/accounts/show_with_date.twig index 89ae367972..957e3b5e98 100644 --- a/resources/views/accounts/show_with_date.twig +++ b/resources/views/accounts/show_with_date.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} diff --git a/resources/views/admin/configuration/index.twig b/resources/views/admin/configuration/index.twig index 412fb2459b..73e183c8b2 100644 --- a/resources/views/admin/configuration/index.twig +++ b/resources/views/admin/configuration/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists }} diff --git a/resources/views/admin/domains/index.twig b/resources/views/admin/domains/index.twig index 2da0fe9ad2..5245f5901c 100644 --- a/resources/views/admin/domains/index.twig +++ b/resources/views/admin/domains/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists }} @@ -12,7 +12,7 @@
{% if domains|length > 0 %} - +
@@ -59,7 +59,7 @@ {{ 'all_domains_is_filtered'|_ }}

-
 
+
@@ -118,3 +118,9 @@ {% endblock %} +{% block styles %} + +{% endblock %} +{% block scripts %} + +{% endblock %} diff --git a/resources/views/admin/index.twig b/resources/views/admin/index.twig index 0c7d5f3dfb..8ca383246f 100644 --- a/resources/views/admin/index.twig +++ b/resources/views/admin/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists }} diff --git a/resources/views/admin/users/index.twig b/resources/views/admin/users/index.twig index 9b47a66dd8..602c4dba48 100644 --- a/resources/views/admin/users/index.twig +++ b/resources/views/admin/users/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists }} @@ -11,7 +11,7 @@

{{ 'all_users'|_ }}

-
 
+
@@ -29,15 +29,15 @@ {% for user in users %} - - - + - @@ -47,28 +47,28 @@ - - - - - - diff --git a/resources/views/transactions/edit.twig b/resources/views/transactions/edit.twig index 5046d67512..edf11ef10b 100644 --- a/resources/views/transactions/edit.twig +++ b/resources/views/transactions/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, journal) }} @@ -83,11 +83,7 @@ {{ ExpandedForm.text('tags') }} - - {% if what == 'transfer' and piggyBankList|length > 0 %} - {{ ExpandedForm.select('piggy_bank_id',piggyBankList,data['piggy_bank_id']) }} - {% endif %} - + {# NO PIGGY BANK #} diff --git a/resources/views/transactions/index.twig b/resources/views/transactions/index.twig index bf1f2efe79..3d63c4e0b1 100644 --- a/resources/views/transactions/index.twig +++ b/resources/views/transactions/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, what) }} @@ -12,7 +12,7 @@

{{ subTitle }}

- {% include 'list.journals' %} + {% include 'list.journals-tasker' %}
diff --git a/resources/views/transactions/mass-delete.twig b/resources/views/transactions/mass-delete.twig index 2be459dbbc..0cc411b8d0 100644 --- a/resources/views/transactions/mass-delete.twig +++ b/resources/views/transactions/mass-delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/transactions/mass-edit.twig b/resources/views/transactions/mass-edit.twig index 59260f2603..b42592d13a 100644 --- a/resources/views/transactions/mass-edit.twig +++ b/resources/views/transactions/mass-edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/transactions/show.twig b/resources/views/transactions/show.twig index f4928d89f4..2ade2b2752 100644 --- a/resources/views/transactions/show.twig +++ b/resources/views/transactions/show.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, journal) }} diff --git a/routes/web.php b/routes/web.php index 7a5072cf29..32cda245ba 100755 --- a/routes/web.php +++ b/routes/web.php @@ -196,6 +196,7 @@ Route::group( // budgets: Route::get('/chart/budget/frontpage', ['uses' => 'Chart\BudgetController@frontpage']); + Route::get('/chart/budget/period/{budget}/default/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\BudgetController@period']); // this chart is used in reports: Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'Chart\BudgetController@budgetLimit']); @@ -216,6 +217,7 @@ Route::group( Route::get('/chart/report/in-out-sum/{reportType}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@yearInOutSummarized']); Route::get('/chart/report/net-worth/{reportType}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@netWorth']); + /** * IMPORT CONTROLLER */ @@ -316,10 +318,20 @@ Route::group( ['uses' => 'Report\AccountController@accountReport', 'as' => 'reports.data.accountReport'] ); - // income report + // income and expenses report Route::get( - '/reports/data/in-out-report/{start_date}/{end_date}/{accountList}', - ['uses' => 'Report\InOutController@inOutReport', 'as' => 'reports.data.inOutReport'] + '/reports/data/inc-exp-report/{start_date}/{end_date}/{accountList}', + ['uses' => 'Report\InOutController@incExpReport', 'as' => 'reports.data.incExpReport'] + ); + // (income report): + Route::get( + '/reports/data/income-report/{start_date}/{end_date}/{accountList}', + ['uses' => 'Report\InOutController@incomeReport', 'as' => 'reports.data.incomeReport'] + ); + // (expense report): + Route::get( + '/reports/data/expense-report/{start_date}/{end_date}/{accountList}', + ['uses' => 'Report\InOutController@expenseReport', 'as' => 'reports.data.expenseReport'] ); // category report: @@ -345,6 +357,12 @@ Route::group( ['uses' => 'Report\BudgetController@budgetYearOverview', 'as' => 'reports.data.budgetYearOverview'] ); + // budget multi year overview + Route::get( + '/reports/data/budget-multi-year/{start_date}/{end_date}/{accountList}', + ['uses' => 'Report\BudgetController@budgetMultiYear', 'as' => 'reports.data.budgetMultiYear'] + ); + /** * Rules Controller From f653bc5f6ef59438786ad161e896a736bcadae7d Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 7 Nov 2016 18:49:35 +0100 Subject: [PATCH 064/709] Expand firefly config. --- .env.example | 1 - app/Handlers/Events/UserEventHandler.php | 17 +- .../Admin/ConfigurationController.php | 9 +- app/Http/Controllers/Admin/UserController.php | 17 +- .../Controllers/Auth/RegisterController.php | 8 +- app/Http/Middleware/IsConfirmed.php | 5 +- app/Http/Middleware/IsNotConfirmed.php | 5 +- app/Http/Requests/ConfigurationRequest.php | 8 +- app/Repositories/User/UserRepository.php | 5 +- config/firefly.php | 5 +- resources/lang/en_US/firefly.php | 209 +++++++++--------- resources/lang/en_US/form.php | 8 +- .../views/admin/configuration/index.twig | 37 +++- resources/views/auth/register.twig | 8 +- 14 files changed, 198 insertions(+), 144 deletions(-) diff --git a/.env.example b/.env.example index c628064094..e8ebe3c838 100755 --- a/.env.example +++ b/.env.example @@ -35,7 +35,6 @@ MAIL_PASSWORD=null MAIL_ENCRYPTION=null SEND_REGISTRATION_MAIL=true -MUST_CONFIRM_ACCOUNT=false SHOW_INCOMPLETE_TRANSLATIONS=false diff --git a/app/Handlers/Events/UserEventHandler.php b/app/Handlers/Events/UserEventHandler.php index 4d9651158f..c6235d0a6d 100644 --- a/app/Handlers/Events/UserEventHandler.php +++ b/app/Handlers/Events/UserEventHandler.php @@ -14,6 +14,7 @@ declare(strict_types = 1); namespace FireflyIII\Handlers\Events; use Exception; +use FireflyConfig; use FireflyIII\Events\ConfirmedUser; use FireflyIII\Events\RegisteredUser; use FireflyIII\Events\ResentConfirmation; @@ -81,10 +82,10 @@ class UserEventHandler */ public function sendConfirmationMessage(RegisteredUser $event): bool { - $user = $event->user; - $ipAddress = $event->ipAddress; - $confirmAccount = env('MUST_CONFIRM_ACCOUNT', false); - if ($confirmAccount === false) { + $user = $event->user; + $ipAddress = $event->ipAddress; + $mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data; + if ($mustConfirmAccount === false) { Preferences::setForUser($user, 'user_confirmed', true); Preferences::setForUser($user, 'user_confirmed_last_mail', 0); Preferences::mark(); @@ -124,10 +125,10 @@ class UserEventHandler */ function sendConfirmationMessageAgain(ResentConfirmation $event): bool { - $user = $event->user; - $ipAddress = $event->ipAddress; - $confirmAccount = env('MUST_CONFIRM_ACCOUNT', false); - if ($confirmAccount === false) { + $user = $event->user; + $ipAddress = $event->ipAddress; + $mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data; + if ($mustConfirmAccount === false) { Preferences::setForUser($user, 'user_confirmed', true); Preferences::setForUser($user, 'user_confirmed_last_mail', 0); Preferences::mark(); diff --git a/app/Http/Controllers/Admin/ConfigurationController.php b/app/Http/Controllers/Admin/ConfigurationController.php index 33753166d2..f64c807606 100644 --- a/app/Http/Controllers/Admin/ConfigurationController.php +++ b/app/Http/Controllers/Admin/ConfigurationController.php @@ -14,7 +14,6 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers\Admin; -use Config; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\ConfigurationRequest; use FireflyIII\Support\Facades\FireflyConfig; @@ -59,9 +58,11 @@ class ConfigurationController extends Controller // all available configuration and their default value in case // they don't exist yet. - $singleUserMode = FireflyConfig::get('single_user_mode', Config::get('firefly.configuration.single_user_mode'))->data; + $singleUserMode = FireflyConfig::get('single_user_mode', config('firefly.configuration.single_user_mode'))->data; + $mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data; + $isDemoSite = FireflyConfig::get('is_demo_site', config('firefly.configuration.is_demo_site'))->data; - return view('admin.configuration.index', compact('subTitle', 'subTitleIcon', 'singleUserMode')); + return view('admin.configuration.index', compact('subTitle', 'subTitleIcon', 'singleUserMode', 'mustConfirmAccount', 'isDemoSite')); } @@ -77,6 +78,8 @@ class ConfigurationController extends Controller // store config values FireflyConfig::set('single_user_mode', $data['single_user_mode']); + FireflyConfig::set('must_confirm_account', $data['must_confirm_account']); + FireflyConfig::set('is_demo_site', $data['is_demo_site']); // flash message Session::flash('success', strval(trans('firefly.configuration_updated'))); diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 5557539f35..69a329c2c9 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -14,6 +14,7 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers\Admin; +use FireflyConfig; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; @@ -46,20 +47,20 @@ class UserController extends Controller */ public function index(UserRepositoryInterface $repository) { - $title = strval(trans('firefly.administration')); - $mainTitleIcon = 'fa-hand-spock-o'; - $subTitle = strval(trans('firefly.user_administration')); - $subTitleIcon = 'fa-users'; - $confirmAccount = env('MUST_CONFIRM_ACCOUNT', false); - $users = $repository->all(); + $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; + $users = $repository->all(); // add meta stuff. $users->each( - function (User $user) use ($confirmAccount) { + function (User $user) use ($mustConfirmAccount) { // is user activated? $isConfirmed = Preferences::getForUser($user, 'user_confirmed', false)->data; $user->activated = true; - if ($isConfirmed === false && $confirmAccount === true) { + if ($isConfirmed === false && $mustConfirmAccount === true) { $user->activated = false; } diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php index e7d6dc6692..8de1eb1d63 100755 --- a/app/Http/Controllers/Auth/RegisterController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -123,7 +123,11 @@ class RegisterController extends Controller */ public function showRegistrationForm(Request $request) { - $showDemoWarning = config('firefly.show-demo-warning', false); + // is demo site? + $isDemoSite = FireflyConfig::get('is_demo_site', Config::get('firefly.configuration.is_demo_site'))->data; + + // activate account? + $mustConfirmAccount = FireflyConfig::get('must_confirm_account', Config::get('firefly.configuration.must_confirm_account'))->data; // is allowed to? $singleUserMode = FireflyConfig::get('single_user_mode', Config::get('firefly.configuration.single_user_mode'))->data; @@ -136,7 +140,7 @@ class RegisterController extends Controller $email = $request->old('email'); - return view('auth.register', compact('showDemoWarning', 'email')); + return view('auth.register', compact('isDemoSite', 'email', 'mustConfirmAccount')); } /** diff --git a/app/Http/Middleware/IsConfirmed.php b/app/Http/Middleware/IsConfirmed.php index bd6dfa1c02..ef8e398a9e 100644 --- a/app/Http/Middleware/IsConfirmed.php +++ b/app/Http/Middleware/IsConfirmed.php @@ -14,6 +14,7 @@ declare(strict_types = 1); namespace FireflyIII\Http\Middleware; use Closure; +use FireflyConfig; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Preferences; @@ -45,11 +46,11 @@ class IsConfirmed return redirect()->guest('login'); } // must the user be confirmed in the first place? - $confirmAccount = env('MUST_CONFIRM_ACCOUNT', false); + $mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data; // user must be logged in, then continue: $isConfirmed = Preferences::get('user_confirmed', false)->data; - if ($isConfirmed === false && $confirmAccount === true) { + if ($isConfirmed === false && $mustConfirmAccount === true) { // user account is not confirmed, redirect to // confirmation page: diff --git a/app/Http/Middleware/IsNotConfirmed.php b/app/Http/Middleware/IsNotConfirmed.php index 62887408c0..237f28c17a 100644 --- a/app/Http/Middleware/IsNotConfirmed.php +++ b/app/Http/Middleware/IsNotConfirmed.php @@ -14,6 +14,7 @@ declare(strict_types = 1); namespace FireflyIII\Http\Middleware; use Closure; +use FireflyConfig; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Preferences; @@ -45,10 +46,10 @@ class IsNotConfirmed return redirect()->guest('login'); } // must the user be confirmed in the first place? - $confirmAccount = env('MUST_CONFIRM_ACCOUNT', false); + $mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data; // user must be logged in, then continue: $isConfirmed = Preferences::get('user_confirmed', false)->data; - if ($isConfirmed || $confirmAccount === false) { + if ($isConfirmed || $mustConfirmAccount === false) { // user account is confirmed, simply send them home. return redirect(route('home')); } diff --git a/app/Http/Requests/ConfigurationRequest.php b/app/Http/Requests/ConfigurationRequest.php index 2d9cc6a9a9..c2f2d3becd 100644 --- a/app/Http/Requests/ConfigurationRequest.php +++ b/app/Http/Requests/ConfigurationRequest.php @@ -36,7 +36,9 @@ class ConfigurationRequest extends Request public function getConfigurationData(): array { return [ - 'single_user_mode' => intval($this->get('single_user_mode')) === 1, + 'single_user_mode' => intval($this->get('single_user_mode')) === 1, + 'must_confirm_account' => intval($this->get('must_confirm_account')) === 1, + 'is_demo_site' => intval($this->get('is_demo_site')) === 1, ]; } @@ -46,7 +48,9 @@ class ConfigurationRequest extends Request public function rules() { $rules = [ - 'single_user_mode' => 'between:0,1|numeric', + 'single_user_mode' => 'between:0,1|numeric', + 'must_confirm_account' => 'between:0,1|numeric', + 'is_demo_site' => 'between:0,1|numeric', ]; return $rules; diff --git a/app/Repositories/User/UserRepository.php b/app/Repositories/User/UserRepository.php index 97642b2ea9..f7b76eb1d9 100644 --- a/app/Repositories/User/UserRepository.php +++ b/app/Repositories/User/UserRepository.php @@ -14,6 +14,7 @@ declare(strict_types = 1); namespace FireflyIII\Repositories\User; +use FireflyConfig; use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\Role; use FireflyIII\User; @@ -94,10 +95,10 @@ class UserRepository implements UserRepositoryInterface } // is user activated? - $confirmAccount = env('MUST_CONFIRM_ACCOUNT', false); + $mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data; $isConfirmed = Preferences::getForUser($user, 'user_confirmed', false)->data; $return['is_activated'] = true; - if ($isConfirmed === false && $confirmAccount === true) { + if ($isConfirmed === false && $mustConfirmAccount === true) { $return['is_activated'] = false; } diff --git a/config/firefly.php b/config/firefly.php index 40f65d62d5..a8b27f2513 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -19,7 +19,9 @@ declare(strict_types = 1); return [ 'configuration' => [ - 'single_user_mode' => true, + 'single_user_mode' => true, + 'is_demo_site' => false, + 'must_confirm_account' => false, ], 'chart' => 'chartjs', 'version' => '4.1.6', @@ -204,5 +206,4 @@ return [ ], 'default_currency' => 'EUR', 'default_language' => 'en_US', - 'show-demo-warning' => false, ]; diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index b7643c4751..57e2e48caf 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -771,112 +771,117 @@ return [ 'tags_group' => 'Tags group transactions together, which makes it possible to store reimbursements (in case you front money for others) and other "balancing acts" where expenses are summed up (the payments on your new TV) or where expenses and deposits are cancelling each other out (buying something with saved money). It\'s all up to you. Using tags the old-fashioned way is of course always possible.', 'tags_start' => 'Create a tag to get started or enter tags when creating new transactions.', - 'transaction_journal_information' => 'Transaction information', - 'transaction_journal_meta' => 'Meta information', - 'total_amount' => 'Total amount', + 'transaction_journal_information' => 'Transaction information', + 'transaction_journal_meta' => 'Meta information', + 'total_amount' => 'Total amount', // administration - 'administration' => 'Administration', - 'user_administration' => 'User administration', - 'list_all_users' => 'All users', - 'all_users' => 'All users', - 'all_blocked_domains' => 'All blocked domains', - 'blocked_domains' => 'Blocked domains', - 'no_domains_banned' => 'No domains blocked', - 'all_user_domains' => 'All user email address domains', - 'all_domains_is_filtered' => 'This list does not include already blocked domains.', - 'domain_now_blocked' => 'Domain :domain is now blocked', - 'domain_now_unblocked' => 'Domain :domain is now unblocked', - 'manual_block_domain' => 'Block a domain by hand', - 'block_domain' => 'Block domain', - 'no_domain_filled_in' => 'No domain filled in', - 'domain_already_blocked' => 'Domain :domain is already blocked', - 'domain_is_now_blocked' => 'Domain :domain is now blocked', - 'instance_configuration' => 'Configuration', - 'firefly_instance_configuration' => 'Configuration options for Firefly III', - 'setting_single_user_mode' => 'Single user mode', - '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', - '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', - 'total_size' => 'total size', - 'budget_or_budgets' => 'budget(s)', - 'budgets_with_limits' => 'budget(s) with configured amount', - 'rule_or_rules' => 'rule(s)', - 'rulegroup_or_groups' => 'rule group(s)', + 'administration' => 'Administration', + 'user_administration' => 'User administration', + 'list_all_users' => 'All users', + 'all_users' => 'All users', + 'all_blocked_domains' => 'All blocked domains', + 'blocked_domains' => 'Blocked domains', + 'no_domains_banned' => 'No domains blocked', + 'all_user_domains' => 'All user email address domains', + 'all_domains_is_filtered' => 'This list does not include already blocked domains.', + 'domain_now_blocked' => 'Domain :domain is now blocked', + 'domain_now_unblocked' => 'Domain :domain is now unblocked', + 'manual_block_domain' => 'Block a domain by hand', + 'block_domain' => 'Block domain', + 'no_domain_filled_in' => 'No domain filled in', + 'domain_already_blocked' => 'Domain :domain is already blocked', + 'domain_is_now_blocked' => 'Domain :domain is now blocked', + 'instance_configuration' => 'Configuration', + 'firefly_instance_configuration' => 'Configuration options for Firefly III', + 'setting_single_user_mode' => 'Single user mode', + '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', + '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', + 'total_size' => 'total size', + 'budget_or_budgets' => 'budget(s)', + 'budgets_with_limits' => 'budget(s) with configured amount', + 'rule_or_rules' => 'rule(s)', + 'rulegroup_or_groups' => 'rule group(s)', + 'setting_must_confirm_account' => 'Account confirmation', + 'setting_must_confirm_account_explain' => 'When this setting is enabled, users must activate their account before it can be used.', + 'configuration_updated' => 'The configuration has been updated', + 'setting_is_demo_site' => 'Demo site', + 'setting_is_demo_site_explain' => 'If you check this box, this installation will behave as if it is the demo site, which can have weird side effects.', // split a transaction: - 'transaction_meta_data' => 'Transaction meta-data', - 'transaction_dates' => 'Transaction dates', - 'splits' => 'Splits', - 'split_title_withdrawal' => 'Split your new withdrawal', - 'split_intro_one_withdrawal' => 'Firefly supports the "splitting" of a withdrawal.', - 'split_intro_two_withdrawal' => 'It means that the amount of money you\'ve spent is divided between several destination expense accounts, budgets or categories.', - 'split_intro_three_withdrawal' => 'For example: you could split your :total groceries so you pay :split_one from your "daily groceries" budget and :split_two from your "cigarettes" budget.', - 'split_table_intro_withdrawal' => 'Split your withdrawal in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', - 'store_splitted_withdrawal' => 'Store splitted withdrawal', - 'update_splitted_withdrawal' => 'Update splitted withdrawal', - 'split_title_deposit' => 'Split your new deposit', - 'split_intro_one_deposit' => 'Firefly supports the "splitting" of a deposit.', - 'split_intro_two_deposit' => 'It means that the amount of money you\'ve earned is divided between several source revenue accounts or categories.', - 'split_intro_three_deposit' => 'For example: you could split your :total salary so you get :split_one as your base salary and :split_two as a reimbursment for expenses made.', - 'split_table_intro_deposit' => 'Split your deposit in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', - 'store_splitted_deposit' => 'Store splitted deposit', - 'split_title_transfer' => 'Split your new transfer', - 'split_intro_one_transfer' => 'Firefly supports the "splitting" of a transfer.', - 'split_intro_two_transfer' => 'It means that the amount of money you\'re moving is divided between several categories or piggy banks.', - 'split_intro_three_transfer' => 'For example: you could split your :total move so you get :split_one in one piggy bank and :split_two in another.', - 'split_table_intro_transfer' => 'Split your transfer in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', - 'store_splitted_transfer' => 'Store splitted transfer', - 'add_another_split' => 'Add another split', - 'split-transactions' => 'Split transactions', - 'split-new-transaction' => 'Split a new transaction', - 'do_split' => 'Do a split', - 'split_this_withdrawal' => 'Split this withdrawal', - 'split_this_deposit' => 'Split this deposit', - 'split_this_transfer' => 'Split this transfer', - 'cannot_edit_multiple_source' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple source accounts.', - 'cannot_edit_multiple_dest' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple destination accounts.', - 'no_edit_multiple_left' => 'You have selected no valid transactions to edit.', + 'transaction_meta_data' => 'Transaction meta-data', + 'transaction_dates' => 'Transaction dates', + 'splits' => 'Splits', + 'split_title_withdrawal' => 'Split your new withdrawal', + 'split_intro_one_withdrawal' => 'Firefly supports the "splitting" of a withdrawal.', + 'split_intro_two_withdrawal' => 'It means that the amount of money you\'ve spent is divided between several destination expense accounts, budgets or categories.', + 'split_intro_three_withdrawal' => 'For example: you could split your :total groceries so you pay :split_one from your "daily groceries" budget and :split_two from your "cigarettes" budget.', + 'split_table_intro_withdrawal' => 'Split your withdrawal in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', + 'store_splitted_withdrawal' => 'Store splitted withdrawal', + 'update_splitted_withdrawal' => 'Update splitted withdrawal', + 'split_title_deposit' => 'Split your new deposit', + 'split_intro_one_deposit' => 'Firefly supports the "splitting" of a deposit.', + 'split_intro_two_deposit' => 'It means that the amount of money you\'ve earned is divided between several source revenue accounts or categories.', + 'split_intro_three_deposit' => 'For example: you could split your :total salary so you get :split_one as your base salary and :split_two as a reimbursment for expenses made.', + 'split_table_intro_deposit' => 'Split your deposit in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', + 'store_splitted_deposit' => 'Store splitted deposit', + 'split_title_transfer' => 'Split your new transfer', + 'split_intro_one_transfer' => 'Firefly supports the "splitting" of a transfer.', + 'split_intro_two_transfer' => 'It means that the amount of money you\'re moving is divided between several categories or piggy banks.', + 'split_intro_three_transfer' => 'For example: you could split your :total move so you get :split_one in one piggy bank and :split_two in another.', + 'split_table_intro_transfer' => 'Split your transfer in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.', + 'store_splitted_transfer' => 'Store splitted transfer', + 'add_another_split' => 'Add another split', + 'split-transactions' => 'Split transactions', + 'split-new-transaction' => 'Split a new transaction', + 'do_split' => 'Do a split', + 'split_this_withdrawal' => 'Split this withdrawal', + 'split_this_deposit' => 'Split this deposit', + 'split_this_transfer' => 'Split this transfer', + 'cannot_edit_multiple_source' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple source accounts.', + 'cannot_edit_multiple_dest' => 'You cannot edit splitted transaction #:id with description ":description" because it contains multiple destination accounts.', + 'no_edit_multiple_left' => 'You have selected no valid transactions to edit.', // import - 'configuration_file_help' => 'If you have previously imported data into Firefly III, you may have a configuration file, which will pre-set configuration values for you. For some banks, other users have kindly provided their configuration file.', - 'import_data_index' => 'Index', - 'import_file_type_csv' => 'CSV (comma separated values)', - 'import_file_type_help' => 'Select the type of file you will upload', - 'import_start' => 'Start the import', - 'configure_import' => 'Further configure your import', - 'import_finish_configuration' => 'Finish configuration', - 'settings_for_import' => 'Settings', - 'import_status' => 'Import status', - 'import_status_text' => 'The import is currently running, or will start momentarily.', - 'import_complete' => 'Import configuration complete!', - 'import_complete_text' => 'The import is ready to start. All the configuration you needed to do has been done. Please download the configuration file. It will help you with the import should it not go as planned. To actually run the import, you can either execute the following command in your console, or run the web-based import. Depending on your configuration, the console import will give you more feedback.', - 'import_download_config' => 'Download configuration', - 'import_start_import' => 'Start import', - 'import_data' => 'Import data', - 'import_data_full' => 'Import data into Firefly III', - 'import' => 'Import', - 'import_file_help' => 'Select your file', - 'import_status_settings_complete' => 'The import is ready to start.', - 'import_status_import_complete' => 'The import has completed.', - 'import_status_import_running' => 'The import is currently running. Please be patient.', - 'import_status_header' => 'Import status and progress', - 'import_status_errors' => 'Import errors', - 'import_status_report' => 'Import report', - 'import_finished' => 'Import has finished', - 'import_error_single' => 'An error has occured during the import.', - 'import_error_multi' => 'Some errors occured during the import.', - 'import_error_fatal' => 'There was an error during the import routine. Please check the log files. The error seems to be:', - 'import_error_timeout' => 'The import seems to have timed out. If this error persists, please import your data using the console command.', - 'import_double' => 'Row #:row: This row has been imported before, and is stored in :description.', - 'import_finished_all' => 'The import has finished. Please check out the results below.', - 'import_with_key' => 'Import with key \':key\'', - 'import_share_configuration' => 'Please consider downloading your configuration and sharing it at the import configuration center. This will allow other users of Firefly III to import their files more easily.', - 'import_finished_report' => 'The import has finished. Please note any errors in the block above this line. All transactions imported during this particular session have been tagged, and you can check them out below. ', - 'import_finished_link' => 'The transactions imported can be found in tag :tag.', - 'need_at_least_one_account' => 'You need at least one asset account to be able to create piggy banks', - 'see_help_top_right' => 'For more information, please check out the help pages using the icon in the top right corner of the page.', + 'configuration_file_help' => 'If you have previously imported data into Firefly III, you may have a configuration file, which will pre-set configuration values for you. For some banks, other users have kindly provided their configuration file.', + 'import_data_index' => 'Index', + 'import_file_type_csv' => 'CSV (comma separated values)', + 'import_file_type_help' => 'Select the type of file you will upload', + 'import_start' => 'Start the import', + 'configure_import' => 'Further configure your import', + 'import_finish_configuration' => 'Finish configuration', + 'settings_for_import' => 'Settings', + 'import_status' => 'Import status', + 'import_status_text' => 'The import is currently running, or will start momentarily.', + 'import_complete' => 'Import configuration complete!', + 'import_complete_text' => 'The import is ready to start. All the configuration you needed to do has been done. Please download the configuration file. It will help you with the import should it not go as planned. To actually run the import, you can either execute the following command in your console, or run the web-based import. Depending on your configuration, the console import will give you more feedback.', + 'import_download_config' => 'Download configuration', + 'import_start_import' => 'Start import', + 'import_data' => 'Import data', + 'import_data_full' => 'Import data into Firefly III', + 'import' => 'Import', + 'import_file_help' => 'Select your file', + 'import_status_settings_complete' => 'The import is ready to start.', + 'import_status_import_complete' => 'The import has completed.', + 'import_status_import_running' => 'The import is currently running. Please be patient.', + 'import_status_header' => 'Import status and progress', + 'import_status_errors' => 'Import errors', + 'import_status_report' => 'Import report', + 'import_finished' => 'Import has finished', + 'import_error_single' => 'An error has occured during the import.', + 'import_error_multi' => 'Some errors occured during the import.', + 'import_error_fatal' => 'There was an error during the import routine. Please check the log files. The error seems to be:', + 'import_error_timeout' => 'The import seems to have timed out. If this error persists, please import your data using the console command.', + 'import_double' => 'Row #:row: This row has been imported before, and is stored in :description.', + 'import_finished_all' => 'The import has finished. Please check out the results below.', + 'import_with_key' => 'Import with key \':key\'', + 'import_share_configuration' => 'Please consider downloading your configuration and sharing it at the import configuration center. This will allow other users of Firefly III to import their files more easily.', + 'import_finished_report' => 'The import has finished. Please note any errors in the block above this line. All transactions imported during this particular session have been tagged, and you can check them out below. ', + 'import_finished_link' => 'The transactions imported can be found in tag :tag.', + 'need_at_least_one_account' => 'You need at least one asset account to be able to create piggy banks', + 'see_help_top_right' => 'For more information, please check out the help pages using the icon in the top right corner of the page.', ]; diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php index cb264972d0..8933321f5d 100644 --- a/resources/lang/en_US/form.php +++ b/resources/lang/en_US/form.php @@ -64,12 +64,12 @@ return [ 'revenue_account_source' => 'Revenue account (source)', 'source_account_asset' => 'Source account (asset account)', 'destination_account_expense' => 'Destination account (expense account)', - 'destination_account_asset' => 'Destination account (asset account)', + 'destination_account_asset' => 'Destination account (asset account)', 'source_account_revenue' => 'Source account (revenue account)', 'type' => 'Type', 'convert_Withdrawal' => 'Convert withdrawal', - 'convert_Deposit' => 'Convert deposit', - 'convert_Transfer' => 'Convert transfer', + 'convert_Deposit' => 'Convert deposit', + 'convert_Transfer' => 'Convert transfer', 'amount' => 'Amount', @@ -150,6 +150,8 @@ return [ // admin 'domain' => 'Domain', 'single_user_mode' => 'Single user mode', + 'must_confirm_account' => 'New users must activate account', + 'is_demo_site' => 'Is demo site', // import 'import_file' => 'Import file', diff --git a/resources/views/admin/configuration/index.twig b/resources/views/admin/configuration/index.twig index 73e183c8b2..0864dfe379 100644 --- a/resources/views/admin/configuration/index.twig +++ b/resources/views/admin/configuration/index.twig @@ -10,7 +10,7 @@ - + {# single user mode #}
@@ -24,16 +24,47 @@
+ {# need to activate account #} +
+
+
+

{{ 'setting_must_confirm_account'|_ }}

+
+
+

+ {{ 'setting_must_confirm_account_explain'|_ }} +

+ {{ ExpandedForm.checkbox('must_confirm_account','1', mustConfirmAccount) }} +
+
+
+ + {# installation is demo site #} +
+
+
+

{{ 'setting_is_demo_site'|_ }}

+
+
+

+ {{ 'setting_is_demo_site_explain'|_ }} +

+ {{ ExpandedForm.checkbox('is_demo_site','1', isDemoSite) }} +
+
+
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%0DyT{kunD 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⋙XjcpMsTB<}!+~Xj9@I4si`Mf(~BgjqzaT6lI_+$E%T$QOUromM;gNW}?5k z^Qg2pRvrK!5~H09&w3&xi==ccDbs5<|MmKVClW;m@q4alkl3{nXp$fDJ`*A*e2^$+&R97WmDxMgGHPH6*d;JV3=A8_qjL-<3>U-~w+NP$GF}NE@&owc+eths zl_fU1u&E271H)ql!PocY!OQa_?YLE&)G=HRKwBc@CrIkGYPEW*l6^oDQxcQFgXp!;CU^&YN?DQtz#+sEv>C&fcS^cfSCa?cn30Qj=E3n- z2>~0GgSd)!wqB{t`E&VVXASrsW9AT(N+H!g57R`7&qkbNE}%AGg{3FVWdb9grR;U2 z6jNbvLE9}1-|3{WSCO3fi87nPi}C4l^+SgmlP1h=3gS(LWNkHxmYPhC#}O!gcyQ&Q z>vUEraxB64UPmB&EAMsii=p)9eq76=s=#juGfp5@*R!QZN1TkvR%y)@Zp1 zFD@A&7dEWb7M5A)CIq3rlg+nZFvOoixX`p&sB$JY(pfpuPU5j5(J~{%8lxtmqpi`L zlTaawVRoDsCvnU0-tsLrng7UE?2UA40CDDX!-JO>TxCBvBTE5tgu_gh1(d*ISm03k zwuzMxpAy~vEWySL1VzusdUVfSNf=XLjcQ9T5Q$R`)+59`7&N1Qq)}(gm6(J^peaR> zns0&P>~B%rIenl8Tt=F`{R#e97r@X)Tp)kckJWFbc;LY_;78B+Ch#rKD8g6lVkgtE zZ3xAv`Jdux`lo3KA5GcS&-*_B>=Yg)0E6^+31q!=wHXi|E}NE>M24L7S@wsofCphG zr?7+!cYwV;L9`u=W)4e+%!jTtRAk=aaTmZZPAAEe>OW-hL7^!xeMH@RoI&j8&4 zt(%0g!d#8Cn1j3NtvWSOS;TnBg_ znQp@-H+N##fXrrFC(pKa-Ud4p3Xrp5_vW?LKqUHQWX+V@&>kRW$$_H8~8}KKwFlk+cRs zfqz!a$UFpAV9DhPunM-{0Kz4JdK};8EIbS0bfr*a4nqp85D(dE=<5U&j3=O914}b- zoa0?TebDCRO#B5R>Z8h1dEKab8@NUFk4(PON5M5O3bicm?HgoDal@h145Lr}x3G_n z+xrlA2RGy$x&E>vM>Nd|%Spd*^;G_Es<7<0^AD$&TZk!=+#ImC8cbY}+nu4H8?|y= zD{G8kbFw%ai@8UO^0rIAYtCX;l> znnid?IB+@<)fYl;j?Hu66tG{3hlALiVJ370c-}TV^j6_)R8-0Tk1z{#=>V%q7g`9I z539w&=&KRaY$~E&huX`tt~MLCrs*Qle8xlhPtL3MyST_wt*eOyww!#MQQ&0#*|!g_ zUV&dt%Tv4d;g*OvAyY5}OI;I73sU+jxo^HagFY@u7%B`|UMN)RU8S0ny3QOze#a7tJw;nPII zLv)PfQYcJmNOyPOp(SubPM07R^R?AL*jAd5ms=`OnxB zqvn;4v>y%?P6Jyy+@RD)Q;{4e4ThJ*lr$0tfXGrro&kDmJQ?s|wI)Ql5&ZG)TVD$t z4=Cklei8%Vu^`gZ<37lc%L<@$6B~d>)UjIwQWQN)4VbelGj|~!Efsm({J2i1M73;G0 zS6qxC3>+N0v>_Qe45Bj6hq2jfF58kOR#(+lK_=v~U`iR$1r)&WvTO8P7A;??w@-*^ z($3aMU3N*Dd+Sc=RxHE|z&sdhV1>@sn8bPG0twdxtME2Oexx0AaCQ`9(oNwgvXe^z z9SF>FM5VHTk>!Dep(%epu{;UjD_%#q_6LM`0pnH-aNw`d>j1rf z&rD@^gri5rTKyF6z;zu(ollRE_B^A`>vJJJff@48Nb7bcO*!z8#@!ZmJ~~HO;)EZR z<(8C(ADfLEOV_-@P)^f|yI3)dOJs<})LZg@Tz0ZRM=W6wD2grZ(at%6!CQ+SaHSRa z>B05l;pP7&a-V#j9Mr&d8Z!i0h6gG$BP1SfvszZfX~55{2#MAfWX~u~O1CN^P54xV z&!6Z743m@$+2P%%%KsV7$kv;U*#OhRuR@R-3D=ez31Am@+h%h;i)js z49XSnbFIh_dBVU7S$)k-WfR}4rkJyp%X20{E9IIdyacBwKpZXyPb05|(_;r8vO@_b z?Ol2Z8?38fh{zCxpgI-8A|{;O{vDt$CBRu6!9AO{gujd$*^z(=dd0aM^1-Q$FoiLr z&Jj!b?1BSuaPU@V5X);*orRV*&WZpgHvB8=6=I$R0kla~*kgbS#~!Q>t1jbBsLmRu z@b{!}wIdHQpaIh%pn00=yrVM%-M1g;yOkeA9~e`G|0n_gWAE3PEX&eV{&INgL#aOf z>2=VPs=-gfGBD0KkkE-`jTEQXSA9w_yliWT$Fg;pk#;8J777VT*aKf`t`LV?pV}3U z@?q6+=uL5_GBz|W;%TtaQ$QENONE{u%-UXq-oL-o>=&n?hI8DE(uYO1&Qxv%~kU3+KCCP|z_k&7%%8 zQvuXAjMuFl!#CrV-9)=0rcb%_Ya#LNA;b|T&Jkv)l!|~>rqCwJngoz~E&(4T1Y6A? z0;@94QAps3<4J4v*v_^6E6M5Vr+NdVy)Of^}<){Misx*P-&=nzETu#gZ zRg%pm2j?i}UB%Cxz=76enl51HdBbJV5_WX7bx9Q{lTh2 zk)r{6L7z%oRQnp#24s4Pb@!sR7iw!=s$waM23=m4Lt#0Dr{u+Nvim~Y%P4W zHnQFu@^Jr?^U)6iuJBFlk9$VY)A`TZ&3Sui;9xvx$;$>y@F%MY=06KzhqryVGZAmx@SV#{}1F1i& zK?$sJ!+$;sM}n(JYz9NaY07LcIp!sj1nFdes8AQ!_?~?V(+ljIXym2v(w{Q5eSeo9 zdvCd+Q$ms+{7urVEY|C>Wh63m#1Z{IvLvz=D2d#Y+<95&IVAg(6WhL(5v;@{A1)z_ zS)Ow(k_m5gNSx+eNs#%)STuDaazE+^sfNg2?coUz9YjRvODvO8kcgVf;24c?ksYic zTiEkNl^@oapHYftC9AmM&C1#zDVo3`7LPd@59lG`c>~!jc^VSpDAmj&^aH$?hTSRm zwXsv^R#n8Zl$w^rb0co> zWUw;B(TM+PaRwg>SpbFw{OkSF_<-pH1^_wEBGe-n9?yGB?_r6&0yy!H=?~1q!>EGB z-aSOvvekfQ4S)GXq?IAbUd+i46+UOZj^T#IDt2-LjbLHVAZ{;bG$SJmLOVhOMVUXi zf!4w|I;j%0fyJNW7ASmhe@&x~i>w%VvARUFCsEK2Z5t#;7@|+#8vY9CA^yrMI8#kH z(?#ioug~g-DrN(~(5=W|nHi}vEoGm_Vd^I5wx~WKe=0?zOov*Qr$BMw&rPs)OPgTi zZdYxL(JcNJm6s~cAZ;dUeXt2Z0^&C+xD1|wwVnyGPz>wbP@Div7eWA6@Nu|!Tm1E4 zXv;7VX~=x$n(-rR=ls9sgwLCZxNK*fkUZr?UR4>@^kfF?gslsJN)|1loxIbSG+4Mp*C$mYth>TvH;3ZZ0#%q$<2O!0Ljbq1Fk3bNGO)!n6YRe zOH5TuXniQV59Bxp^Tg5um;{Gunor{cA!67P0-1|JLCC<$h?tE5qZ_L_m~B%6{}WA@ zL}yi+y%tOtM~4=&FpiQXuL;z22N}^y8r3+W$yaE+VkC~lYIGX{)8AlwPeaYT^ek-H zJZ2_u)>{F;l?Y<~ce2efjNTgk=4E~p>e)iHN+R-cBGq)O@fI1fX`M*4!-=zMA(!M7qCs$C*vH5NP=sj~$u z{UDA}zzP*Gh0FlQVcsPGg8Uj2wE!9BMig*4zc?&6SY4^zn21^Rj1l6zp87*ac5Q&0 zSChB|>%W~ttcVjQGADJ%5}FNt7%vwLoL0b=<}6B#Rm%h)%HN$iht5e1F4U9a*LvF` z3~(8ORA1mpPFW-p-hoYFmZN5=ay$izn><)C=x4=g3-1NQn&pzcgTDLmS6cm|864C2 zX$@lI-}{ zz#Jqd$Ms3(;!FczP=+nC-tgo8_i^)#NEP_X$e?QB&)9v1X_oJ(0_D66f^RTXqYs3p ziOE=Z=WA7sl!4Y#Mb}vawI9=p{_7D^K&q7vI1ujNV%rnwN;?(V=!8E1S|iPDw-7{0 zP?Fw=WJ{}hVT=LrK~c!`kT5;lxrB3+q<2(5pRSl&@Lm%LW0)NR$X8PKM|qv4xtJY`5Nd0Mnx4dhzx=#O3}#m9#0hG(7kZ0C$o<* zRlc?q$4T?^>whL|Hz+HOf#*jP@->8k{tnVScsrX=5VQubAlqo+8ep2HH9cA&yP%@3 zSE(q|<|pFnc(QRJF4NyTno(W?cX0C_s)(Fhf}Rt}2UDCR^w6Ns8hlL(s-@DjsLr5a z6@bN(BRR>VEhDCQQ_Pj9t=XYnSh-JZHZGFN2`K`1hS+?S9airR=eKgf@E!Xw8G{$e zk~^8L>zFYZyoxI0qX{i*=Gb8t>l`qkD$xFT=)hsE8x?k(F}5KPBcluL-9&!{fw2st zwGYyYcinq+J0lNy7=;}+F#NT!c_Db(C9Oo59Dxo=RgBe3g&a*mao|ZcL^CF5lo01s z5^#FqF(?HFWp#`xJqhczP^lVw8TY9M2zT&&ia!~zQOT^omAbsxqt;w88q1NOgzWa9 zxaNq78#=+jG$3FOtVk#;ZbTb{S})e7rW8SrHBE|a0gdq{&0so=Fc(qfhJGWEOYjWg zLrg~vS}pMJmH;8g_~f$vRy~vBdlPY7j{B#R*FlrhNk%H%j6?Q~BMUC!ONa1; zv+yzYD|%87m2%X$dsW=JyVM_*;3yHYlKRaSjE@=l`&EBuw^GhvvAX5|fqx{{P;*s! zqnb)HP*v1fk>zxww1_rPZaqb%QsWXCdAre|Lr*7Z3r=xF&oFTFV1=_ zP{=!R$AH32RKGjQt_t2|tm-CR9u_N9R`5-I_vcQNNQODri8-mOOWV{!nQIEHN=c}` zNvNKyC-oGVoQ1NI2emB1Ab>Nzwa^vnZV3&6AyrP~@FSkZ7Zvx9Z>W<6XtDK&)tcz-E7 zFWT!Z7$H|c1b9p>yk4X6L$T1UL*b8oP=0Oy2JGXV#yLGfB>iQVlGoq}&;=02`+zIF z9i_iOU0v5I@n|VC`VHh^^Ms8d0!Ay->IvVWeBs?yHE+_5SIXSUWWj5`q5DweLx4IZ z*Wd}VH#Q}l$FjL^0J=DqboWqChQr|xA3m3mW)uejGBy;brz1G=;3OK817SD-J-IR#_1WnFWWJBW6wwR@iLc7j$@JkeZ)YcTAHg_ut1x6HsX7 z@9Y*=!j0_FJ&BtLn%>Mcjt<5T8A!a3+F&r@bm9UrW+4o51rA_sUdjp#1C*+6$q-BN zz>Kcsi7Mwk6aYoM6lfU%1Q(@+oz}NaHgRL=j=396UCOZAbGUUX^GMKy06*fA8jYe$ zWHsrssWD!c>RFacvBriV%|RpTpwW6C3e>aMF^RyRo>PjHK&;kp~?hx6?fGU8kS4Fo1+s+Am4R4PakzYo0CL&l3AAj^I`m5Quf{ukC)2i!qZ_il!HO2nuJiJ z+Oq)B)E*i|qRgI0Ol(YqQb3B7SkMWJ`eG}MuaH9->aLEsNh<%t4FRg!0^2oqr*WgB z$BjeO5SV?Dv!?Hm3OTm64LgK#(&x)GaCks-XKEkt0|%aV0ED#cArQP0FvNr9q*T54xT{fn?GaoUE}RMpKk9{D zaq@*PELdG~>T&Xy-5T2HxbA|f+!~ADHc09(RF+{w2X@n`-!gs`^LzevCpBZo3JH!D zq-AiZQX&rymDozbI0S3bSp!#|c7Lg>DQzii*m|@l0p2ckORF-DkH%8GsdgkZb?w3# zcUn=zz-QX^!i2(>HTX(Wr2;THX8(|Seemq1)d)42JcH(Oxn~HEaV&&$b$8Zh)OVkX zce1XQyzS%FUxbu7P>oy$UvT!xK{Q}J zdlWdw0gIfm9DhnCMnm~Nq{0^DQ3#BEJ$!@d&s>s+5qUrh6t0cm2$ErP41%fz`2yiT zqjEk70W9PNV~!m_Hl3ut36QP~kU-)JT(44mCj-s?($$QOjmN{-ksf9q@j9b&#mRbU z1iC3Jb+}ET(>W;sRe9qHV#)dUV?PKLja>*d!z7K|o#95`*?h@7olBbHHjO3?`Am;n{y=i2 zv^f#-AF_<$;vf+KBE)Y=RxAH%$MY$J2zoBEnRFQXm+JDB)~fi#{TLW>|;_0>&8J+JTtet|VP#@Q&f zGS5zrsbK)3Gf36J&wa0DLgd`4V80B(1<_d?*h=sGW18Ec@n2@c(y#&wv!0@|2?T-&H)F@ANc!@a`WgN# zT_FI8;ZjooDk55`I>jf94^Y691yO{-K;us4q2XaUDhSq+aqIZz0LA z5lsy8j@SK$J_XOCbR@PO6j+I5II;Vd5{uY)NE|UM)yCW^X0cQ7s&AI_uT!iKw$c2S_o%JYM4-?smyGSb$e5a$r&WZ|WTwAQ7 zK4h-VJ#85rnp9cAP|EEn!X`=+hk1%h#YvEs<0mchQa#(&)y=mI9iz!WXGFgr%ED$d zc(giqqi>I!CkVj512ZaNdEaik2zvsy9+|{?mdPg=*y6UO1YYSc~~ zMHE<8Y&Iwnv4{VmC;_SLND3mly1;8nrg7*XgA6b)c}0)>+EqM=aXk+7wde9E;7`=3 zIDaP?NFu0GdiW_;;-|<5j)&8j5~wY4lr!i{4%vB{yI;}09R0L!s?brBsiD0FD`n~7}mELwwUD45V* zR=)*{(`tHnQi^hAa_tBmUc-j~i%<~!dH@Vh1~-Wf9RL+@ENL7Cw1}knAjYB)qsc@^ zoId#x$Z0MY?T&zf>RHRkq)O}(g!mw^?LSWmfnJ=7BeK0#6sAR?TK(g~rQxCS9b2c+ z(u`DMm%|Jc+j0?HhkwP`lf;fzVmbp*V_^x8g}{Lm5!^gTPAA_8pRcRcFEQmKhiqMu zJ*H3|4FHh^i^4ui!eow|FT-#zivV~ef%)kKsg8F3g(~@^3ppNbS`f`dGoCCV8%TsZ zXS-R9MZzx;TJWeRx!MN0h+o3Y{~d^31x1*mxw|@#AP+C~{nM7!~}V9~;j5D8(*2B!*870GjPz~Qeo%~UoVAVYp^k{@5c{1^$jdl`Sqm$$lG zR&OgRwyiq+Ne8f)QkSV_$lDF&8qqucW%h22qN4?Mdi|o z@dM3$frMNnEsv$)!s7@#4ce*~fi4enOOT>!6`Q&n`JGE1!22XXHL{+{uo)o>Ok|S{qsM>s*vTp{F!<#!hhY|#cq>4zAbc*vF@G$g?R^g5aEzm~~ zq>F!f0|jIl9%P(IZKr;GqlcKc9efpPt0O24%QFE07)I4muy1d769b229$*;3S*F~f zsa#59HFw6z?+HzvY3Dcq1|>TG$%u&W2q|vS7?Je>Pt0HNW7P72g`A)r{@BA#mfICo zVcU?3g$Iu2;M^^+SmPEpu+{>${}DsO%xEdYy z0`)iJSbshpFm(!BY_pR+Yy3ig9m7RE!=w5Yo^cj%?~o z8~PX6f|&U%584rT-33s=p=1FilPqY1{4st|=Rf%DwF{57i5hwc{pmqq!-B%$U9yv# zeSWmH*rm4Om9-^v`QZo){Ab01U`Ti@@pC1)Cm)$gX|y6XC5Z*#BztUjlemznJa)WY zfOMF5jQbsvMGf2GU6#%_a5M!EvXc@*6H_5fk8MtKIE@CTRD^_@(ibcTw$B=Z=_&4i znP7RmbvD92Y4a$$!V!ng@xl%Hnd(Ne_VX|hM<9F$Azh+Xea=e~QrWe#ejb@b%ocr4 z#EVTx7>JoYN$!0}rSjH@wkbr=U|q0Sz-5NMVMDL#QA+W9+!O)@wpwDkDf@e#yAr-i zl9lUP6mU8V=BVV$ZG62#&` zR|=qK_~HKQ6fb6?mKh=X(@G{@S&fv2Xq!?&v8=Rug$ZQtY1v+6t^H#Qmf6XHA$A;KPK87$whl$RDD5);QkByhlrQ?k8x(MAL- zgO(IUMsZ<8(EO3sN#GnlJMG3#Tj+?9hqoZ*8_J@Ps8>jF zTPtr23neK;xz{3msSjd^XS6OnXg#}I>SeFkDx}GzQ;V>rFyL1$%800!qH*AB&4>>t z+Gx}}GH^FAYJBVCp18Nfg~p9x{4w2D#wFWndmU5s~4khVw&`q` z8BJ>xX|G$wf`m*noq95?H*1AV%*A>@#D@ZE%+-+Sks?f444yMtAPs7b@mbJ*KaDXU z*xyYN`~#sg_otG5Sl<>U^TP1cHY*b2Gic`aI1r=m2VgF+s)UGWStj!pKpl?}Cg5m< z9niH%(1;@zYQZQlqbSSxjU3nj{tPzUeC6SS4xR+LNIUR4CoR|4d0zzwWbA>b*X#yJ zGegyw9NpRcCH8SfN8N>Q5f%>~?236Z)5D5=qniP$iP@oF4D2-z8ht}c zD-C^_AH@nX0OtZ#(`$ew=h2n3I!VQXGR`*al~=iK)l_Hshsx*9b+HgMS?AznM2{y? z%T$w=5a%Ht?h|lD`>}Cwnrz)L=_YzkTYM3pw(J4yS}Mr+1f;Bbe*5}YPqp6;R0dN0 zG`@{Llp?`+X{l#lH7J8MLXuVc!GRxukzCNrA%s9q|LK*543VO0)}sE1R^VYgq>;9` zHQWe*SYbK003suvL0-{Kw}=zp(&wS%LWAfvXkb{v5Gs-JpSrgK(xpp0N@G2cm`f51 zP24k&xFKBS*$W&N6%LqZbbxe@;RC1Fj4}ZU$zdFG6af{;8M+Wdx#CDawoK^-P^L!q zDUAD!=YHU+)^DzC)6CYZz%CpvHw{F9O%cX1W$c&5K{MkJ1;1pwC4NhXi>1Ks3+^^6 z;%u|@H8H`(kO=yh&zlw{U8y5OZk#Al3L?R6xJ)4qpkj}Jy+K5pTqNi9-?mb`3`HTl zSNR9D9|On$3kV*{aj5KRJOh;=;VIpDiHTwa4lOj-*)d>duKkU+T3Z^Thjg;2nkExk zoe}iCjJq<;et-#gSQ|>g3u=|{`W|%b20%3^DCrj!jHCepWom&}r()g%QZLpF&1rit zddP-ph zg&JxxNgFUR`3-af-5G(@W?p-gJ-L}8kP2EvP+b>bF-D}r%Iw_&xbgh=&B7TNsw z?q3GmRSY`0ef*?^5=G zsI=^mGU~6JgSlm?XsM-c%SE`dzEhBZ<`}Xm?c_cVXPJH%a!XG}5%!ayEy!~|CzLS? zc9Kz6pU~uu4NXwiO32T~!r%}2hg;SJfF6DDG|qIa&rcKe@aiCaFAi4O!kd ze_%-m4HLz8;zQ@kkJ}Wt*?fH2cE>EB*uy<5z;{V(`D1etY>eWuXkoEz!EOmbb-}n% zwGct+!A$!%!z*!arwm0q@UgfzwN1!jyZ5K#^t!6uHj2KE>=?aaS8G7ar(^ zS8ZU^oMg{#TCaL46OQaFnK}SAHtPS=W3RS&ZWZjZMQG~}K$fn2-LTXb-GR8qrE!x+ zugIkh#rbF?^GkwQT~3Y4T?W+mL!*inJw}GMs+VaU#37L zY2IT84ec#2F93@W4ZXJ)8N!TrvDWbuW4)hK`ueMi;1r-aBiXgAG3lld7a<@Dh0Id& zHes%%rp42Z!n$ZuAln)8hj`IYJw>xrOQ77#TPtO0vToGQxIP6oVQ3Q6#J}#NK`Rg~ z^|j$Djl&cX`kC9kY2d$~^2?}}+y_6(Em{L%0`E9o5N=dwg1&am^sKsskr=%QptUm` zE{UO}vj+n3j9f#70z;D7(wEJH97H!cfD9lF2cWC^9Q|X}co3Z5VC-AQ#Pa#HnRS(i zOJu103w%?J6ZohFfGyx^!wgYtxO}Drz^p~){>$A>sT%I{ad4evd$ z(^O@x!fD5WJy}IgP#zj^$6yHpr&#eqDTed>U^GsPJ8(=aB3O64bx39tV^#YK=Jtbe zMw4bXBbvaR(2sQ}zc(p$HS~m!d!*UyN2L4dtpWM*l~&0o*sv@Ax^P9T-VCoER6Jw4 zGzAgE-P=^oqmV^DZU!l>$O_e9k5B)i5Z@w2(%$K(UbtQT5GW6sN3vNh?9cnam6jL* z^pT)@K@^`&zPlfbCVCGBpt_I174gRma0je2B=j5NiyTYVWHfVGFkXNF1_jJBlDP?h zuhcEQ4bWw7zK#U|gWN9IxA0B(e3%e!lPtUn1OfHYcp*A1iP|GEo3whOB3*}#EP(oL zuUFA^FG|5EJCVi|mhRX4LOlWhL|<`o zuHN=@g0KZqw<8}LvMiHI5$3kt$`L0gBQw{|0rN+u_uuX)2PYn(CJef-zMl7wEC>Bn z$-?!)SzQd54-Y&84lsnK&`E)gv=U>93_s9Q?O<;3MA-PAc=Rz96Ghd>_^&+i%)%v* z$DTei4Lp04EGpXg=`%J!Tvwj~b3{(q%98y3>2mmf#SnF5T4g9d29E zS}G&VpJI&i?O0(=H8l!qDw?4}Rwx|BPG@XYScbQaG%;FoszO}K^J1$x#1m;c8!puT zZ1YCmqb8-7D)v~IXn>AFhyVrh=mCj}+6;Z$fV^V(&})soB7F=S!5Lu2Hoc>mL+hGe zP>KnRvaX9N-(onWC+_tDbD(BMB0`*c#1jY(ugus9bkU8dE=v#SOfSH#m6z#APDl3&k8}PvLdsL&CUCd8hwR!wxVOvj+fGj7;k= z98+)Dqy&&iv+yOd;WhwgH$Guva|gYHjHb;>8ydK%B^JSOhAImdXWaY1)AZ)S@fc$=sa>lZq>{YD+7} z;|h6SKG*Ap2f7pDR%ah-b7A8WTc~J=fxkq=lJWpmNRun!5=m&`6S~8k1S|G7%o+|M zwg<6NFv;jd%wcK>o? z2j}5YafuH_tF8lGBp^;O{~*RNa6>_;&^iIUqBr+JD@81s$G=oP4_H|8K2F-^fr1k% zoc!&6xVgZPNxB*EC~n3L0DVa?_n)0-G>xGm*#;RmFD{R{1HzjmfID`IpyHCr_Dw`I zSLr}fc1M;Hp3@GKfvve{tC=d)Q~}i@IFS$PQ|PI^UUG0-zo^z~$Wz;3Y++{e=t-#` zY_wHOD5wc7-qC@YW1+h_Rh5+q{@s+^Xd^=!DAC94`<2+S$nVAO>iouJ`cx<=26AYv zkT&sygn3EQe?!kf=0z>kdsK;&zJ!K;dWu^tbEAj{{7@yT05p30Cf0v^7h?W1mb0_j zF~{`iln3L}x@@WWW0NI^&_ez}m;v7ov8D8x9C*GEDF?o-{PaShpDPy@|ETddFH{LM zvjKD%{)89wfbax1EV7@ZpDqkv2HAsU`SK9Zw@k9+JOvaoa0!=ZFrY;*x^|RPaAZFr z{Tfh==5lmv+%fMu}x+p9WIg=M4eB=Rw+N}Xb#ujecQ{pHXg!QoM8D^gYoE0`z0ka|i z-_w-c5%QHJ?g5MQj5B8NzgeS{5NDhN)i_#&!GuReF&0_>G$TL~5J00m3z{^TMoRe% zJbZxBP#GHn6lX2Py35Eh5k*+&m3NlwNcADrc*KebiuutFg_B}wS+c^Y*(C6oKebOSau^u4Bf5sO&<{Pvz)%i> zBwOo@X)@$z5hQ6Y!M7Mb6}b75NnL(WFV;hrvcgD!Xi0Ub8S9NDYAkZNK{N<=G$N@@ zw_ON*vVBBU4t}-8g7t|-kTMK4xqKpdn~reICdGn9vteL2&WZ8I{i^}BNW6CdJ{DJk z&Asy-eLh(QzjS<2?Hk~vNQ2~nhi2kU?d0f&V(Fy{XlOA3G7ScH@CjWPMjO1~z)p`t zHs;Jb))g3Z(4PE5&RC8+l_>!Oqz|m)g{xj=H5Z&Lv^F50&iTk9OG~ZR*PkeSXj6;8 z4LwCHEXXzpC^=sl;EKz^fbpB@Rxq9s85qJTb*FiblP_@4a4F3-h7WY@(3iR5+kjAIeM2D>739S$7sjkIi9M4V>ZVjNRF*3Rq+G zAHqM#QPnZTdiLOaz%C-r3t4P*?VRsEW^fPIM81&TY@Mo%Nh{dj>hMH4I6 zG&gFpBEKQS8Oa5gxUaizFqO89N=6>@=^4W}fK5G#1}&|Q zaIP+n84u3N%mF);wyN1o2tA40wnIyHcF@nQ z@4&-WGW=%ervm7f8m6B~bs3DCs4et_PC!Wghfu{f*-MP(-Gw*$B#FNlKqH?p8y+5- zox;*_K--T&HAGH8rw`Q6>+29(pBNXn2VeVfi;?z)9pc&`6P+a{BVQRF4S?bP3S!$~ zmc^YYVG+fYGHkDT6N9XRZwba02H`g;Wv@hA16vCQ<}B|N3aqQL&6`VtAE3b1I>MBV zAPNvEA+=x_pGGZ%uxG7}B;A+#0-l`FAp$QLo@79Gi}*(VQ4H@4W(hoj28I=428M+2 zbV_H>O`KJ|dP+&Y!d67<;Y)I{mOH3eI8gX!L4KwCgW&lm7|d<_7R2vEqC&vkHZ^`II!}hIJp&0Q7?mb%zR2r zYv^fdx>VY)N6TlI$u5;N^D7gEBwur4k=+7`HcA?PDVh>o?ajt;{!&@uhY0GBL0OnI zxS{v!{NZrGpPDtrLZKQ`OYATMJD$;&vxCXlLin*PDRh|O+IV&`uGh!RZzM7ZRhWO3 zo(~{mT{A0k`wRc0-?yBlb>p5B0nFK(`GQG7&U-PNSa#;zaqlD+!Vk*0`UJDu=aVwh z!pwMZCA1yypaSX<97cG2oKV7ok(p~@skadz_C`n0B18-GerV%W;Ne}16SpDya#sK8 zhL?vTH*+*&UyY?0lFqk^aRkRcM2XfP1bG0uaUv<{Si8)$6H-(>5_sZz5|BcK%w-@Y z{JOLD+IFFEA{T_1?3CO|6*n>e!h&6|8$o$zx`WN1|M;clj* zs|8@7heRW}?vf;?Ng6^Va~ivr;b5V4mgAf|7d58tV%5ja!?F?a{EL(}tG$TQTTxJw zB1k|S!;l^xyf#%No50!f(g5%iuaG;NMBxa6q9CYG&&yUWxFvH+XR|z6ONxe(SKNpb zkp`EIBh&CBeT<)HF2Y!p>}!ck^8v92ddwXF@O0oJm}5aZ3nPfaCOG-=ohoo(at>a! zZs~n2Ik8&o#pCu68!Gvj*FNh#=IqA|IbvADisw4NS8Sjmb>5Sz@QH>6liPb@T?^+p+^&lRViZ;3u@95HTiC zO9rZ*VvU6a{I)$*sRYI+Ku3_Kk`xCxsTE6!NSKwnyB3{Z?HfG;U7#WZXE8D@SLZyX zrGt{d={_Zu{&HxpO@myO6~p9Gf+yeT64+$HpV}xZ4M>pjN@emk5y%h8(2$21)Iz|b zc^dSjkPi|OJ^+9-t=Ph3UAW(Tx+CJ;XwYJJ2!EJ@FRSQNsv&xmQ&YHxOlB3=W$AK%QUAxe%m1Oo}XOm!TeZjC3@O(=3=>!9ESxNawdpg5eA7y8||anN!Ii_*YK+liSFfd-Zb z;b_|!`YzJNE})>@Ixw#i z9|P0DuL8W{zOCaGFZQ5CuXeL}|7}~ptcP{`9Kp4)U5w91MM`vvUSxuZo zPKu0D>d{^l1xE3q!7096J+4WY8>uOwlR)!f2idum+LgitK=ESd?D0^f{Q22*ZN?I^ zk26vdF{#ZQl0KIx0e1+53BrVxZ5Ed}Wa{9&^hxEXFFL>oc9MCpM*+t+4B&gNEjO$l z*g&w|U*VVQ0wVg94_eihN|neeT+B-+?C-reS99l+k`a!{`vJUfc6mz_m5({xzc9I; zEb*XcaKh#n=5_JKyovVR^&wI#?G}b$<8f;G&pqH97V(_?c<9ZLSl}@>k57=n6r!{l zM8h{j_ejA|q=s=n{r=?Z`-HR1yN#1yBlc`uhBaiV{Z)4y%^@cFyraNoU>i9Sn#zb=GZ~;RPsS9L1!I0D zNf3!eTwWAHa!@-!_`@`Bz`u;`KO|T|w4n&$a+?C+X1!S(yK2P<5F@3H&kGGFv3aVN?NuM9hL6 zRXYl?q&8$S>F5-Q(jxf-NSyLwCt8QrVth>3`G8m$oh@={XJRO6_0m9ZtJJ)nvhZOczWp z!V?7S>pRp4CF`t^{K%@2n|R6)q5MbI%ihgbQm&10GNp*yYe_40_b67^vuAc@!*l5#%os{*10y)bcK zr2vJ-|HS*QOo~CbcCsi!Q7}P*JY)NMUgb<$7q=qDJ>f8l*iPKc@j?VqwpPl<$fWEL zqU@&ST4;>jrkD@gst9<&I4LdIn(%Gd=m!Q`6*K@l<}}&$^i)ON1%=saTZGTmu4(Z;9bIG&Lvxok1vuo0Y#)#-Sk0a%4Kb_hE5zTgn08op-VIX7P$DKP^O}Aj zB63T|hTLbq!R`y&G7+K5Z~Vmmn`KAK8dJa}R1+iD2*=DpY)M7PqY6V=nXDl+@CG~# z@0fZ*v(+dSB|}+M5XyV;mQT*d-8sUy=+l#I><3k{U<7lig(xy%T}8TYbps&BpfUO? z?f{?oO0|MC)e(6>3=1(qqv@p^&P5khW2;e^#$~KmI)g#T4ir)5^smMZhbi>$L^Ac|$_=3U^}0 zN@WJDXvi8T4Swtni^6^VU`PivOJh-}^h8+F$C{FRojqu;5&M98_D^ayMO=dh3fpMl z!Vsh`7tChJAVJV7^oY-gp&w_-k`S3+3Gp(a)87|F09II0Gid6D!ifPirgF5MZ=xC^ zUDcpN-I@wJzz6(Upr$)t)nRmw3aF41aVrY?AZ*fthYS@=P{xZkN-8!*<;DiZP6A3` zXmEBKcvk*?((WG z344d5sA^miUIQPmIC_-PGI^Z>Mp{rhysZ6Jj%4-vrYu;l|3B`{Ab^&X4x^x{T#Ve} z2Ir^7b6pyHRk+oOh=qc-=&-$SEBc05^TmOp;Fmvw5IZ5$xZsi+xZ$kfkuT93k-Pvuf#tG*+F^$^rGo$*Q5HABvpn6k^ucxq=bjhs-PILHuw=NBAGkJZa|3K zaGrov45Z>C5ul5md{ii;QSfL`m52m&aZvw2h=em+5t5{V6f%*Gg$`*OCI@_*31#u> z3JZKBR=FZgSz0lg5wNTQWG2AJZUy^@CK(6t3(L3DLX#Ji!IKFyF3Cz}6MVVpGcwJQ%hFiAYm0 zUx8l!{<0+n3w%2Q@<&aCRnUbZi(q*KK|St5A3F+Q6J1b_AC@W%!W>yh#jM}bWS&MX ze@zw?Qg(27u`rq3+v360SyN?L0BF>B=^bSO+2Mj`3p%BZsag|&M7c}~Yf)GRc@hCD z9(5fDx8(qyBPvqcMHLaQi5!3y4MKINJEd$17?LCRswuWPq z|7~sPdgWe@GF(r1*q<7CrJA~S^PCDx8~0(kLk18P4T?^{UKJV?K6HY01PK@@4TSV5 zYxEXO53*u8K7qqCxk-AR!aY4IWAlLY0y)G?VC_kOqfltlgP|l7m_Q?(69bgVhyjP) z``WGQR-V~AaHn$XjK;ZJ0T}l842u;#;9SABQS4$nj0;#(V*2ihCto@@X1MC|^{c3) zQV1_VRo!r_yYg2~J-?>XB*0$PeDvhf~Ok$U_X~fFbM^S z)FD&i(^9`FB836g95a1oIXHS(f0xRRK zAba8su3CmhM8Ff89V&|RZGDywf-D<+k>hPn83Lqx+Ad)Wu_!)>?eKAKvJB}4laB$A z>deSF_i59&?MB6#ie(P7;!fmMj&tY$&|%?7c(lqAk_wchdG9TkSw zM;4OpC(=~bg87(dTA=ikF$Ouno`qR}1gIT!*#iBEjZnBrhfnh%PYksmc?V7&T)Iwh zy8dSl(}|$+XbF_(!4KMHE%Iu7VVx5)p%EMEEP&jw2L_Y)k$Qc6N*A6t_wF~oW5Nm< zt3t`5;>p##e|p%x`v+I46xd44N^*(f#CjgO9M9>^mHg!!WEYwM6&^M(G-X{23NL$K-v*MLZ*A8=%$z8}M~YO2WjjL}rR!Wg z+DFt%kuJMZ*qeXRg7IgpFA8bp%Pnes^(0ZPo;>D;;H*%JuMww(aNEGKe_fPR=Tiz} zVLj_6(zgeVVVu7BT7>lw=D<|~e@vZCb1*p;Myz%?71QlET zE?Srx8Ux7LRk@~J?9S%0WwssU1HHKu>3p&AF}0)aMI)=UwL`GOlxjK>8Q6=JxdJiI zwzAzj0cA79t?gY#5-b@DP7rpqOv%j{kZBAy_>*qQW2rkegJUNK|X8B|+^2Nwcbvo&f zX0*uWcwr_%uIakr?Sv^$T9|y(1NrwY4qgHg88#OOotTZ4Z)p0!W85x-Y z{c-|;{$NA9H5~Nsx+<=Y``nMDJdX>+LZz5&rbn+8O4u7A@erZuE!9Y;HeSPFjaQA`10N${KB9&Z#Nc2eXFi}V`k~Gm>YSdMDdFN z#CCL?0s-_SGwXxJHyX#i5FG(iI<%U_F(&R>jiS^<=r7No4o zgr8Vi;$rd3Et+KK;G8Nnf{FNSkvH{h>Ok-rDjI=}M%Ex?HuLC0j zizrq)cBRr<<3cfi3zY3%uH%W>cG)Ms&MXCjSJ)8= zM4OxT?(8@nOyIAr;x(50!-~%;G4Un>oatJiip3*^-9_CU=x*F{ZW~6F4p0_Sgs8!j zBFuecVQEOAJVgtK2(Yj6f%m6M@|A~zL^xI)NvzQKy2pHP+e&8f`PD{u7yd& zj6B1#eH8O9=t!Qex77v(I2isuL}Vw(Yt zN25@L#WaYogEDKY7zvI-QW!SPXiA}|N>lKZgnI?1S~TG%gEcAyaG0DhQ;BEOfO`7+ zii-dJHk<|unqnOucu%`JIkJm6ea%+GnR29dWQ2gFq@PP_AXit9750&?^2BAU*}y+r75g&s@_EteQcF#YO? zI`c41MP&x)07EFzyJA9NXi>l(&{B$ik@oYGRG-2WpFmq>wHRhLfyWACLLVBS+VcGr z-Yd&OZLS8W$vuVIp8`9{t)f7|CCLsD2a<*%h#P>Dj{G=2v10^o+|go=j1?lUa&^jy z2WavT-c^6lT~p0H33!*_jtqF;rY|b@z>6p!{FoIEi4ZXD;6iVpMzHDBl&*s^Kmem~gCUxAFJTpFw0U#tGR8lgG2heZ_6XQhB3*Zs*p*zI6BJ;HpvqF_}HEQSRL z)sJsNYXoQeBqAB_pmPwY2v5wH)06%yb{|IrZ`)fUBp9%a<3 zQE?pN|G%+S{a|utDq(xLDv(}NES*-u?yH|mL2yiZ@Eue0>zQQ`g`3+o6H*_3LSTja z$VvS3QU5GrVnlX>;xc8#4ui|al!Dcjz(J8NI$x1#c3|JcD9xaP&viT=z?3LP7IL3c zi^c!A4AnSNw@qy88^;h~(hh7w5XqYMr^4oyM=V5L#|+vO-2$LkcbDms!}AJKcj&;o z3eVxDh;vOZ$oh+APuvDez!L$41kBxu%+#Zc5Zk=N2Hr0ic`Xs-2xqYh=nRz*V&FhE z0MRE%nO8LPWF_1H=lbHT2FVXUm~>5v)@&>+>sOjG5XFSbl|nT1@fp`rq?3@?^IjBo zkufr*sEhxNY$WEJ3F~E2^RyeJ&(epG0TIk#oU}t)qYpG-VTv@s;~+MImza&lgJUMW zI&3HBil!pgQ|!Jg4b`UUOIr$A>HsbC8QviOBrl0&rIP_!Q^y{Zlmc5(JvP4R8hwIf!rhE-zdg|yvt3ZR}7D2kE*}gxA}kZ8cYi8qgFQNQB~9 zAFFwhZii`ngT=B2R8)m7?H>Ce(+(m8!PaiEFeQ~y-W}n13M9SJI(gXZQVwwM(FU-U z0q#+?1&#-2)NQfzQ@uHan{{nDE1n1)dxL9O`MHQ};n$4Agl7q_SBNld@iwPo?%?NG6NX-Ll%{BzS_wFwnyghuiDqj%jHOOFRP?6prFB7kb!$Ut1_p@jS zd_C_l|HE_A?owD04%ik{#Gm|-l{O^UA&ayfI#42299wWP$~zOA)$IwbwB4PIW~sJX z7xF!}lLKU?x5147^fx!&xON_iDTXs2?f@=ht`i0rh7FQ-PbBg2bh%@2v7{GNfI*Dd zfi(g*1PI(sJLw==($xgcu*DDhu`|LbLF!2_7YkOIzGb`j0R~d zX~?yxp}dhWv)<9LDQ%EBz;N*-pq2W~+8YYh@^RhxOff)>RtNvMV{BAXmIXOaLcIdf zdWhySXjehMP3TlmE6l#nS*88IFy+4fI~?eo>do-*!_io@4{=B%M|X}-@DcCblv@a% zOGOux;6kxjHNMy+{c{Z)Rtg-8(e2c2t-8#(TF=;Exx6u3%l#%)xLZGHBZ0)bQ&( z$Tr@|p)tjjh2NEU`I@dJL+kkrVIbb}%%MQF8bPZf%?Jop?`xBq@_<`|3-yJbSq?nC z(uFjpc(Bt&Wg1CeM5tTUi+5Nu+8}^d#wA}f$nGFc=G+8tw32t_$zxrCy& z+&9XKcVNX5KebgMNgJoTWhi~zSzorG?_noHY!_`-_ia=wRQO7@xi%6jhpwC;Jkj4N zV66nJy};@7U6Dz4hnPTA!y%YgU{R?OIJyJ1X0T{PZ}3*_5I>$L)DnJU(3q%#jt5*5 zEEstN$d(PhdlM`fDNY0&g4+zAU&!B{mBsECDvMRR$oIM{g5=(!=m^VbKY+C&$-UgV zWSU^*$c_UIH2u&n8=|UM0ZpA}Bn~Z;hF#Hl9@KUCxx5=n)w<|Mn@Tn&Ykk4}K#Q4_ z^-fZ+r-@gJec_G)UJV57H-|e(4wY%2&M#Lw7uXvlh-PHb3y4T5SwyO^_FA8)oD7s7 zA0MebVRopa*dSn25)(wg&!oyxGp?9W`|TT0WkkWY$aD#}d)q#p7c> zeoDH(r;xRvlRY?4&_p(th)0(#U4o|Fda6gWWy@;yQRBa@z_d7qIA`vJH}wi4+9b=p z{`qZq{VeNb2RwUwb|^?UbH_Wv{LY}99hX7CA5e5Tsk-@mI5rRhQ0(Ln zoR-v6E}^)Wy2;|_Ild&|&A71!09RMd#25!Oa?M)uv~1S*2eFJ5Z7NP$!-Z|BZ$0;{ zs|P{mEtwacUpVL)OxfY_mn*;(sS6JNt{mssJY5V8CL&F>h^U5=>ryBTpRCc6sERU$ zvI?dJ%rQqx%cLCNq8>&EwW800KnM($faW9Yit3S~7Fa|H7Cny(5z0dHcuKW3 z51FVwhg?cRuzXY2+)?jU2~b5FR})F(ZK4Il4%l#C>v^$Zr;&L;n^54 zdNy+rLN`z>8Y=%zd4b3RRG3AvYm<5wfuK~K8kMqh-hdu_tdXQ3>fV4CL@F4 zQ9myxfs=FJ$LLx2tQZZ50&rKc=Md0fGl}aF;Z^F?%Wg1$!GdCW86^QlWsPcKjTK|S zNK1JkWEq4xLlxS%8Bao*r2NvLunr{BpqTM+Jr33dW6SF}Lzp0Cn;9)_n$4RMg*D|+ zoT3~}E*;mm!kPzXT(W-sdda1=W>7K&2>9nHRCSfGzV82Ww=xLHX)m|!^hE=sG=B3v zzl?&1S|r^n_g(IG*nxehYoEcVS|U-@;*X-XKp+W&*U}dV#f#QIBJ}e2TOg+R?iMX+ z7z82q_8SYkco9tlGZM`q0~RU1ojs<6`dp*=(Omd~TOAEjS8vC_4q~;vskmoxbN_uz zcLOihA_NXn>0&7gX#u2izG<-22SO-FOE{vJ-86<#qq1R4VkSIT_!m!>v$zMv#tz*j)&x({ZA(9v#WA! z=)R}DEpigrke+8R2e}iuL;|)hCIfO$Q@zSGU*Xc6H?Pe}+2#gUHyWh!0fN)YBVCyr z?Ku`c`lBKaP9>?0j_}s{TzSy}t|RgqXWp!82~(4~ajz_~&wE@-OcY%YWrnwT}m_)~!H+N~5n1!)wpLp$INqbM;k$3}}h56xIS z&ul2ElLh3fRyl&o!B1C1jxoCY^kxHyp}^>>rAm5CwYUea+vzu`55~{;gF1Tnv=+D>bupg zC$Vi15sIM_K*c9aRhi-G;+O^Cjpvco1`Mi4N&cy>0A8vGMbODu<9o;o5)720L1@jv zqz@4s zu1{jY8=gW?>$KF+wS1e{ICi^^F)Hq3Gx$WoGFnhRkAU-i!52y# z9eR&nbwswURWRUozX*03i&_B&=7H>{BTW|q75HNOr^T`baH+zJYV%^VOU3WlIl^Bw zNQ(IcA{NJ)y-TieZk2`Z#V)Q~Q8~Q7|Ru!}Q{-*Ty8Ey_at*sMdy)r`; zwvl|Ppc2B^Q5h-+zqLA!-p|+I#ZH5O`lDn7> z*C0$2OUT!;#MXAXuMWk&bb1ud~GW|O= zJuQMGOCI1UrK?KdJ2#&t>w^Oj7;_ zn37f)sK9Y~5^vHkkR`Qqt{IzF1Ee6sA*LP)6gi02G1OygBr9rVbWb8Rx#Rb&p% z0^vcOYaEq19^VhNM7Y5g8uPO#-U+PK8#^F*AW{e(qQ`LKOOvKI1VqB@=&qOCkfpV} z2AK8}EbRKi>0i(g-g0&dN(FAiJsK+k7=)1i`w{UAo)GeR1{hPX=0A)&`m|swq*ek# zUOwvLygDz+wi@Of5clii{BoJORwA{gi&WbDT{7;?a0j;0@0)5@2}XjgMidAiwj-+j zvI^NJcsZ-^CKBefS4Tt}(ETDE`{r%dFB68?Km*-E^Im4!pcZvxyg1q~9&*#IphP1n zq0muFNzD@sq{-h8mhYM_Tu$u+QtZVeHdIs~u0Luy4c?cu;^0V@WOR>P)=44r8$g>N>zB zJ-eadTgu%#FmO+@=Jv@fibqB8s_2`+L5QwA7)O#ttD}>Si}$o@;;V4QA|by(Nz?5T zk;6;^OkdZpBo;nkkcj#aXjTEeDMHrFnifcfmg(CW1OtWvFr`iJ_$GI|C_m$}jX49` zp#--KT!SoU<#UKR=md=5q~V;;lna-9Np(lMJTL->vsNO(jcqVxTRbJTtv}X^ivMMR zgqGnuV~_D|+l7PIY0)o;7~hL4C|AQE(QoLfA^Vw2N{lJOP7bgx8biGY54KGGZs;DQ znMFc|7{g#bZLZW_G#Le>Vmc&C$PprNEm1PDi8M?#O#}3}68cj_Nr}g&l7!KvB{D##~$7dU=jV zWP{M~>Q3)59xdzNSWdIN_M2h#D8YOhTx36$oiN?IA70+>0ciqt6s z0!lzOl>p_kf~9CeMzs&YL9ny+$vlkf@B)}u?n3XBa{5-o4vvftqo74)%%JZI2tB;g zJK6w#B}`4K0qgjQgF~$!^B*IE=RswqbY@@tlt3U2c0Z5C&cEd7VqL>Alx82hN;TDN zR1HY11`^^*_mLSNl6X@$$D)@5*y>3suH>yal~QZy4kb+r!A*Bs(1|)iOK$lTqkkYj z%~mW$Pti(68i$}lk&fSqjY0O`ZL%OS(%4D13GF-c{Wnfi67PwGte}BtWxfc|&dKgp ztFqYu)#_H#WnG+b%9}EK+@=sH_{W&toCq*z5xSB)wz$6y5o5kRy% z3F0S>i=mUqo-iL1&HWHn?4m%X*SMt1Z2*f#lPUY)Ts&PDq82INisCUK27Xo$;Q(mL zlofXto}ZEzlg-o%ZdW5c(HzlHsPkF`>n@SbIOK&%64+sZl@jBl4$1d*A}pX1Z82$u zqVzBZhr;9oWjiZkRT`!yb9bv&-p2ig zbhMo_9|xFr3<&&>`L5O^TPL9CPZ5mv%h*bkhBK-T}>r%v2As|G+Egn6F+P$MmV zN)Se9E>!Cm{~dhGWbqmJQ7HBnE(D2w&Y7!nqCPWQvCvr&vOCUiziknqj;vjp%nO9; z#818cp!SQu<@~#l&Oe+dPk|#z?pBU;R>l?c@TjxsC7gPmt zR*j1|fQgjuOb)SCXvI!R`CjT}5(ZZayOU}|1g0Y9M&`$WFXvnY-SBr~%MLG&md($1QueMht(wnEx^tqU9!9a$@1QF@l+02&`;&{xyaF)IN zmBHl&xgEuXzyXz|#~v1nswlpu3Iwb}0~~_#|89zlIB(Pg!ll;ePt-xnfr#WV0e*e` zk6v++;{hS8rd6g~3dtuNNCb(xr%%8#PwcV7I2av(qX5JjB2cNNZW!l?1R7I+9}8pw zmL0Ua1Ld>Wj%%P}JcHW$EU$TTy%AVbsW&0ix_x@82WCl2e}xjXu%e3>!%0?pRE1Ds zr7W7uAsv*&0KEDAn8au?GGOf7;}T5^Ykyt}BS}7W_C?eEYV|jr`)3T6X@w-YT=JR% z{XkqbDhvi5;EWYL2!#Auj3mtLHxsT>iFILsKM6`P4W)Hhtk=42R*TvYx(W$jcwEa3 zxCmmk<`;=&L3(2J%!5}7Gz_()w;6K|Fxtt2u%wLTz$j;)NOKL&Fnlg1iT8ZHxj%7C9l)b>XvqN#83306QiJ|DfZ?e%9wIbW!=jW|{fFVWN$f2?1lG?E}bFP5^#aOOKO$7+a0>;o^Z z{`8Nrl`#$8Vpxn~@h(^*SdZ69JWsJ|N%%hcuu6R3{TJM*3D+5C>lb#N*-&ChI${-) zTC_p!bdxX(MPyKyfh414L8usjz=43x;z!HiiYBka$;Za@3@Q=v68I>D+u|6w2W&X~ zf#-+f2_iWO4uJGwcylxoY06Iv+jzJ}68Q$b+tCmEi$6w+bW1YU)l z3II{dz}MgJK-0w6VlrmX1;W139bSTw`+Rgk>sn4z6ik?R3f|H-Kg4v;wiUGy7Vu4DR5@MxB5TTK=aTB}Fg z6hq0gK>`9nvWQz9GfxOB5pn9YF)vQ2=4zM$^bZ&XmNJac$;zjau~jw|D|HveR8j$M z)E%_;SjVJI=Np}6r1O)Powu-i5eHJTI5FIuwYGf0s2h4bP^=CR0urtY@`IFUW9azf z2H3)yD<}l03qV~HDhkN>Mv0k--(o@K#p)zQhAx@kj>h}!8VG(z_CVjC((%6zW~6sw zK_DCg0W7BGzi0*^@|RAhESKwumg~`CKw07`oIRPSNs0P=!xC>Z1{D`SUnk{;7|3@W z-)B3NY6YBqqv7nXq?3QV`=3z|16IxE>B+*j0=#>H4EebPTqP<-@gZScLoEl96|2>R z;bC)YZ9^OnxJb-{>Hw00UH#uQL$4kYDt;M7Iez~?dt;1Q~ii*Y+y zIQr7u=}};-rp^^W(1Mrt#-_ZO6~nn}*bsD;Q{f;hnBFX?)@Xc!D!uE*r`o+3lsFx- zb(Rolm*3uho7|2EFT-nJHW>-eIR{J|gj&HuC^r(^6ESHJ)_18OqH=P2PwPnVMT*IG z`!fe*W%a6bd;#iXp-1(QRwYO;;V}sIRs@i=@d7eZdc4^jUC`1KYo7BN{5NNzhJQ_N zzig5OTj+Fh=`VXg;L>LhBwcede~utonJw|SQ|^b~OePfH#Dkg_@^KbM!TIS~4me}B z_BFYj`zBzo?VJx~a^>B#%)kp|g?NlW)j)Rzx{5{ouC#RAZkKjcTy~)5BFT z6*N%-hM0h-%SM9j1yE^5f@Gq6q0$ETZV}kEgCi`iP!DRl{SLM44S&KMpjNm}z`%eu zutAMaIYnTE4FJjHf|3_}-J^J`!Xa-0L$E58OhBP}!G2GW#07+a9flhL{b_&{JpzGaC9ic`8B<;M?Wc`I_AbfSsp^RfPn?!3g zhJ5?qQ$lXX(UL6GF$0+JfAb9o1I6Eu62cbaW`(Zc+TbK0QqUEpHfxxvA2;sAjxY!` zfJ?Qz*)`v%{A`XoqZ^4@fQ(f{V73chf`Y8G;}dY7c2Mrdv@>tn7R?{G+8Ba@3Kwvl z#ZifJ^SbA*aTT&^$lst!E|FKp%|YeIf5UI+=FhJ3H6Bn5=EJwN)QW}2a+~CuDVe&_p-`jiM5j7G8bAKq9Jn|p-v|2r_hWxHpj5#0+t}et(B2Lt-O@|u_TwTTcj6f>G%a&Zk9uvK6yrBw!aDVi$u?g!t+|kjG9(PUfbvq zN_pTGfe`5oGqkfg6Neg^syIQC`+Hhgr$k%pz>4ot9!+5-$%J zkh>mM==3gXj8xIL0xm3@Jz<5oEfRep78#Tvq&rOOhY;Mnz&nv9mj)K47VZ6D&su12 zbLOH2nUqwPL7(#5b(+SK^2a~~lMSmx=}u&3HMgqAtMxsf75CZe?$LHSRyPtqY%ii% z?n^CPi*#q2^ZE-(3K^)MP`ULRlOk`}xspP`|Bmj2hDS)p*z6v`0Zn0>_rhpfze`Fe z8kmd~XO0PA(8=<%I=U$o5l|H%B+d|RqL@&`pxQQ2;VM^P(4LGDOCRxFji0Om=v8d! z%4>o7C{kfUxR#i1J9v23&tC#Vcg7_tKr{QRxQDN3=KdYV$+|D~lMZ#;!RlCbP+sg$ zY?vO&VoNCP;)-Ys*Iwbk1?)&B&uJ4+hE)Gg2uP|FlvP}TL>fiLjJRT~cVA;{1zo`O z5DS$H~#^P94YZu$=8$Ksmucr>u;%@2qt$5Jm46sKq!_D2-Q=K-X9~| zm(u~L18Bq;!@^iwBDHG8c2+p;2fIyp!m%E3z_qO$h=g`nO#xnp5JPsoi*l0UP#DCp(Maz@;b+Ik-U&pVLn*@)=VnLaAK)`q*;p|V83WG#t=%|*wwAm=EQgj@hmbwzVXLOhl? zwV}h4$~7+U!4SnEgVPCz*uZxEYR@OO0;uUphCc^05zd_c7VI-3;TVjewHKbZso;8cuJC5C&1O_^>V}(3kC4esa#bw_>VKtnBC;Vh-T?Wq5;^l~QuZiP4vmjB%ZivKrYymn_nUHM(Vjj-CF@D&|*U&2cez?T_(OaekXE}YU`?%+=s?}BZ|Q&w6^V#(iIL{i(tlxJOXelXY+GF3k+6e zkiQ$Y%2BWc=J9)XprH{7VcZ!D3c?T|R8(9y!NTFJJ+|1Tm1xM3Sb7v=X_%1;bidCxivs~!WE|o1!w0#C*pQq5G1cjb z7>9oC>`9;y_OiMnaS-|@Xv|C)DaJ_MXY<9XMU_>m@ZY?|qLxMlt`hQ7hFQ^EvaYtR z7zNc{`5h&8RRz(ff-4=~7OLTI6L#RZ33Tq`-AQu$l$tX+6=q1Ii8zR&%NTYr)2ecE zw(dkMO!kpz!H^<}e+75$m~muO%42d~@7*yql~!L5#aOh8O*a@krd#affsAPCq9PG&AOWHJfS(@F4<1zC32<;6Na3`8kezkhIE-BJ7S zI_%=#5o~-I{{$!pv@~jjdzU%Bx$GU)i+vp53@_W>KDa>L*C! zJA?>`hE&+XoGLj`r2TNOGPDx~3y)$aEm3}O5MW=1*B-i21!n&pe*@ro$WRB{=mGI3 zksS~#`SA9E$f;>Jap#4rFHr78_P6YV7 z8fF%#R4Iq}5210H*8{T2SQu9ay*lGHJa|}@N^!sapP*PQX4-`k5?thT4I3!ij_(Z^ zxpFQ6B3{Wu8+4XO893O;7UUcki9G6)Cv?!t;)~(kf>=%uo5}C%j-_O z1cvvCb@B_yk&r88rkBq(Iu5Ogi^vxXMT2l2mUe;*!BlQiMB&Go9ssavD4-I*6b=a$3^1F;Qh+7+1slm@ zp;@D9H}yp2FMnPhnpKIiF=*ml=t)3w{0NUwB`%>&5e%3e4XEi>gG0Q@W?Xv!Z?Oh1 ztCpZlP8t9ay<6Fc_C}J`{HR9K3~H_f3cQr13b#WyAPzVZOk~1#Uf|61L zNZBNre~s@#NdP>OA>E&+i^+NGL*1GAz&Hw0kqv#dw5Nblq5Z$!GL*9ZsaCcu37gOe zRM?&BHqJ-VEn@CaEQL1GbhtIe0EdNoSU_VP#0TS=VFxf^Fqq>C7(vRnYLIhGbDDGi z;=p;a9DmKb8>^Xx44tAjq9@NUn{t3+G$G70GI2cO5CMBDBPT5?(Qy-i#A7=xPu_#s zuHYG`n04O4tX%8VA+O6tfZc?+$R!AS-)D$n(PtQj5)1<~nnOQ^=fi9J3dQvKwgLxl z-|tEgE!f9>`&_Nd-7Fgaw=IMxk~*H*p!SxQ&3CZRZBVN&NQI~s#Oy%zNMQ?|fHCZA zO~en3C_ky{8AQRbNGQt|me9Fb_d7xRJGEpuDg4gRzc801pxsjFw}2AuWw1SWXd_WV z40J}s!`;QnK{G;*RU0WOd8k|gcJe;W3V#JcpZ3GD@_%Wmgtt?&;Mx^3;sn*)fM`rD zmx`8yUAGuVkw&l~`pLQLVWkG&>z8f-;CI`A`~d87hpht&`)Sv}J;pvy8qLOau(57u z!Ys%%2^P=r>Ci9C0Ks)~BPKZude)b#>M|)^`Iw)_@E)Qe zcGsQou*qPC-_HX4C{)F272hD?J`HT_X?)u(3NT~+JAGdT>#dJ;S6)&3St0+qZK z?1Q|W5qXsr%%rQStxYtfF(?&T551)UllK=`pm*9!N&xlpfNeidCv6k_!;69y%fnBNfVY z`AtMUA!9v%%%GK3j2x417|_^5s5k7w$O$RHJ*#7~;Mo?B&@f{1rHf!Y0=)6HZ0wan z@w68oysCSEWNb#!8(Q9Ej2*Ku7VaC*qTj#TBGy-+F{+j%)ToAW2s=Y4p4uIEWmR#Q z-rgIf!_zm~%OdkqQ{`T%I%JyTE)1Ri_n zHd=lcVFJ?0K)mY1mIC{%LFGcFw2dD#|C8f~J*+;=?)jyn8yQ?i&V+50u=aF67NS`T zIsznMzh^;1CWXw%D;IB!GN4phx$yQ>Blb_R@u7IT6a3sr375{LPWAbJ*?sG3;zPP! zjbNZP;({iCATuaPQ4FV|wLB7t3Q8(;p^;F%HwR2TDw`q$qe7%XhJhtzWTujlvHpM? z!0p=g3D6@VM!u-=y%XX{d5t50b=hYfG3P+2=^QMNk7=v{9M1tkGNltSfuzzvcqJgweVNcOzU7zAYTv(7%(uli`z!#laBTiv&waQ)I_|n z5b7_8SRJqMJzt)$z%M+&NED$t)?im{bcD}Ps6MdC#2>ZOL?kt_M`y{^z!TZTLMs|q zO)S(y!MyG1H1nn?ost@h{B;k8(Ry9I#DQEMMk%=%4bDvmNk0zoEyqIZis3*gpryyG zI&QSOE(cFbmC>5S)A;Yoamnj)M@LNjj|$EKV1pf22!Ft%n{0j~og}Q4qGV*P$r#84 zq2jovuy*`Aj=&%&dt8ySW(naXT$%!4NpT_EjRFZoei7GDtD#HS{#7J7hVqHH12(GJrIUIUssa zbf{~{UcEpH8A36ioDdW=JR$IG%mVraatquQph`hvg9--< z4!9MNBY<2$xLV|0AK(~G?9bAeSkXvngG}j za1g*Qz-0jB0Fndz23QOr44^5#Z2YI@UzWau`0MZQlzzeZ-}B#)|I7CO@1K(X6a8<% zFZ2I>__6sn_FuOD4f%)A|KNTZ^Dhwkf5jfy`!(;Ut)4ghS$Nj=AHcs&ya@V(;0N2^ z3O)zCS^8u3N##4F??~Q?ogRATbd2cn)x)U=Y2M0RQu{S@oa#f_7jo{*{akzdmVg3= z9(q67Uhef*v;BVKe;s+D!ao39`{DzHP7pW}=l22J5Aw%^ZWFjU=C3aJWyfwt<-?3l zPB@>&97o`HV!fd^3*Iws?XGt3+UCl3zuX&l?dmqN*yC-DxJE84633)^>c*XdodkOq z!jVANf~PSmISj1k4=Ox`AP&oMh%q2~=rdbNg%J6`cP43cs10lo9t9*Qalp26P9?qR zHo%-fb_KEN(*g*B<7a$q6RJ8h_YfgPm+(*{djb%E@Ndcnxx#c|%b*npK?O6i?G5X= zVX~}1(G@?ASeAak(>Pk6xC=4QHCO7RIp>T96`F#$$f=luJ!^mHV8eN7atDjZ2J%h^ z1QbBCO-j6wSmfwpZU7$i=noJO0qjx-ho-I+L%=A&R4YwUfiy@wFZNH9V|f93X)s`D zMM9_HD$j5#e8goHw1pDa!R;|Lu#Xb0EHQVY^^p3>WK=fk-oA zAu}O&^p$)eMDn*-3Bqllu6T8Z0Ns*UmywW=(*6~C$|i^h;HCtwb6-mkmZ=V2`JIwT zb>Ko#Nkp0sfVV=yc0{0XMrRPymr5m*3(>5KARkRkDWLdXje|VXq&}}ba}Vv}S8Knb z0Kz<>PT!NaBk#tjyburpFUq(LoDnWIa1mMp?JPCpoWNQJ^{XL&EF(@qJaH7q((aJ1 zLWsOV62kC;x7!hwJpC^#;5dA>&7X8Y0T+WnuX%~XOC=f&WKK^9xHIrd8S1^9?g-eO z9v5*vZ95YKb!XsDSZYCjJ}RdO^N2}MGVorS7**dRIZ*4tw6-`Xlsr;GNL*>eoeLL< z1l$e1GGYA88(TX<)!PZ3n~lh;WyficA(MC<4GX3`pN>qSFl%h8;352i0WqzjwU|6X z7-1B>nK*udYLYBOS;sjnn@NsV3sI-A;{FO{?1oc0AYoE!(I0$P`kF{pkq$xw*=Xc? zRRPhbu+l{a*y@5ri%f(f6XOO<5@^i7;scS)!`n_sF@iV97q^%a2nDz!WdU$$&}F*1 zy8?0s-NMA5GrvM*-;P>Qr8CkFGuS%#TrA;+)o`G1P$Xzn zk0Q<<|FUREIp}gI&$4P7Lg-7qXuoGClCSZ@#kYMV3O@`&kE+Ku(7OXDM^v`B6McVA zT=Hl9lE-S}>$H1mEB_PnwvMz(ES{ z?gjuYoGGc2YVy$W41Uv8ix~OX6tSELl2oN%pLX#`>sY^_DfXZg=801~a3*}?HrEpy ztfdGkvpPz8=5Flq-O;1GOHD-=Gw9WZst*P;z4z@DxeI4eYS8!xl2}79^$HK7Bk-Fh z02nw=k@)N`9A54d!XX)xF>}^(h9L##*T~AsX4oG2159o0j8tYPV@-Mm;>WbT2IciF zP*@L8(Zm>pWP5|h{Y?2cc{htgnB5~( z^4gd<_z7cDZ|#-zN+HUqb3q0^9m9P+P^OAXMpu-oI~KtgQ#~ zd=q36FQ=yEB0x$#v8MlGA{xbR0=`yQAIhSBW{xcD?NqP$$F&q5erfT~f(pa{Drr&* zCn+U$V%lIRU7ayuWCOG2l9w+moT20~W((634 zmJr1-oRDM`209QDceG<`BqInbt0be8QrV>ll=U`0>WQh_D8MAJotFu%W0Lhk+1Ldb zY?uN^sOUn3XYP{?d05oj1ke;N1GreR{SQEwD%foqHTN(vj_$q)E_q+|k^dH&w14xe z%=S^LE{JCc-VD$ZQ5*<@si^RLL~-dTxU=E<=uk@iyI>x|OuTbcU(_|(rjotr6%cr1 zBmpstr;Nus`UDOzE_2}th;c=-Bwaz4KfC!_h(b{BLU|yOS^G|M4c1GTV=l|z962Hr zA+6#o$B89gdxR%K6dB~@wb+?~-N{N6-+js?a_joB8l^tu^ionWYhdDN%}DgOwkhTRO9IT$=sM^gcFW-L8)7-3ZDak^`CRx+;u1Z%+H zraIVI!8VjFlp%C}=e~kdF`(eitLgJoR1xtQsEK3e zYseq1j?IZ8MKqUr5PmkO76F`1YtlEk^@V z9!4@iAz>|J)fsd|0YsLO-sU)-DZ)+sNliOpU>wS{K`RikZBiqax=RM{kdFP}a-E<9 zdIGKE;ROY=xCH=%i--#V#3>+NC{B(1(1gE_ngeM8iC=^ktulJVXL1*_K`=-|W;g0h z)sXW6A_OOb2`oD>6#=6(SJ3|2WHwATn@p3K-(FMz^;csO2qnwuO6Gb$E$e32!1hIM zLv+l7lbeWK2>=2}9D40wgu& zeMKWcZZru{Giw&xt^0dOv~<{T`fWf1xB-*E6<7Ql{j$AcUN&%unDG;(Wl#xgmMyu` zHs?BJyf)tG+Pi~l>WTJ4Hj2nGl0Y`i#4%Q%zv3)-jZx-*s}@qO*dPvLL-G$T=5qR+gX)R8jP{K+~B6 zOXLg_6lpvGr0>R%DrN`J(ZOme9(Hb;qb0r`&bt^U4N*Ubv#tigv>O7zYs%5CYDTr> zwH+w~Y63oy9T@tM2R?o#3c}FvlXMNp3L*XmKUMEh7n+0wB=fWsbD^9j=KG%1VBlMw z08@CLOU-Sp28WrIXo1Z16Fkt#C$U^$feY1g?V7u22Z~R|kgFGEdq$vFCWVdf=d>A>1|oxT0s}m)tIhmsEw85nN#PCu;$`nGhP-Pn{kDQHF_dt^k$9O6(G-qI{nX5(1RY zB{KO$j8ANu020&$u@Xu!1fLBO0g_GxlJO*rLu`irAg0Hw+Qy~N^rFj!)O6;qA@Nsl z - - + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/lib/font-awesome/fonts/fontawesome-webfont.ttf b/public/lib/font-awesome/fonts/fontawesome-webfont.ttf index f221e50a2ef60738ba30932d834530cdfe55cb3e..35acda2fa1196aad98c2adf4378a7611dd713aa3 100644 GIT binary patch delta 20319 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
KR(t_1oO^m(QlD*QmV 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 SdSQM28?+M|D(2r_;{O0|uQ74} literal 90412 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%0Ded40&XED0GYFoIh+AV*?9!MR5pBW?X_8Bp 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 IH>08b4`|GD*#H0l diff --git a/public/lib/font-awesome/fonts/fontawesome-webfont.woff2 b/public/lib/font-awesome/fonts/fontawesome-webfont.woff2 index 7eb74fd127ee5eddf3b95fee6a20dc1684b0963b..4d13fc60404b91e398a37200c4a77b645cfd9586 100644 GIT binary patch literal 77160 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 literal 71896 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≠dR0E9ICQ{-tcR;@{R^I5P#1GUH!p=-{c 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 diff --git a/resources/views/layout/default.twig b/resources/views/layout/default.twig index a6f3f62355..71009c1e20 100644 --- a/resources/views/layout/default.twig +++ b/resources/views/layout/default.twig @@ -160,7 +160,7 @@ - + diff --git a/resources/views/layout/empty.twig b/resources/views/layout/empty.twig index 5d0caaf749..3ef09325e0 100644 --- a/resources/views/layout/empty.twig +++ b/resources/views/layout/empty.twig @@ -28,7 +28,7 @@ {% block content %}{% endblock %} - + {% if env('ANALYTICS_ID','') != '' %} diff --git a/resources/views/layout/guest.twig b/resources/views/layout/guest.twig index 94ac3316ea..a4ff2b0433 100644 --- a/resources/views/layout/guest.twig +++ b/resources/views/layout/guest.twig @@ -38,7 +38,7 @@ {% block content %}{% endblock %} - + From cf69333c6dbac32de554e382ac718a70ae35e032 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 8 Nov 2016 21:34:13 +0100 Subject: [PATCH 070/709] This fixes #395 --- app/Rules/Actions/SetDestinationAccount.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Rules/Actions/SetDestinationAccount.php b/app/Rules/Actions/SetDestinationAccount.php index 4f6b4d28d5..829c4928c4 100644 --- a/app/Rules/Actions/SetDestinationAccount.php +++ b/app/Rules/Actions/SetDestinationAccount.php @@ -123,7 +123,7 @@ class SetDestinationAccount implements ActionInterface */ private function findExpenseAccount() { - $account = $this->repository->findByName($this->action->action_value, [AccountType::REVENUE]); + $account = $this->repository->findByName($this->action->action_value, [AccountType::EXPENSE]); if (is_null($account->id)) { // create new revenue account with this name: $data = [ From f0e0cdb49bcf459f7dfbecc3da0eaf03b0b17f2e Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 9 Nov 2016 11:04:14 +0100 Subject: [PATCH 071/709] New website --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a62386c40a..faf06b33f4 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ ## Installation -To install Firefly III, you'll need a web server (preferrably on Linux) and access to the command line. Then, please read the [installation guide](https://jc5.github.io/firefly-iii/installation-guide/). +To install Firefly III, you'll need a web server (preferrably on Linux) and access to the command line. Then, please read the [installation guide](https://firefly-iii.github.io/installation-guide/). ## More about Firefly III @@ -25,6 +25,6 @@ Firefly works on the principle that if you know where you're money is going, you - Firefly has lots of features without becoming fancy or bloated. - If you feel you're missing something you can just ask me and I'll add it! -Firefly is pretty awesome. [You can read more about Firefly III, and its features, on the Github Pages](https://jc5.github.io/firefly-iii/). +Firefly is pretty awesome. [You can read more about Firefly III, and its features, on the Github Pages](https://firefly-iii.github.io/). -If you want to contact me, please open an issue or [email me](mailto:thegrumpydictator@gmail.com). \ No newline at end of file +If you want to contact me, please open an issue or [email me](mailto:thegrumpydictator@gmail.com). From fe5764834900e202c437e1458c6bae318d9bf5d6 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 9 Nov 2016 19:25:09 +0100 Subject: [PATCH 072/709] Allow report options to be pulled using AJAX. Ajax is cool. --- app/Http/Controllers/ReportController.php | 26 +++++++++++++++++++ public/js/ff/reports/index.js | 15 +++++++++++ resources/views/reports/index.twig | 6 ++--- .../views/reports/options/no-options.twig | 3 +++ routes/web.php | 1 + 5 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 resources/views/reports/options/no-options.twig diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index a92648b398..b01e4fcbaa 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -23,6 +23,7 @@ use FireflyIII\Models\Transaction; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use Illuminate\Support\Collection; use Preferences; +use Response; use Session; use Steam; use View; @@ -85,6 +86,23 @@ class ReportController extends Controller return view('reports.index', compact('months', 'accounts', 'start', 'accountList', 'customFiscalYear')); } + /** + * @param string $reportType + * + * @return mixed + */ + public function options(string $reportType) + { + $result = false; + switch ($reportType) { + default: + $result = $this->noReportOptions(); + break; + } + + return Response::json($result); + } + /** * @param string $reportType * @param Carbon $start @@ -292,4 +310,12 @@ class ReportController extends Controller ) ); } + + /** + * @return array + */ + private function noReportOptions(): array + { + return ['html' => view('reports.options.no-options')->render()]; + } } diff --git a/public/js/ff/reports/index.js b/public/js/ff/reports/index.js index 8abb613f25..6edeccac2a 100644 --- a/public/js/ff/reports/index.js +++ b/public/js/ff/reports/index.js @@ -44,9 +44,24 @@ $(function () { $('.date-select').on('click', preSelectDate); $('#report-form').on('submit', catchSubmit); + $('select[name="report_type"]').on('change', getReportOptions); + getReportOptions(); }); +function getReportOptions() { + "use strict"; + var reportType = $('select[name="report_type"]').val(); + $('#extra-options').empty(); + $('#extra-options').addClass('loading'); + console.log('Changed report type to ' + reportType); + $.getJSON('reports/options/' + reportType, function(data) { + $('#extra-options').removeClass('loading').html(data.html); + }).fail(function(){ + $('#extra-options').removeClass('loading').addClass('error'); + }); +} + function catchSubmit() { "use strict"; // default;20141201;20141231;4;5 diff --git a/resources/views/reports/index.twig b/resources/views/reports/index.twig index 0531cd5775..03ded3a6ab 100644 --- a/resources/views/reports/index.twig +++ b/resources/views/reports/index.twig @@ -26,6 +26,7 @@ @@ -86,10 +87,7 @@

{{ 'reports_extra_options'|_ }}

-
-

- {{ 'report_has_no_extra_options'|_ }} -

+
diff --git a/resources/views/reports/options/no-options.twig b/resources/views/reports/options/no-options.twig new file mode 100644 index 0000000000..c5fbcb501f --- /dev/null +++ b/resources/views/reports/options/no-options.twig @@ -0,0 +1,3 @@ +

+ {{ 'report_has_no_extra_options'|_ }} +

\ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 32cda245ba..f4d9dcbd7e 100755 --- a/routes/web.php +++ b/routes/web.php @@ -308,6 +308,7 @@ Route::group( */ Route::get('/reports', ['uses' => 'ReportController@index', 'as' => 'reports.index']); Route::get('/reports/report/{reportType}/{start_date}/{end_date}/{accountList}', ['uses' => 'ReportController@report', 'as' => 'reports.report']); + Route::get('/reports/options/{reportType}', ['uses' => 'ReportController@options', 'as' => 'reports.options']); /** * Report AJAX data Controller: From 3600e1b5e7d2b093b4bab820ddeee6dfcffafda2 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 9 Nov 2016 21:36:54 +0100 Subject: [PATCH 073/709] Extend report capability for issue #396 and related report issues. --- .../Report/Audit/MonthReportGenerator.php | 137 ++++++++ .../Report/Audit/MultiYearReportGenerator.php | 27 ++ .../Report/Audit/YearReportGenerator.php | 28 ++ .../Report/ReportGeneratorFactory.php | 58 ++++ .../Report/ReportGeneratorInterface.php | 53 +++ .../Report/Standard/MonthReportGenerator.php | 90 +++++ .../Standard/MultiYearReportGenerator.php | 87 +++++ .../Report/Standard/YearReportGenerator.php | 87 +++++ app/Http/Controllers/ReportController.php | 314 +++++++----------- app/Http/Requests/ReportFormRequest.php | 128 +++++++ public/js/ff/reports/index.js | 21 +- resources/views/reports/index.twig | 35 +- resources/views/reports/options/category.twig | 8 + routes/web.php | 16 +- 14 files changed, 866 insertions(+), 223 deletions(-) create mode 100644 app/Generator/Report/Audit/MonthReportGenerator.php create mode 100644 app/Generator/Report/Audit/MultiYearReportGenerator.php create mode 100644 app/Generator/Report/Audit/YearReportGenerator.php create mode 100644 app/Generator/Report/ReportGeneratorFactory.php create mode 100644 app/Generator/Report/ReportGeneratorInterface.php create mode 100644 app/Generator/Report/Standard/MonthReportGenerator.php create mode 100644 app/Generator/Report/Standard/MultiYearReportGenerator.php create mode 100644 app/Generator/Report/Standard/YearReportGenerator.php create mode 100644 app/Http/Requests/ReportFormRequest.php create mode 100644 resources/views/reports/options/category.twig diff --git a/app/Generator/Report/Audit/MonthReportGenerator.php b/app/Generator/Report/Audit/MonthReportGenerator.php new file mode 100644 index 0000000000..27398a2fab --- /dev/null +++ b/app/Generator/Report/Audit/MonthReportGenerator.php @@ -0,0 +1,137 @@ +start; + $dayBefore->subDay(); + /** @var Account $account */ + foreach ($this->accounts as $account) { + // balance the day before: + $id = $account->id; + $dayBeforeBalance = Steam::balance($account, $dayBefore); + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts(new Collection([$account]))->setRange($this->start, $this->end); + $journals = $collector->getJournals(); + $journals = $journals->reverse(); + $startBalance = $dayBeforeBalance; + + + /** @var Transaction $journal */ + foreach ($journals as $transaction) { + $transaction->before = $startBalance; + $transactionAmount = $transaction->transaction_amount; + $newBalance = bcadd($startBalance, $transactionAmount); + $transaction->after = $newBalance; + $startBalance = $newBalance; + } + + /* + * Reverse set again. + */ + $auditData[$id]['journals'] = $journals->reverse(); + $auditData[$id]['exists'] = $journals->count() > 0; + $auditData[$id]['end'] = $this->end->formatLocalized(strval(trans('config.month_and_day'))); + $auditData[$id]['endBalance'] = Steam::balance($account, $this->end); + $auditData[$id]['dayBefore'] = $dayBefore->formatLocalized(strval(trans('config.month_and_day'))); + $auditData[$id]['dayBeforeBalance'] = $dayBeforeBalance; + } + + $reportType = 'audit'; + $accountIds = join(',', $this->accounts->pluck('id')->toArray()); + + $hideable = ['buttons', 'icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', + 'interest_date', 'book_date', 'process_date', + // three new optional fields. + 'due_date', 'payment_date', 'invoice_date', + 'from', 'to', 'budget', 'category', 'bill', + // more new optional fields + 'internal_reference', 'notes', + + 'create_date', 'update_date', + ]; + $defaultShow = ['icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', 'to']; + + return view('reports.audit.report', compact('reportType', 'accountIds', 'auditData', 'hideable', 'defaultShow')) + ->with('start', $this->start)->with('end', $this->end)->with('accounts', $this->accounts) + ->render(); + + } + + /** + * @param Collection $accounts + * + * @return ReportGeneratorInterface + */ + public function setAccounts(Collection $accounts): ReportGeneratorInterface + { + $this->accounts = $accounts; + + 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; + } +} \ No newline at end of file diff --git a/app/Generator/Report/Audit/MultiYearReportGenerator.php b/app/Generator/Report/Audit/MultiYearReportGenerator.php new file mode 100644 index 0000000000..6d9e2857b8 --- /dev/null +++ b/app/Generator/Report/Audit/MultiYearReportGenerator.php @@ -0,0 +1,27 @@ +diffInMonths($end) > 12) { + $period = 'MultiYear'; + } + // more than two months date difference means year report. + if ($start->diffInMonths($end) > 1) { + $period = 'Year'; + } + + $class = sprintf('FireflyIII\Generator\Report\%s\%sReportGenerator', $type, $period); + if (class_exists($class)) { + /** @var ReportGeneratorInterface $obj */ + $obj = new $class; + $obj->setStartDate($start); + $obj->setEndDate($end); + + return $obj; + } + throw new FireflyException(sprintf('Class "%s" does not exist.', $class)); + } +} \ No newline at end of file diff --git a/app/Generator/Report/ReportGeneratorInterface.php b/app/Generator/Report/ReportGeneratorInterface.php new file mode 100644 index 0000000000..3d1770700e --- /dev/null +++ b/app/Generator/Report/ReportGeneratorInterface.php @@ -0,0 +1,53 @@ +getBillReport($this->start, $this->end, $this->accounts); + + // and some id's, joined: + $accountIds = join(',', $this->accounts->pluck('id')->toArray()); + $reportType = 'default'; + + // continue! + return view( + 'reports.default.month', + compact('bills', 'accountIds', 'reportType') + )->with('start', $this->start)->with('end', $this->end)->render(); + } + + /** + * @param Collection $accounts + * + * @return ReportGeneratorInterface + */ + public function setAccounts(Collection $accounts): ReportGeneratorInterface + { + $this->accounts = $accounts; + + 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; + } +} \ No newline at end of file diff --git a/app/Generator/Report/Standard/MultiYearReportGenerator.php b/app/Generator/Report/Standard/MultiYearReportGenerator.php new file mode 100644 index 0000000000..e3ab423b7d --- /dev/null +++ b/app/Generator/Report/Standard/MultiYearReportGenerator.php @@ -0,0 +1,87 @@ +accounts->pluck('id')->toArray()); + $reportType = 'default'; + + // continue! + return view( + 'reports.default.multi-year', + compact('accountIds', 'reportType') + )->with('start', $this->start)->with('end', $this->end)->render(); + } + + /** + * @param Collection $accounts + * + * @return ReportGeneratorInterface + */ + public function setAccounts(Collection $accounts): ReportGeneratorInterface + { + $this->accounts = $accounts; + + 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; + } +} \ No newline at end of file diff --git a/app/Generator/Report/Standard/YearReportGenerator.php b/app/Generator/Report/Standard/YearReportGenerator.php new file mode 100644 index 0000000000..265d79e9fe --- /dev/null +++ b/app/Generator/Report/Standard/YearReportGenerator.php @@ -0,0 +1,87 @@ +accounts->pluck('id')->toArray()); + $reportType = 'default'; + + // continue! + return view( + 'reports.default.year', + compact('accountIds', 'reportType') + )->with('start', $this->start)->with('end', $this->end)->render(); + } + + /** + * @param Collection $accounts + * + * @return ReportGeneratorInterface + */ + public function setAccounts(Collection $accounts): ReportGeneratorInterface + { + $this->accounts = $accounts; + + 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; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index b01e4fcbaa..2a24548513 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -15,12 +15,18 @@ namespace FireflyIII\Http\Controllers; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Generator\Report\ReportGeneratorFactory; +use FireflyIII\Generator\Report\Standard\MonthReportGenerator; +use FireflyIII\Generator\Report\StandardReportGenerator; use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Helpers\Report\ReportHelperInterface; +use FireflyIII\Http\Requests\ReportFormRequest; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use FireflyIII\Models\Transaction; use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use Illuminate\Http\RedirectResponse; use Illuminate\Support\Collection; use Preferences; use Response; @@ -59,6 +65,84 @@ class ReportController extends Controller } + /** + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return string + * @throws FireflyException + */ + public function auditReport(Carbon $start, Carbon $end, Collection $accounts) + { + // throw an error if necessary. + if ($end < $start) { + throw new FireflyException('End date cannot be before start date, silly!'); + } + + // lower threshold + if ($start < session('first')) { + $start = session('first'); + } + + View::share( + 'subTitle', trans( + 'firefly.report_audit', + [ + 'start' => $start->formatLocalized($this->monthFormat), + 'end' => $end->formatLocalized($this->monthFormat), + ] + ) + ); + View::share('subTitleIcon', 'fa-calendar'); + + $generator = ReportGeneratorFactory::reportGenerator('Audit', $start, $end); + $generator->setAccounts($accounts); + $result = $generator->generate(); + + return $result; + + } + + /** + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return string + * @throws FireflyException + */ + public function defaultReport(Carbon $start, Carbon $end, Collection $accounts) + { + // throw an error if necessary. + if ($end < $start) { + throw new FireflyException('End date cannot be before start date, silly!'); + } + + // lower threshold + if ($start < session('first')) { + $start = session('first'); + } + + View::share( + 'subTitle', trans( + 'firefly.report_default', + [ + 'start' => $start->formatLocalized($this->monthFormat), + 'end' => $end->formatLocalized($this->monthFormat), + ] + ) + ); + View::share('subTitleIcon', 'fa-calendar'); + + $generator = ReportGeneratorFactory::reportGenerator('Standard', $start, $end); + $generator->setAccounts($accounts); + $result = $generator->generate(); + + return $result; + + } + /** * @param AccountRepositoryInterface $repository * @@ -93,28 +177,34 @@ class ReportController extends Controller */ public function options(string $reportType) { - $result = false; + $result = ''; switch ($reportType) { default: $result = $this->noReportOptions(); break; + case 'category': + $result = $this->categoryReportOptions(); + break; } - return Response::json($result); + return Response::json(['html' => $result]); } /** - * @param string $reportType - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts + * @param ReportFormRequest $request * - * @return View + * @return RedirectResponse * @throws FireflyException */ - public function report(string $reportType, Carbon $start, Carbon $end, Collection $accounts) + public function postIndex(ReportFormRequest $request): RedirectResponse { - // throw an error if necessary. + // report type: + $reportType = $request->get('report_type'); + $start = $request->getStartDate()->format('Ymd'); + $end = $request->getEndDate()->format('Ymd'); + $accounts = join(',', $request->getAccountList()->pluck('id')->toArray()); + $categories = join(',', $request->getCategoryList()->pluck('id')->toArray()); + if ($end < $start) { throw new FireflyException('End date cannot be before start date, silly!'); } @@ -124,198 +214,42 @@ class ReportController extends Controller $start = session('first'); } - View::share( - 'subTitle', trans( - 'firefly.report_' . $reportType, - [ - 'start' => $start->formatLocalized($this->monthFormat), - 'end' => $end->formatLocalized($this->monthFormat), - ] - ) - ); - View::share('subTitleIcon', 'fa-calendar'); - switch ($reportType) { default: - throw new FireflyException('Unfortunately, reports of the type "' . e($reportType) . '" are not available at this time.'); + throw new FireflyException(sprintf('Firefly does not support the "%s"-report yet.', $reportType)); + case 'category': + $uri = route('reports.report.category', [$start, $end, $accounts, $categories]); + break; case 'default': - - // more than one year date difference means year report. - if ($start->diffInMonths($end) > 12) { - return $this->defaultMultiYear($reportType, $start, $end, $accounts); - } - // more than two months date difference means year report. - if ($start->diffInMonths($end) > 1) { - return $this->defaultYear($reportType, $start, $end, $accounts); - } - - // otherwise default - return $this->defaultMonth($reportType, $start, $end, $accounts); + $uri = route('reports.report.default', [$start, $end, $accounts]); + break; case 'audit': - // always default - return $this->auditReport($start, $end, $accounts); + $uri = route('reports.report.audit', [$start, $end, $accounts]); + break; } + return redirect($uri); + } + + /** + * @return string + */ + private function categoryReportOptions(): string + { + /** @var CategoryRepositoryInterface $repository */ + $repository = app(CategoryRepositoryInterface::class); + $categories = $repository->getCategories(); + $result = view('reports.options.category', compact('categories'))->render(); + + return $result; } /** - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return View + * @return string */ - private function auditReport(Carbon $start, Carbon $end, Collection $accounts) + private function noReportOptions(): string { - $auditData = []; - $dayBefore = clone $start; - $dayBefore->subDay(); - /** @var Account $account */ - foreach ($accounts as $account) { - // balance the day before: - $id = $account->id; - $dayBeforeBalance = Steam::balance($account, $dayBefore); - $collector = new JournalCollector(auth()->user()); - $collector->setAccounts(new Collection([$account]))->setRange($start, $end); - $journals = $collector->getJournals(); - $journals = $journals->reverse(); - $startBalance = $dayBeforeBalance; - - - /** @var Transaction $journal */ - foreach ($journals as $transaction) { - $transaction->before = $startBalance; - $transactionAmount = $transaction->transaction_amount; - $newBalance = bcadd($startBalance, $transactionAmount); - $transaction->after = $newBalance; - $startBalance = $newBalance; - } - - /* - * Reverse set again. - */ - $auditData[$id]['journals'] = $journals->reverse(); - $auditData[$id]['exists'] = $journals->count() > 0; - $auditData[$id]['end'] = $end->formatLocalized(strval(trans('config.month_and_day'))); - $auditData[$id]['endBalance'] = Steam::balance($account, $end); - $auditData[$id]['dayBefore'] = $dayBefore->formatLocalized(strval(trans('config.month_and_day'))); - $auditData[$id]['dayBeforeBalance'] = $dayBeforeBalance; - } - - $reportType = 'audit'; - $accountIds = join(',', $accounts->pluck('id')->toArray()); - - $hideable = ['buttons', 'icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', - 'interest_date', 'book_date', 'process_date', - // three new optional fields. - 'due_date', 'payment_date', 'invoice_date', - 'from', 'to', 'budget', 'category', 'bill', - // more new optional fields - 'internal_reference', 'notes', - - 'create_date', 'update_date', - ]; - $defaultShow = ['icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', 'to']; - - return view('reports.audit.report', compact('start', 'end', 'reportType', 'accountIds', 'accounts', 'auditData', 'hideable', 'defaultShow')); - } - - /** - * @param $reportType - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return View - */ - private function defaultMonth(string $reportType, Carbon $start, Carbon $end, Collection $accounts) - { - $bills = $this->helper->getBillReport($start, $end, $accounts); - $tags = $this->helper->tagReport($start, $end, $accounts); - - // and some id's, joined: - $accountIds = join(',', $accounts->pluck('id')->toArray()); - - // continue! - return view( - 'reports.default.month', - compact( - 'start', 'end', - 'tags', - 'bills', - 'accountIds', - 'reportType' - ) - ); - } - - /** - * @param $reportType - * @param $start - * @param $end - * @param $accounts - * - * @return View - */ - private function defaultMultiYear(string $reportType, Carbon $start, Carbon $end, Collection $accounts) - { - // need all budgets - // need all years. - - - // and some id's, joined: - $accountIds = []; - /** @var Account $account */ - foreach ($accounts as $account) { - $accountIds[] = $account->id; - } - $accountIds = join(',', $accountIds); - - return view( - 'reports.default.multi-year', - compact( - 'accounts', 'start', 'end', 'accountIds', 'reportType' - ) - ); - } - - /** - * @param $reportType - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return View - */ - private function defaultYear(string $reportType, Carbon $start, Carbon $end, Collection $accounts) - { - Session::flash('gaEventCategory', 'report'); - Session::flash('gaEventAction', 'year'); - Session::flash('gaEventLabel', $start->format('Y')); - - // and some id's, joined: - $accountIds = []; - /** @var Account $account */ - foreach ($accounts as $account) { - $accountIds[] = $account->id; - } - $accountIds = join(',', $accountIds); - - return view( - 'reports.default.year', - compact( - 'start', 'reportType', - 'accountIds', 'end' - ) - ); - } - - /** - * @return array - */ - private function noReportOptions(): array - { - return ['html' => view('reports.options.no-options')->render()]; + return view('reports.options.no-options')->render(); } } diff --git a/app/Http/Requests/ReportFormRequest.php b/app/Http/Requests/ReportFormRequest.php new file mode 100644 index 0000000000..6219cce4ea --- /dev/null +++ b/app/Http/Requests/ReportFormRequest.php @@ -0,0 +1,128 @@ +check(); + } + + /** + * @return Collection + */ + public function getAccountList():Collection + { + /** @var AccountRepositoryInterface $repository */ + $repository = app(AccountRepositoryInterface::class); + $set = $this->get('accounts'); + $collection = new Collection; + if (is_array($set)) { + foreach ($set as $accountId) { + $account = $repository->find(intval($accountId)); + if (!is_null($account->id)) { + $collection->push($account); + } + } + } + + return $collection; + } + + /** + * @return Collection + */ + public function getCategoryList(): Collection + { + /** @var CategoryRepositoryInterface $repository */ + $repository = app(CategoryRepositoryInterface::class); + $set = $this->get('category'); + $collection = new Collection; + if (is_array($set)) { + foreach ($set as $categoryId) { + $category = $repository->find(intval($categoryId)); + if (!is_null($category->id)) { + $collection->push($category); + } + } + } + + return $collection; + } + + public function getEndDate(): Carbon + { + $date = new Carbon; + $range = $this->get('daterange'); + $parts = explode(' - ', strval($range)); + if (count($parts) === 2) { + try { + $date = new Carbon($parts[1]); + } catch (Exception $e) { + throw new FireflyException(sprintf('"%s" is not a valid date range.', $range)); + } + } + + return $date; + } + + /** + * @return Carbon + * @throws FireflyException + */ + public function getStartDate(): Carbon + { + $date = new Carbon; + $range = $this->get('daterange'); + $parts = explode(' - ', strval($range)); + if (count($parts) === 2) { + try { + $date = new Carbon($parts[0]); + } catch (Exception $e) { + throw new FireflyException(sprintf('"%s" is not a valid date range.', $range)); + } + } + + return $date; + } + + /** + * @return array + */ + public function rules(): array + { + return [ + 'report_type' => 'in:audit,default,category', + ]; + } + +} diff --git a/public/js/ff/reports/index.js b/public/js/ff/reports/index.js index 6edeccac2a..5df1bd03cc 100644 --- a/public/js/ff/reports/index.js +++ b/public/js/ff/reports/index.js @@ -55,23 +55,17 @@ function getReportOptions() { $('#extra-options').empty(); $('#extra-options').addClass('loading'); console.log('Changed report type to ' + reportType); - $.getJSON('reports/options/' + reportType, function(data) { + $.getJSON('reports/options/' + reportType, function (data) { $('#extra-options').removeClass('loading').html(data.html); - }).fail(function(){ + }).fail(function () { $('#extra-options').removeClass('loading').addClass('error'); }); } function catchSubmit() { "use strict"; - // default;20141201;20141231;4;5 - // report name: - var url = '' + $('select[name="report_type"]').val() + '/'; - // date, processed: var picker = $('#inputDateRange').data('daterangepicker'); - url += moment(picker.startDate).format("YYYYMMDD") + '/'; - url += moment(picker.endDate).format("YYYYMMDD") + '/'; // all account ids: var count = 0; @@ -79,23 +73,24 @@ function catchSubmit() { $.each($('.account-checkbox'), function (i, v) { var c = $(v); if (c.prop('checked')) { - url += c.val() + ','; accounts.push(c.val()); count++; } }); + + // all category ids to come + + + // 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-start', moment(picker.startDate).format("YYYYMMDD"), 365); createCookie('report-end', moment(picker.endDate).format("YYYYMMDD"), 365); - - window.location.href = reportURL + "/" + url; } - //console.log(url); - return false; + return true; } function preSelectDate(e) { diff --git a/resources/views/reports/index.twig b/resources/views/reports/index.twig index 03ded3a6ab..886deb0477 100644 --- a/resources/views/reports/index.twig +++ b/resources/views/reports/index.twig @@ -7,7 +7,7 @@ {% block content %}
-
+
@@ -116,16 +116,16 @@

{{ 'quick_link_default_report'|_ }}

  • - {{ 'report_this_month_quick'|_ }}
  • - {% if customFiscalYear == 1 %}
  • - {% endif %}
  • - {{ 'quick_link_audit_report'|_ }} -

    {{ 'reports_can_bookmark'|_ }}

    @@ -202,8 +201,6 @@ {% block scripts %} diff --git a/resources/views/reports/options/category.twig b/resources/views/reports/options/category.twig new file mode 100644 index 0000000000..19ab935805 --- /dev/null +++ b/resources/views/reports/options/category.twig @@ -0,0 +1,8 @@ +

    + {{ 'select_category'|_ }} +

    +{% for category in categories %} + +{% endfor %} diff --git a/routes/web.php b/routes/web.php index f4d9dcbd7e..464999178f 100755 --- a/routes/web.php +++ b/routes/web.php @@ -307,7 +307,21 @@ Route::group( * Report Controller */ Route::get('/reports', ['uses' => 'ReportController@index', 'as' => 'reports.index']); - Route::get('/reports/report/{reportType}/{start_date}/{end_date}/{accountList}', ['uses' => 'ReportController@report', 'as' => 'reports.report']); + Route::post('/reports', ['uses' => 'ReportController@postIndex', 'as' => 'reports.index.post']); + + // default report: + Route::get('/reports/default/{start_date}/{end_date}/{accountList}', ['uses' => 'ReportController@defaultReport', 'as' => 'reports.report.default']); + + // audit report: + Route::get('/reports/audit/{start_date}/{end_date}/{accountList}', ['uses' => 'ReportController@auditReport', 'as' => 'reports.report.audit']); + + // category report: + Route::get( + '/reports/category/{start_date}/{end_date}/{accountList}/{categoryList}', + ['uses' => 'ReportController@categoryReport', 'as' => 'reports.report.category'] + ); + + Route::get('/reports/options/{reportType}', ['uses' => 'ReportController@options', 'as' => 'reports.options']); /** From 8583b574ac67afe9aed0114d913c8dd20bf59bf1 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 9 Nov 2016 21:46:32 +0100 Subject: [PATCH 074/709] Multi year was not visible. --- app/Generator/Report/ReportGeneratorFactory.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/Generator/Report/ReportGeneratorFactory.php b/app/Generator/Report/ReportGeneratorFactory.php index d2ae6f9809..f5c1d4ef1e 100644 --- a/app/Generator/Report/ReportGeneratorFactory.php +++ b/app/Generator/Report/ReportGeneratorFactory.php @@ -35,15 +35,17 @@ class ReportGeneratorFactory public static function reportGenerator(string $type, Carbon $start, Carbon $end): ReportGeneratorInterface { $period = 'Month'; - // more than one year date difference means multi year report. - if ($start->diffInMonths($end) > 12) { - $period = 'MultiYear'; - } // more than two months date difference means year report. if ($start->diffInMonths($end) > 1) { $period = 'Year'; } + // more than one year date difference means multi year report. + if ($start->diffInMonths($end) > 12) { + $period = 'MultiYear'; + } + + $class = sprintf('FireflyIII\Generator\Report\%s\%sReportGenerator', $type, $period); if (class_exists($class)) { /** @var ReportGeneratorInterface $obj */ From 5d4f1bc76d8e44dd2ab4ca323be433337759a13e Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 10 Nov 2016 06:23:21 +0100 Subject: [PATCH 075/709] First working example of category report. No content, just place holders. #396 --- .../Report/Audit/MonthReportGenerator.php | 9 ++ .../Report/Category/MonthReportGenerator.php | 99 ++++++++++++++++ .../Category/MultiYearReportGenerator.php | 27 +++++ .../Report/Category/YearReportGenerator.php | 28 +++++ .../Report/ReportGeneratorFactory.php | 2 +- .../Report/ReportGeneratorInterface.php | 7 ++ .../Report/Standard/MonthReportGenerator.php | 9 ++ .../Standard/MultiYearReportGenerator.php | 10 +- .../Report/Standard/YearReportGenerator.php | 10 +- app/Helpers/Report/ReportHelper.php | 84 +------------- app/Helpers/Report/ReportHelperInterface.php | 11 -- app/Http/Controllers/ReportController.php | 72 +++++++----- resources/lang/en_US/firefly.php | 2 + resources/views/reports/category/month.twig | 65 +++++++++++ resources/views/reports/index.twig | 106 ++++++------------ 15 files changed, 345 insertions(+), 196 deletions(-) create mode 100644 app/Generator/Report/Category/MonthReportGenerator.php create mode 100644 app/Generator/Report/Category/MultiYearReportGenerator.php create mode 100644 app/Generator/Report/Category/YearReportGenerator.php create mode 100644 resources/views/reports/category/month.twig diff --git a/app/Generator/Report/Audit/MonthReportGenerator.php b/app/Generator/Report/Audit/MonthReportGenerator.php index 27398a2fab..3ad211f952 100644 --- a/app/Generator/Report/Audit/MonthReportGenerator.php +++ b/app/Generator/Report/Audit/MonthReportGenerator.php @@ -111,6 +111,15 @@ class MonthReportGenerator implements ReportGeneratorInterface return $this; } + /** + * @param Collection $categories + * + * @return ReportGeneratorInterface + */ + public function setCategories(Collection $categories): ReportGeneratorInterface + { + } + /** * @param Carbon $date * diff --git a/app/Generator/Report/Category/MonthReportGenerator.php b/app/Generator/Report/Category/MonthReportGenerator.php new file mode 100644 index 0000000000..d2bd5ec3c2 --- /dev/null +++ b/app/Generator/Report/Category/MonthReportGenerator.php @@ -0,0 +1,99 @@ +accounts->pluck('id')->toArray()); + $reportType = 'category'; + + // render! + return view('reports.category.month', compact('accountIds', 'reportType')) + ->with('start', $this->start)->with('end', $this->end) + ->with('categories', $this->categories) + ->render(); + } + + /** + * @param Collection $accounts + * + * @return ReportGeneratorInterface + */ + public function setAccounts(Collection $accounts): ReportGeneratorInterface + { + $this->accounts = $accounts; + + 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; + } +} \ No newline at end of file diff --git a/app/Generator/Report/Category/MultiYearReportGenerator.php b/app/Generator/Report/Category/MultiYearReportGenerator.php new file mode 100644 index 0000000000..2f663c3925 --- /dev/null +++ b/app/Generator/Report/Category/MultiYearReportGenerator.php @@ -0,0 +1,27 @@ +budgetRepository = $budgetRepository; - $this->tagRepository = $tagRepository; } /** @@ -234,78 +226,4 @@ class ReportHelper implements ReportHelperInterface return $months; } - /** - * Returns an array of tags and their comparitive size with amounts bla bla. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return array - */ - public function tagReport(Carbon $start, Carbon $end, Collection $accounts): array - { - $ids = $accounts->pluck('id')->toArray(); - $set = Tag:: - leftJoin('tag_transaction_journal', 'tags.id', '=', 'tag_transaction_journal.tag_id') - ->leftJoin('transaction_journals', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->leftJoin( - 'transactions AS source', function (JoinClause $join) { - $join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', '0'); - } - ) - ->leftJoin( - 'transactions AS destination', function (JoinClause $join) { - $join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')->where('destination.amount', '>', '0'); - } - ) - ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) - ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) - ->where( - // source.account_id in accountIds XOR destination.account_id in accountIds - function (Builder $query) use ($ids) { - $query->where( - function (Builder $q1) use ($ids) { - $q1->whereIn('source.account_id', $ids) - ->whereNotIn('destination.account_id', $ids); - } - )->orWhere( - function (Builder $q2) use ($ids) { - $q2->whereIn('destination.account_id', $ids) - ->whereNotIn('source.account_id', $ids); - } - ); - } - ) - ->get(['tags.id', 'tags.tag', 'transaction_journals.id as journal_id', 'destination.amount']); - $collection = []; - if ($set->count() === 0) { - return $collection; - } - /** @var Tag $entry */ - foreach ($set as $entry) { - // less than zero? multiply to be above zero. - $amount = $entry->amount; - $id = intval($entry->id); - $previousAmount = $collection[$id]['amount'] ?? '0'; - $collection[$id] = [ - 'id' => $id, - 'tag' => $entry->tag, - 'amount' => bcadd($previousAmount, $amount), - ]; - } - - // cleanup collection (match "fonts") - $max = strval(max(array_column($collection, 'amount'))); - foreach ($collection as $id => $entry) { - $size = bcdiv($entry['amount'], $max, 4); - if (bccomp($size, '0.25') === -1) { - $size = '0.5'; - } - $collection[$id]['fontsize'] = $size; - } - - return $collection; - } - } diff --git a/app/Helpers/Report/ReportHelperInterface.php b/app/Helpers/Report/ReportHelperInterface.php index 34ffcc9e7b..47af08976b 100644 --- a/app/Helpers/Report/ReportHelperInterface.php +++ b/app/Helpers/Report/ReportHelperInterface.php @@ -80,15 +80,4 @@ interface ReportHelperInterface */ public function listOfMonths(Carbon $date): array; - /** - * Returns an array of tags and their comparitive size with amounts bla bla. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return array - */ - public function tagReport(Carbon $start, Carbon $end, Collection $accounts): array; - } diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index 2a24548513..8d29ba1edf 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -16,22 +16,15 @@ namespace FireflyIII\Http\Controllers; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Generator\Report\ReportGeneratorFactory; -use FireflyIII\Generator\Report\Standard\MonthReportGenerator; -use FireflyIII\Generator\Report\StandardReportGenerator; -use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Helpers\Report\ReportHelperInterface; use FireflyIII\Http\Requests\ReportFormRequest; -use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; -use FireflyIII\Models\Transaction; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Collection; use Preferences; use Response; -use Session; -use Steam; use View; /** @@ -56,6 +49,7 @@ class ReportController extends Controller function ($request, $next) { View::share('title', trans('firefly.reports')); View::share('mainTitleIcon', 'fa-line-chart'); + View::share('subTitleIcon', 'fa-calendar'); $this->helper = app(ReportHelperInterface::class); @@ -75,12 +69,9 @@ class ReportController extends Controller */ public function auditReport(Carbon $start, Carbon $end, Collection $accounts) { - // throw an error if necessary. if ($end < $start) { - throw new FireflyException('End date cannot be before start date, silly!'); + return view('error')->with('message', trans('firefly.end_after_start_date')); } - - // lower threshold if ($start < session('first')) { $start = session('first'); } @@ -94,7 +85,7 @@ class ReportController extends Controller ] ) ); - View::share('subTitleIcon', 'fa-calendar'); + $generator = ReportGeneratorFactory::reportGenerator('Audit', $start, $end); $generator->setAccounts($accounts); @@ -112,14 +103,47 @@ class ReportController extends Controller * @return string * @throws FireflyException */ - public function defaultReport(Carbon $start, Carbon $end, Collection $accounts) + public function categoryReport(Carbon $start, Carbon $end, Collection $accounts, Collection $categories) { - // throw an error if necessary. if ($end < $start) { - throw new FireflyException('End date cannot be before start date, silly!'); + return view('error')->with('message', trans('firefly.end_after_start_date')); + } + if ($start < session('first')) { + $start = session('first'); } - // lower threshold + View::share( + 'subTitle', trans( + 'firefly.report_category', + [ + 'start' => $start->formatLocalized($this->monthFormat), + 'end' => $end->formatLocalized($this->monthFormat), + ] + ) + ); + + $generator = ReportGeneratorFactory::reportGenerator('Category', $start, $end); + $generator->setAccounts($accounts); + $generator->setCategories($categories); + $result = $generator->generate(); + + return $result; + + } + + /** + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * + * @return string + * @throws FireflyException + */ + public function defaultReport(Carbon $start, Carbon $end, Collection $accounts) + { + if ($end < $start) { + return view('error')->with('message', trans('firefly.end_after_start_date')); + } if ($start < session('first')) { $start = session('first'); } @@ -133,7 +157,6 @@ class ReportController extends Controller ] ) ); - View::share('subTitleIcon', 'fa-calendar'); $generator = ReportGeneratorFactory::reportGenerator('Standard', $start, $end); $generator->setAccounts($accounts); @@ -155,16 +178,8 @@ class ReportController extends Controller $start = clone session('first'); $months = $this->helper->listOfMonths($start); $customFiscalYear = Preferences::get('customFiscalYear', 0)->data; - - // does the user have shared accounts? - $accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); - // get id's for quick links: - $accountIds = []; - /** @var Account $account */ - foreach ($accounts as $account) { - $accountIds [] = $account->id; - } - $accountList = join(',', $accountIds); + $accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); + $accountList = join(',', $accounts->pluck('id')->toArray()); return view('reports.index', compact('months', 'accounts', 'start', 'accountList', 'customFiscalYear')); @@ -177,7 +192,6 @@ class ReportController extends Controller */ public function options(string $reportType) { - $result = ''; switch ($reportType) { default: $result = $this->noReportOptions(); @@ -206,7 +220,7 @@ class ReportController extends Controller $categories = join(',', $request->getCategoryList()->pluck('id')->toArray()); if ($end < $start) { - throw new FireflyException('End date cannot be before start date, silly!'); + return view('error')->with('message', trans('firefly.end_after_start_date')); } // lower threshold diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 57e2e48caf..0700a0f643 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -686,6 +686,8 @@ return [ 'reports_extra_options' => 'Extra options', '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.', // charts: 'chart' => 'Chart', diff --git a/resources/views/reports/category/month.twig b/resources/views/reports/category/month.twig new file mode 100644 index 0000000000..4864dc3ca6 --- /dev/null +++ b/resources/views/reports/category/month.twig @@ -0,0 +1,65 @@ +{% extends "./layout/default" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} +{% endblock %} + +{% block content %} + +
    +
    + Summary here. Accounts and categories involved. Summary of in/out +
    +
    + Pie chart with spending (aka all withdrawals in category). Optional checkbox to include all other transactions. +
    +
    + Pie chart with income (aka all deposits in category). Optional checkbox to include all other transactions (for comparison). +
    +
    + +
    +
    + big chart here + + Show income / expenses per period. Differs per report: month = per day, year = per month, multi-year = per year. + In a bar chart, possibly grouped by expense/revenue account. + +
    +
    + +
    +
    + List of spending (withdrawals) by account, if relevant. Grouped:
    + + BC: 456 + AH: 123 + + order by size + + linked to chart + + use reset button to reset after chart was clicked. +
    +
    + List of spending (withdrawals) by transaction, if relevant. Not grouped
    + + more groceries: 456 + groceries: 123 + + ordered by size. top x list? +
    + +
    + Same but for income +
    + +
    + +{% endblock %} + +{% block scripts %} +{% endblock %} + +{% block styles %} +{% endblock %} \ No newline at end of file diff --git a/resources/views/reports/index.twig b/resources/views/reports/index.twig index 886deb0477..eec3c8c134 100644 --- a/resources/views/reports/index.twig +++ b/resources/views/reports/index.twig @@ -113,81 +113,47 @@

    {{ 'quick_link_reports'|_ }}

-

{{ 'quick_link_default_report'|_ }}

- -

{{ 'quick_link_audit_report'|_ }}

- + {% endfor %}

{{ 'reports_can_bookmark'|_ }}

From 727717931a018d7c25821d2d970655596902a82e Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 11 Nov 2016 20:52:48 +0100 Subject: [PATCH 076/709] Do something fancy with colours. --- .../Chart/Bill/ChartJsBillChartGenerator.php | 3 +- app/Support/ChartColour.php | 56 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 app/Support/ChartColour.php diff --git a/app/Generator/Chart/Bill/ChartJsBillChartGenerator.php b/app/Generator/Chart/Bill/ChartJsBillChartGenerator.php index 07f29dd58b..3e1a2ea4bc 100644 --- a/app/Generator/Chart/Bill/ChartJsBillChartGenerator.php +++ b/app/Generator/Chart/Bill/ChartJsBillChartGenerator.php @@ -15,6 +15,7 @@ namespace FireflyIII\Generator\Chart\Bill; use FireflyIII\Models\Bill; use FireflyIII\Models\Transaction; +use FireflyIII\Support\ChartColour; use Illuminate\Support\Collection; /** @@ -37,7 +38,7 @@ class ChartJsBillChartGenerator implements BillChartGeneratorInterface 'datasets' => [ [ 'data' => [round($unpaid, 2), round(bcmul($paid, '-1'), 2)], - 'backgroundColor' => ['rgba(53, 124, 165,0.7)', 'rgba(0, 141, 76, 0.7)',], + 'backgroundColor' => [ChartColour::getColour(0), ChartColour::getColour(1)], ], ], diff --git a/app/Support/ChartColour.php b/app/Support/ChartColour.php new file mode 100644 index 0000000000..5ae8f881c0 --- /dev/null +++ b/app/Support/ChartColour.php @@ -0,0 +1,56 @@ + Date: Sat, 12 Nov 2016 06:27:48 +0100 Subject: [PATCH 077/709] Fixes #398 --- app/Repositories/Tag/TagRepository.php | 90 ++++++++++++++++++-------- 1 file changed, 62 insertions(+), 28 deletions(-) diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index c71d031d72..ecdc5bfe94 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -19,6 +19,7 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\User; use Illuminate\Support\Collection; +use Log; /** * Class TagRepository @@ -54,11 +55,14 @@ class TagRepository implements TagRepositoryInterface * Already connected: */ if ($journal->tags()->find($tag->id)) { + Log::error(sprintf('Cannot find tag #%d', $tag->id)); + return false; } switch ($tag->tagMode) { case 'nothing': + Log::debug(sprintf('Tag #%d connected', $tag->id)); $journal->tags()->save($tag); $journal->save(); @@ -184,22 +188,23 @@ class TagRepository implements TagRepositoryInterface */ protected function connectAdvancePayment(TransactionJournal $journal, Tag $tag): bool { - /** @var TransactionType $transfer */ - $transfer = TransactionType::whereType(TransactionType::TRANSFER)->first(); - /** @var TransactionType $withdrawal */ - $withdrawal = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); - /** @var TransactionType $deposit */ - $deposit = TransactionType::whereType(TransactionType::DEPOSIT)->first(); + $type = $journal->transactionType->type; + $withdrawals = $tag->transactionJournals() + ->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id') + ->where('transaction_types.type', TransactionType::WITHDRAWAL)->count(); + $deposits = $tag->transactionJournals() + ->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id') + ->where('transaction_types.type', TransactionType::DEPOSIT)->count(); - $withdrawals = $tag->transactionJournals()->where('transaction_type_id', $withdrawal->id)->count(); - $deposits = $tag->transactionJournals()->where('transaction_type_id', $deposit->id)->count(); + if ($type === TransactionType::TRANSFER) { // advance payments cannot accept transfers: + Log::error(sprintf('Journal #%d is a transfer and cannot connect to tag #%d', $journal->id, $tag->id)); - if ($journal->transaction_type_id == $transfer->id) { // advance payments cannot accept transfers: return false; } // the first transaction to be attached to this tag is attached just like that: if ($withdrawals < 1 && $deposits < 1) { + Log::debug(sprintf('Tag #%d has 0 withdrawals and 0 deposits so its fine.', $tag->id)); $journal->tags()->save($tag); $journal->save(); @@ -207,12 +212,16 @@ class TagRepository implements TagRepositoryInterface } // if withdrawal and already has a withdrawal, return false: - if ($journal->transaction_type_id == $withdrawal->id && $withdrawals == 1) { + if ($type === TransactionType::WITHDRAWAL && $withdrawals > 0) { + Log::error(sprintf('Journal #%d is a withdrawal but tag already has %d withdrawal(s).', $journal->id, $withdrawals)); + return false; } // if already has transaction journals, must match ALL asset account id's: if ($deposits > 0 || $withdrawals == 1) { + Log::debug('Need to match all asset accounts.'); + return $this->matchAll($journal, $tag); } @@ -229,28 +238,39 @@ class TagRepository implements TagRepositoryInterface */ protected function connectBalancingAct(TransactionJournal $journal, Tag $tag): bool { - /** @var TransactionType $withdrawal */ - $withdrawal = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); - $withdrawals = $tag->transactionJournals()->where('transaction_type_id', $withdrawal->id)->count(); - /** @var TransactionType $transfer */ - $transfer = TransactionType::whereType(TransactionType::TRANSFER)->first(); - $transfers = $tag->transactionJournals()->where('transaction_type_id', $transfer->id)->count(); + $type = $journal->transactionType->type; + $withdrawals = $tag->transactionJournals() + ->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id') + ->where('transaction_types.type', TransactionType::WITHDRAWAL)->count(); + $transfers = $tag->transactionJournals() + ->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id') + ->where('transaction_types.type', TransactionType::TRANSFER)->count(); + Log::debug(sprintf('Journal #%d is a %s', $journal->id, $type)); + // only if this is the only withdrawal. - if ($journal->transaction_type_id == $withdrawal->id && $withdrawals < 1) { + if ($type === TransactionType::WITHDRAWAL && $withdrawals < 1) { + Log::debug('Will connect this journal because it is the only withdrawal in this tag.'); $journal->tags()->save($tag); $journal->save(); return true; } // and only if this is the only transfer - if ($journal->transaction_type_id == $transfer->id && $transfers < 1) { + if ($type === TransactionType::TRANSFER && $transfers < 1) { + Log::debug('Will connect this journal because it is the only transfer in this tag.'); $journal->tags()->save($tag); $journal->save(); return true; } + Log::error( + sprintf( + 'Tag #%d has %d withdrawals and %d transfers and cannot contain %s #%d', + $tag->id, $withdrawals, $transfers, $type, $journal->id + ) + ); // ignore expense return false; @@ -267,28 +287,42 @@ class TagRepository implements TagRepositoryInterface * * @return bool */ - protected function matchAll(TransactionJournal $journal, Tag $tag): bool + private function matchAll(TransactionJournal $journal, Tag $tag): bool { - $checkSources = join(',', TransactionJournal::sourceAccountList($journal)->pluck('id')->toArray()); - $checkDestinations = join(',', TransactionJournal::destinationAccountList($journal)->pluck('id')->toArray()); + $journalSources = join(',', TransactionJournal::sourceAccountList($journal)->pluck('id')->toArray()); + $journalDestinations = join(',', TransactionJournal::destinationAccountList($journal)->pluck('id')->toArray()); + $match = true; + $journals = $tag->transactionJournals()->get(['transaction_journals.*']); - $match = true; - /** @var TransactionJournal $check */ - foreach ($tag->transactionjournals as $check) { + Log::debug(sprintf('Tag #%d has %d journals to verify:', $tag->id, $journals->count())); + + /** @var TransactionJournal $original */ + foreach ($journals as $original) { // $checkAccount is the source_account for a withdrawal // $checkAccount is the destination_account for a deposit - $thisSources = join(',', TransactionJournal::sourceAccountList($check)->pluck('id')->toArray()); - $thisDestinations = join(',', TransactionJournal::destinationAccountList($check)->pluck('id')->toArray()); + $originalSources = join(',', TransactionJournal::sourceAccountList($original)->pluck('id')->toArray()); + $originalDestinations = join(',', TransactionJournal::destinationAccountList($original)->pluck('id')->toArray()); + + if ($original->isWithdrawal() && $originalSources !== $journalDestinations) { + Log::debug(sprintf('Original journal #%d is a withdrawal.', $original->id)); + Log::debug(sprintf('Journal #%d must have these destination accounts: %s', $journal->id, $originalSources)); + Log::debug(sprintf('Journal #%d actually these destination accounts: %s', $journal->id, $journalDestinations)); + Log::debug('So match is FALSE'); - if ($check->isWithdrawal() && $thisSources !== $checkSources) { $match = false; } - if ($check->isDeposit() && $thisDestinations !== $checkDestinations) { + if ($original->isDeposit() && $originalDestinations !== $journalSources) { + Log::debug(sprintf('Original journal #%d is a deposit.', $original->id)); + Log::debug(sprintf('Journal #%d must have these destination accounts: %s', $journal->id, $originalSources)); + Log::debug(sprintf('Journal #%d actually these destination accounts: %s', $journal->id, $journalDestinations)); + Log::debug('So match is FALSE'); + $match = false; } } if ($match) { + Log::debug(sprintf('Match is true, connect journal #%d with tag #%d.', $journal->id, $tag->id)); $journal->tags()->save($tag); $journal->save(); From 32b5a84a0c1243129dc9d1e0bbdbb3afe946db1c Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 12 Nov 2016 06:34:54 +0100 Subject: [PATCH 078/709] Fixes #401 --- .../Transaction/MassController.php | 42 +++++++++---------- .../Journal/JournalRepository.php | 2 + resources/views/transactions/mass-delete.twig | 12 +----- resources/views/transactions/mass-edit.twig | 2 +- 4 files changed, 25 insertions(+), 33 deletions(-) diff --git a/app/Http/Controllers/Transaction/MassController.php b/app/Http/Controllers/Transaction/MassController.php index f32e8df053..54e886abab 100644 --- a/app/Http/Controllers/Transaction/MassController.php +++ b/app/Http/Controllers/Transaction/MassController.php @@ -196,35 +196,33 @@ class MassController extends Controller $journal = $repository->find(intval($journalId)); if ($journal) { // get optional fields: - $what = strtolower(TransactionJournal::transactionTypeStr($journal)); - + $what = strtolower(TransactionJournal::transactionTypeStr($journal)); $sourceAccountId = $request->get('source_account_id')[$journal->id] ?? 0; $sourceAccountName = $request->get('source_account_name')[$journal->id] ?? ''; $destAccountId = $request->get('destination_account_id')[$journal->id] ?? 0; $destAccountName = $request->get('destination_account_name')[$journal->id] ?? ''; - - $budgetId = $journal->budgets->first() ? $journal->budgets->first()->id : 0; - $category = $request->get('category')[$journal->id]; - $tags = $journal->tags->pluck('tag')->toArray(); + $budgetId = $journal->budgets->first() ? $journal->budgets->first()->id : 0; + $category = $request->get('category')[$journal->id]; + $tags = $journal->tags->pluck('tag')->toArray(); // build data array $data = [ - 'id' => $journal->id, - 'what' => $what, - 'description' => $request->get('description')[$journal->id], - 'source_account_id' => intval($sourceAccountId), - 'source_account_name' => $sourceAccountName, - 'destination_account_id' => intval($destAccountId), - 'destination_account_name' => $destAccountName, - 'amount' => round($request->get('amount')[$journal->id], 4), - 'amount_currency_id_amount' => intval($request->get('amount_currency_id_amount_' . $journal->id)), - 'date' => new Carbon($request->get('date')[$journal->id]), - 'interest_date' => $journal->interest_date, - 'book_date' => $journal->book_date, - 'process_date' => $journal->process_date, - 'budget_id' => $budgetId, - 'category' => $category, - 'tags' => $tags, + 'id' => $journal->id, + 'what' => $what, + 'description' => $request->get('description')[$journal->id], + 'source_account_id' => intval($sourceAccountId), + 'source_account_name' => $sourceAccountName, + 'destination_account_id' => intval($destAccountId), + 'destination_account_name' => $destAccountName, + 'amount' => round($request->get('amount')[$journal->id], 4), + 'currency_id' => intval($request->get('amount_currency_id_amount_' . $journal->id)), + 'date' => new Carbon($request->get('date')[$journal->id]), + 'interest_date' => $journal->interest_date, + 'book_date' => $journal->book_date, + 'process_date' => $journal->process_date, + 'budget_id' => $budgetId, + 'category' => $category, + 'tags' => $tags, ]; // call repository update function. diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php index 6b3e476b50..0e8c73942d 100644 --- a/app/Repositories/Journal/JournalRepository.php +++ b/app/Repositories/Journal/JournalRepository.php @@ -397,6 +397,7 @@ class JournalRepository implements JournalRepositoryInterface if (strlen(trim($name)) > 0) { $tag = Tag::firstOrCreateEncrypted(['tag' => $name, 'user_id' => $journal->user_id]); if (!is_null($tag)) { + Log::debug(sprintf('Will try to connect tag #%d to journal #%d.', $tag->id, $journal->id)); $tagRepository->connect($journal, $tag); } } @@ -733,6 +734,7 @@ class JournalRepository implements JournalRepositoryInterface // connect each tag to journal (if not yet connected): /** @var Tag $tag */ foreach ($tags as $tag) { + Log::debug(sprintf('Will try to connect tag #%d to journal #%d.', $tag->id, $journal->id)); $tagRepository->connect($journal, $tag); } diff --git a/resources/views/transactions/mass-delete.twig b/resources/views/transactions/mass-delete.twig index 0cc411b8d0..6eec80dec5 100644 --- a/resources/views/transactions/mass-delete.twig +++ b/resources/views/transactions/mass-delete.twig @@ -49,18 +49,10 @@ {{ journal.date.formatLocalized(monthAndDayFormat) }}
{% endfor %} diff --git a/resources/views/transactions/mass-edit.twig b/resources/views/transactions/mass-edit.twig index b42592d13a..6489998547 100644 --- a/resources/views/transactions/mass-edit.twig +++ b/resources/views/transactions/mass-edit.twig @@ -90,7 +90,7 @@
 
+
#{{ user.id }} + #{{ user.id }} {{ user.email }} + {{ user.created_at.formatLocalized(monthAndDayFormat) }} {{ user.created_at.format('H:i') }} {{ Preferences.getForUser(user,"confirmation_ip_address").data }} + {% if user.isAdmin %} {% else %} {% endif %} + {% if user.has2FA %} {% else %} {% endif %} + {% if user.activated %} {% else %} {% endif %} + {% if user.blocked == 1 %} {% else %} @@ -95,3 +95,9 @@ {% endblock %} +{% block styles %} + +{% endblock %} +{% block scripts %} + +{% endblock %} diff --git a/resources/views/admin/users/show.twig b/resources/views/admin/users/show.twig index e008708de1..38794f32c2 100644 --- a/resources/views/admin/users/show.twig +++ b/resources/views/admin/users/show.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, user) }} diff --git a/resources/views/attachments/delete.twig b/resources/views/attachments/delete.twig index a6191d5c6e..3e854f8bde 100644 --- a/resources/views/attachments/delete.twig +++ b/resources/views/attachments/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute().getName(), attachment) }} diff --git a/resources/views/attachments/edit.twig b/resources/views/attachments/edit.twig index 976e3b2981..199b39b64f 100644 --- a/resources/views/attachments/edit.twig +++ b/resources/views/attachments/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute().getName(), attachment) }} diff --git a/resources/views/auth/confirmation/error.twig b/resources/views/auth/confirmation/error.twig index d95de0b214..a417e4db7b 100644 --- a/resources/views/auth/confirmation/error.twig +++ b/resources/views/auth/confirmation/error.twig @@ -12,8 +12,8 @@ - - {% include('partials/favicons.twig') %} + {# favicons #} + {% include('partials.favicons') %} diff --git a/resources/views/auth/confirmation/no-resent.twig b/resources/views/auth/confirmation/no-resent.twig index 01e079cbad..64c24e95e0 100644 --- a/resources/views/auth/confirmation/no-resent.twig +++ b/resources/views/auth/confirmation/no-resent.twig @@ -12,8 +12,8 @@ - - {% include('partials/favicons.twig') %} + {# favicons #} + {% include('partials.favicons') %} diff --git a/resources/views/auth/confirmation/resent.twig b/resources/views/auth/confirmation/resent.twig index c7e0307d3d..accae395df 100644 --- a/resources/views/auth/confirmation/resent.twig +++ b/resources/views/auth/confirmation/resent.twig @@ -12,8 +12,8 @@ - - {% include('partials/favicons.twig') %} + {# favicons #} + {% include('partials.favicons') %} diff --git a/resources/views/auth/login.twig b/resources/views/auth/login.twig index 435870685a..1411157599 100644 --- a/resources/views/auth/login.twig +++ b/resources/views/auth/login.twig @@ -1,4 +1,4 @@ -{% extends "./layout/guest.twig" %} +{% extends "./layout/guest" %} {% block content %} diff --git a/resources/views/auth/lost-two-factor.twig b/resources/views/auth/lost-two-factor.twig index 7f64e57379..c82ffddd6c 100644 --- a/resources/views/auth/lost-two-factor.twig +++ b/resources/views/auth/lost-two-factor.twig @@ -12,8 +12,8 @@ - - {% include('partials/favicons.twig') %} + {# favicons #} + {% include('partials.favicons') %} diff --git a/resources/views/auth/passwords/email.twig b/resources/views/auth/passwords/email.twig index 3f9a286116..475f9bb7e6 100644 --- a/resources/views/auth/passwords/email.twig +++ b/resources/views/auth/passwords/email.twig @@ -1,4 +1,4 @@ -{% extends "./layout/guest.twig" %} +{% extends "./layout/guest" %} {% block content %} diff --git a/resources/views/auth/passwords/reset.twig b/resources/views/auth/passwords/reset.twig index c70b06e8a8..bbe9a732d8 100644 --- a/resources/views/auth/passwords/reset.twig +++ b/resources/views/auth/passwords/reset.twig @@ -1,4 +1,4 @@ -{% extends "./layout/guest.twig" %} +{% extends "./layout/guest" %} {% block content %} diff --git a/resources/views/auth/register.twig b/resources/views/auth/register.twig index fc3f7bf51b..532dc26d91 100644 --- a/resources/views/auth/register.twig +++ b/resources/views/auth/register.twig @@ -1,4 +1,4 @@ -{% extends "./layout/guest.twig" %} +{% extends "./layout/guest" %} {% block content %} diff --git a/resources/views/auth/two-factor.twig b/resources/views/auth/two-factor.twig index e7148d811e..65a29bed85 100644 --- a/resources/views/auth/two-factor.twig +++ b/resources/views/auth/two-factor.twig @@ -1,4 +1,4 @@ -{% extends "./layout/guest.twig" %} +{% extends "./layout/guest" %} {% block content %} diff --git a/resources/views/bills/create.twig b/resources/views/bills/create.twig index eb1a970b22..69ccaa19fb 100644 --- a/resources/views/bills/create.twig +++ b/resources/views/bills/create.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, piggyBank) }} diff --git a/resources/views/bills/delete.twig b/resources/views/bills/delete.twig index d9185aa759..344779bdc7 100644 --- a/resources/views/bills/delete.twig +++ b/resources/views/bills/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, bill) }} diff --git a/resources/views/bills/edit.twig b/resources/views/bills/edit.twig index ed8d331b82..53c1d7a717 100644 --- a/resources/views/bills/edit.twig +++ b/resources/views/bills/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, bill) }} diff --git a/resources/views/bills/index.twig b/resources/views/bills/index.twig index 644c0f243e..28bc6261fe 100644 --- a/resources/views/bills/index.twig +++ b/resources/views/bills/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} @@ -22,7 +22,7 @@
- {% include 'list/bills.twig' %} + {% include 'list/bills' %}
diff --git a/resources/views/bills/show.twig b/resources/views/bills/show.twig index 85dd232568..5730340721 100644 --- a/resources/views/bills/show.twig +++ b/resources/views/bills/show.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, bill) }} @@ -105,8 +105,8 @@

{{ 'connected_journals'|_ }}

-
- {% include 'list.journals' %} +
+ {% include 'list.journals-tasker' %}
diff --git a/resources/views/budgets/create.twig b/resources/views/budgets/create.twig index 24f47d7ac2..3974791a23 100644 --- a/resources/views/budgets/create.twig +++ b/resources/views/budgets/create.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/budgets/delete.twig b/resources/views/budgets/delete.twig index 10601310ee..297cef882a 100644 --- a/resources/views/budgets/delete.twig +++ b/resources/views/budgets/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, budget) }} diff --git a/resources/views/budgets/edit.twig b/resources/views/budgets/edit.twig index 0bf5ec3daa..e8010bc424 100644 --- a/resources/views/budgets/edit.twig +++ b/resources/views/budgets/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, budget) }} diff --git a/resources/views/budgets/index.twig b/resources/views/budgets/index.twig index e3b0f4ba0c..66e95d7f1f 100644 --- a/resources/views/budgets/index.twig +++ b/resources/views/budgets/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} @@ -185,8 +185,8 @@

{{ 'inactiveBudgets'|_ }}

- {% for index,budget in inactive %} - {% if index != inactive|length-1 %} + {% for budget in inactive %} + {% if loop.index == inactive.count() %} {{ budget.name }} {% else %} {{ budget.name }}, diff --git a/resources/views/budgets/noBudget.twig b/resources/views/budgets/no-budget.twig similarity index 83% rename from resources/views/budgets/noBudget.twig rename to resources/views/budgets/no-budget.twig index 6abbb397d0..e8e1f0809b 100644 --- a/resources/views/budgets/noBudget.twig +++ b/resources/views/budgets/no-budget.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, subTitle) }} @@ -12,7 +12,7 @@

{{ subTitle }}

- {% include 'list.journals' with {'journals': list} %} + {% include 'list.journals-tasker' with {'journals': journals} %}
diff --git a/resources/views/budgets/show.twig b/resources/views/budgets/show.twig index f88ab7ba9b..b0331ae279 100644 --- a/resources/views/budgets/show.twig +++ b/resources/views/budgets/show.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, budget, repetition) }} @@ -36,7 +36,7 @@

{{ 'transactions'|_ }}

- {% include 'list.journals' with {budgetPerspective: budget} %} + {% include 'list.journals-tasker' %}
diff --git a/resources/views/categories/create.twig b/resources/views/categories/create.twig index a7911555fd..e99d27b20a 100644 --- a/resources/views/categories/create.twig +++ b/resources/views/categories/create.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/categories/delete.twig b/resources/views/categories/delete.twig index 858af38417..ffe9a5b0ef 100644 --- a/resources/views/categories/delete.twig +++ b/resources/views/categories/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, category) }} diff --git a/resources/views/categories/edit.twig b/resources/views/categories/edit.twig index 5e40a3254e..3a465adf1b 100644 --- a/resources/views/categories/edit.twig +++ b/resources/views/categories/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, category) }} diff --git a/resources/views/categories/index.twig b/resources/views/categories/index.twig index 95428b52a3..18a1e08314 100644 --- a/resources/views/categories/index.twig +++ b/resources/views/categories/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} @@ -23,7 +23,7 @@
- {% include 'list/categories.twig' %} + {% include 'list/categories' %}
diff --git a/resources/views/categories/noCategory.twig b/resources/views/categories/no-category.twig similarity index 84% rename from resources/views/categories/noCategory.twig rename to resources/views/categories/no-category.twig index 3f43588b4c..c302ca0203 100644 --- a/resources/views/categories/noCategory.twig +++ b/resources/views/categories/no-category.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, subTitle) }} @@ -12,7 +12,7 @@ {{ subTitle }}
- {% include 'list.journals' with {'journals': list} %} + {% include 'list.journals-tasker' %}
diff --git a/resources/views/categories/show.twig b/resources/views/categories/show.twig index f80113b6cc..311b857d6e 100644 --- a/resources/views/categories/show.twig +++ b/resources/views/categories/show.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, category) }} @@ -35,7 +35,7 @@

{{ 'transactions'|_ }}

- {% include 'list.journals' %} + {% include 'list.journals-tasker' %}
diff --git a/resources/views/categories/show_with_date.twig b/resources/views/categories/show_with_date.twig index 4e1cb77b81..624a933957 100644 --- a/resources/views/categories/show_with_date.twig +++ b/resources/views/categories/show_with_date.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, category, carbon) }} @@ -31,7 +31,7 @@

{{ 'transactions'|_ }}

- {% include 'list.journals' %} + {% include 'list.journals-tasker' %}
diff --git a/resources/views/csv/column-roles.twig b/resources/views/csv/column-roles.twig index f25a0a2ea7..92b2590df7 100644 --- a/resources/views/csv/column-roles.twig +++ b/resources/views/csv/column-roles.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/csv/download-config.twig b/resources/views/csv/download-config.twig index bd317bea7c..1dd7a94795 100644 --- a/resources/views/csv/download-config.twig +++ b/resources/views/csv/download-config.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/csv/index.twig b/resources/views/csv/index.twig index cfa3f6450a..141566dff6 100644 --- a/resources/views/csv/index.twig +++ b/resources/views/csv/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/csv/map.twig b/resources/views/csv/map.twig index f8562205bd..3f1cca1587 100644 --- a/resources/views/csv/map.twig +++ b/resources/views/csv/map.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/csv/process.twig b/resources/views/csv/process.twig index 8ca39b64c9..2561268912 100644 --- a/resources/views/csv/process.twig +++ b/resources/views/csv/process.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/currency/create.twig b/resources/views/currency/create.twig index 70943d3e15..26dec5a0b1 100644 --- a/resources/views/currency/create.twig +++ b/resources/views/currency/create.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/currency/delete.twig b/resources/views/currency/delete.twig index 492f33b6e0..5874f126f2 100644 --- a/resources/views/currency/delete.twig +++ b/resources/views/currency/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, currency) }} diff --git a/resources/views/currency/edit.twig b/resources/views/currency/edit.twig index a39ab1b0e4..e54d5f5ca1 100644 --- a/resources/views/currency/edit.twig +++ b/resources/views/currency/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, currency) }} diff --git a/resources/views/currency/index.twig b/resources/views/currency/index.twig index 4effcba0a5..7720779d98 100644 --- a/resources/views/currency/index.twig +++ b/resources/views/currency/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/error.twig b/resources/views/error.twig index 4390f9c05c..6d5f52c6ab 100644 --- a/resources/views/error.twig +++ b/resources/views/error.twig @@ -1,4 +1,4 @@ -{% extends "./layout/guest.twig" %} +{% extends "./layout/guest" %} {% block content %} diff --git a/resources/views/errors/FireflyException.twig b/resources/views/errors/FireflyException.twig index 3e0a701038..9d8343e04c 100644 --- a/resources/views/errors/FireflyException.twig +++ b/resources/views/errors/FireflyException.twig @@ -12,8 +12,8 @@ - - {% include('partials/favicons.twig') %} + {# favicons #} + {% include('partials.favicons') %} diff --git a/resources/views/export/index.twig b/resources/views/export/index.twig index 1b599e19d5..50b0123c5d 100644 --- a/resources/views/export/index.twig +++ b/resources/views/export/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/form/amount.twig b/resources/views/form/amount.twig index 46f9fec160..c28227d1f7 100644 --- a/resources/views/form/amount.twig +++ b/resources/views/form/amount.twig @@ -26,7 +26,7 @@ - {% include 'form/feedback.twig' %} + {% include 'form/feedback' %} diff --git a/resources/views/form/balance.twig b/resources/views/form/balance.twig index 46f9fec160..c28227d1f7 100644 --- a/resources/views/form/balance.twig +++ b/resources/views/form/balance.twig @@ -26,7 +26,7 @@ - {% include 'form/feedback.twig' %} + {% include 'form/feedback' %} diff --git a/resources/views/form/checkbox.twig b/resources/views/form/checkbox.twig index 9e2d718b81..d4a0143871 100644 --- a/resources/views/form/checkbox.twig +++ b/resources/views/form/checkbox.twig @@ -13,7 +13,7 @@ {{ Form.checkbox(name, value, options.checked, options) }} - {% include 'form/help.twig' %} - {% include 'form/feedback.twig' %} + {% include 'form/help' %} + {% include 'form/feedback' %} diff --git a/resources/views/form/date.twig b/resources/views/form/date.twig index 3f08f20e3b..0e86cf5a07 100644 --- a/resources/views/form/date.twig +++ b/resources/views/form/date.twig @@ -8,7 +8,7 @@ {{ Form.input('date', name, value, options) }} - {% include 'form/help.twig' %} - {% include 'form/feedback.twig' %} + {% include 'form/help' %} + {% include 'form/feedback' %} diff --git a/resources/views/form/file.twig b/resources/views/form/file.twig index 0596ef7dbd..21da01da64 100644 --- a/resources/views/form/file.twig +++ b/resources/views/form/file.twig @@ -3,7 +3,7 @@
{{ Form.file(name, options) }} - {% include 'form/help.twig' %} - {% include 'form/feedback.twig' %} + {% include 'form/help' %} + {% include 'form/feedback' %}
diff --git a/resources/views/form/integer.twig b/resources/views/form/integer.twig index 6b0b94fe09..9ce0e38f5b 100644 --- a/resources/views/form/integer.twig +++ b/resources/views/form/integer.twig @@ -4,7 +4,7 @@
{{ Form.input('number', name, value, options) }} - {% include 'form/feedback.twig' %} + {% include 'form/feedback' %}
diff --git a/resources/views/form/location.twig b/resources/views/form/location.twig index 0f57303f3b..34503bab77 100644 --- a/resources/views/form/location.twig +++ b/resources/views/form/location.twig @@ -10,6 +10,6 @@ - {% include 'form/feedback.twig' %} + {% include 'form/feedback' %} diff --git a/resources/views/form/multiCheckbox.twig b/resources/views/form/multiCheckbox.twig index 5687551004..39776e2be0 100644 --- a/resources/views/form/multiCheckbox.twig +++ b/resources/views/form/multiCheckbox.twig @@ -15,8 +15,8 @@ {% endfor %} - {% include 'form/help.twig' %} - {% include 'form/feedback.twig' %} + {% include 'form/help' %} + {% include 'form/feedback' %} diff --git a/resources/views/form/multiRadio.twig b/resources/views/form/multiRadio.twig index 1bef4bec1e..f321eda908 100644 --- a/resources/views/form/multiRadio.twig +++ b/resources/views/form/multiRadio.twig @@ -10,8 +10,8 @@ {% endfor %} - {% include 'form/help.twig' %} - {% include 'form/feedback.twig' %} + {% include 'form/help' %} + {% include 'form/feedback' %} diff --git a/resources/views/form/select.twig b/resources/views/form/select.twig index e5b6a7874f..2d48ddfcaa 100644 --- a/resources/views/form/select.twig +++ b/resources/views/form/select.twig @@ -3,8 +3,8 @@
{{ Form.select(name, list, selected , options ) }} - {% include 'form/help.twig' %} - {% include 'form/feedback.twig' %} + {% include 'form/help' %} + {% include 'form/feedback' %}
diff --git a/resources/views/form/tags.twig b/resources/views/form/tags.twig index 0b199f9117..e504f6c452 100644 --- a/resources/views/form/tags.twig +++ b/resources/views/form/tags.twig @@ -3,6 +3,6 @@
{{ Form.input('text', name, value, options) }} - {% include 'form/feedback.twig' %} + {% include 'form/feedback' %}
diff --git a/resources/views/form/text.twig b/resources/views/form/text.twig index c2f890c0fe..e94cee84fb 100644 --- a/resources/views/form/text.twig +++ b/resources/views/form/text.twig @@ -3,7 +3,7 @@
{{ Form.input('text', name, value, options) }} - {% include 'form/help.twig' %} - {% include 'form/feedback.twig' %} + {% include 'form/help' %} + {% include 'form/feedback' %}
diff --git a/resources/views/form/textarea.twig b/resources/views/form/textarea.twig index 9bed7883e2..c765ee4eee 100644 --- a/resources/views/form/textarea.twig +++ b/resources/views/form/textarea.twig @@ -3,7 +3,7 @@
{{ Form.textarea(name, value, options) }} - {% include 'form/help.twig' %} - {% include 'form/feedback.twig' %} + {% include 'form/help' %} + {% include 'form/feedback' %}
diff --git a/resources/views/import/complete.twig b/resources/views/import/complete.twig index 4272cc3648..9a95c16620 100644 --- a/resources/views/import/complete.twig +++ b/resources/views/import/complete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists }} diff --git a/resources/views/import/csv/configure.twig b/resources/views/import/csv/configure.twig index 7dccd35ede..d650adbbb8 100644 --- a/resources/views/import/csv/configure.twig +++ b/resources/views/import/csv/configure.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, job) }} diff --git a/resources/views/import/csv/map.twig b/resources/views/import/csv/map.twig index 5f21f1a9d0..159f73edd6 100644 --- a/resources/views/import/csv/map.twig +++ b/resources/views/import/csv/map.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/import/csv/roles.twig b/resources/views/import/csv/roles.twig index c18b7e8c03..aad820fbbd 100644 --- a/resources/views/import/csv/roles.twig +++ b/resources/views/import/csv/roles.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} @@ -13,7 +13,9 @@

{{ trans('csv.column_roles_title') }}

-

{{ trans('csv.column_roles_text')|raw }}

+

+ {{ 'see_help_top_right'|_ }} +

diff --git a/resources/views/import/finished.twig b/resources/views/import/finished.twig index abf47a20c6..63e5b450fc 100644 --- a/resources/views/import/finished.twig +++ b/resources/views/import/finished.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists }} diff --git a/resources/views/import/index.twig b/resources/views/import/index.twig index 137d87bf9d..922a3fa12e 100644 --- a/resources/views/import/index.twig +++ b/resources/views/import/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists }} @@ -17,20 +17,7 @@ {{ 'import_data_full'|_ }}

- {{ 'import_intro_what_it_does'|_ }} -

-

- {{ 'import_intro_import_conf_title'|_ }} -

-

- {{ 'import_intro_import_conf_text'|_ }} -

-

- {{ 'import_intro_beta_warning'|_ }} -

- -

-  {{ 'import_intro_beta'|_ }} + {{ 'see_help_top_right'|_ }}

diff --git a/resources/views/import/status.twig b/resources/views/import/status.twig index c2bb550fff..fbddfa8421 100644 --- a/resources/views/import/status.twig +++ b/resources/views/import/status.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists }} diff --git a/resources/views/index.twig b/resources/views/index.twig index 14a14c66e3..679b8a1581 100644 --- a/resources/views/index.twig +++ b/resources/views/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists }} @@ -8,7 +8,7 @@ - {% include 'partials/boxes.twig' %} + {% include 'partials.boxes' %}
diff --git a/resources/views/layout/default.twig b/resources/views/layout/default.twig index 921138dd81..45a77cca73 100644 --- a/resources/views/layout/default.twig +++ b/resources/views/layout/default.twig @@ -28,8 +28,8 @@ - - {% include('partials/favicons.twig') %} + {# favicons #} + {% include('partials.favicons') %} @@ -98,7 +98,7 @@ - {% include('partials/menu-sidebar.twig') %} + {% include('partials.menu-sidebar') %} @@ -107,14 +107,14 @@
- {% include('partials/page-header.twig') %} + {% include('partials.page-header') %} {% block breadcrumbs %}{% endblock %}
- {% include('partials/flashes.twig') %} + {% include('partials.flashes') %} {% block content %}{% endblock %} @@ -130,7 +130,7 @@ Firefly III - {% include('partials/control-bar.twig') %} + {% include('partials.control-bar') %}
@@ -148,6 +148,9 @@
diff --git a/resources/views/layout/empty.twig b/resources/views/layout/empty.twig index 6545719f45..5d0caaf749 100644 --- a/resources/views/layout/empty.twig +++ b/resources/views/layout/empty.twig @@ -16,8 +16,8 @@ - - {% include('partials/favicons.twig') %} + {# favicons #} + {% include('partials.favicons') %} diff --git a/resources/views/layout/guest.twig b/resources/views/layout/guest.twig index a55e49c412..94ac3316ea 100644 --- a/resources/views/layout/guest.twig +++ b/resources/views/layout/guest.twig @@ -26,8 +26,8 @@ - - {% include('partials/favicons.twig') %} + {# favicons #} + {% include('partials.favicons') %} diff --git a/resources/views/list/journals.twig b/resources/views/list/journals.twig index ddadd0aa60..45d5005fb8 100644 --- a/resources/views/list/journals.twig +++ b/resources/views/list/journals.twig @@ -1,5 +1,12 @@ {{ journals.render|raw }} +{% if journals.count == 0 %} +

+ {{ 'nothing_to_display'|_ }} +

+{% endif %} + +{% if journals.count > 0 %} @@ -52,7 +59,7 @@ title="{{ Lang.choice('firefly.nr_of_attachments', journal.attachments|length, {count: journal.attachments|length}) }}"> {% endif %} {% if journal.count_transactions > 2 %} - + {% endif %} @@ -130,3 +137,4 @@ var edit_selected_txt = "{{ 'edit_selected'|_ }}"; var delete_selected_txt = "{{ 'delete_selected'|_ }}"; +{% endif %} \ No newline at end of file diff --git a/resources/views/new-user/index.twig b/resources/views/new-user/index.twig index 7dbb572dcc..3dbcdb81ae 100644 --- a/resources/views/new-user/index.twig +++ b/resources/views/new-user/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/piggy-banks/add-mobile.twig b/resources/views/piggy-banks/add-mobile.twig index 8839178622..29460b3e47 100644 --- a/resources/views/piggy-banks/add-mobile.twig +++ b/resources/views/piggy-banks/add-mobile.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/piggy-banks/create.twig b/resources/views/piggy-banks/create.twig index c2d5480ef1..4d462164bc 100644 --- a/resources/views/piggy-banks/create.twig +++ b/resources/views/piggy-banks/create.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/piggy-banks/delete.twig b/resources/views/piggy-banks/delete.twig index 6c52087c91..3b7b88beef 100644 --- a/resources/views/piggy-banks/delete.twig +++ b/resources/views/piggy-banks/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, piggyBank) }} diff --git a/resources/views/piggy-banks/edit.twig b/resources/views/piggy-banks/edit.twig index 8daeef739f..35f8f26cc5 100644 --- a/resources/views/piggy-banks/edit.twig +++ b/resources/views/piggy-banks/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, piggyBank) }} diff --git a/resources/views/piggy-banks/index.twig b/resources/views/piggy-banks/index.twig index b01ceac8b7..6c4afac57b 100644 --- a/resources/views/piggy-banks/index.twig +++ b/resources/views/piggy-banks/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} @@ -11,7 +11,7 @@

{{ 'piggyBanks'|_ }}

- {% include 'list/piggy-banks.twig' %} + {% include 'list/piggy-banks' %}
@@ -108,7 +108,7 @@
- {% include 'reports/partials/bills.twig' %} + {% include 'reports/partials/bills' %}
@@ -132,11 +132,14 @@ var accountIds = '{{ accountIds }}'; - var accountReportUrl = '{{ route('reports.data.accountReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; - var inOutReportUrl = '{{ route('reports.data.inOutReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; - var categoryReportUrl = '{{ route('reports.data.categoryReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; - var balanceReportUrl = '{{ route('reports.data.balanceReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; - var budgetReportUrl = '{{ route('reports.data.budgetReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var accountReportUri = '{{ route('reports.data.accountReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var incomeReportUri = '{{ route('reports.data.incomeReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var expenseReportUri = '{{ route('reports.data.expenseReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var incExpReportUri = '{{ route('reports.data.incExpReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var categoryReportUri = '{{ route('reports.data.categoryReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var budgetReportUri = '{{ route('reports.data.budgetReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var balanceReportUri = '{{ route('reports.data.balanceReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + diff --git a/resources/views/reports/default/multi-year.twig b/resources/views/reports/default/multi-year.twig index 314415d2f8..39ee4403ab 100644 --- a/resources/views/reports/default/multi-year.twig +++ b/resources/views/reports/default/multi-year.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, start, end, reportType, accountIds) }} @@ -91,32 +91,8 @@

{{ 'budgets'|_ }}

-
-
- - - - {% for year in years %} - - {% endfor %} +
-
- - - - {% for id, info in budgetMultiYear %} - - - {% for amount in info.entries %} - - {% endfor %} - - - {% endfor %} - -
Budget{{ year }}
{{ info.name }} - {{ amount|formatAmount }} -
@@ -142,8 +118,12 @@ var accountIds = '{{ accountIds }}'; - var accountReportUrl = '{{ route('reports.data.accountReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; - var inOutReportUrl = '{{ route('reports.data.inOutReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var accountReportUri = '{{ route('reports.data.accountReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var incomeReportUri = '{{ route('reports.data.incomeReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var expenseReportUri = '{{ route('reports.data.expenseReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var incExpReportUri = '{{ route('reports.data.incExpReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + + var budgetMultiUri = '{{ route('reports.data.budgetMultiYear', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; diff --git a/resources/views/reports/default/year.twig b/resources/views/reports/default/year.twig index 41e6ef0ebb..bce36d9970 100644 --- a/resources/views/reports/default/year.twig +++ b/resources/views/reports/default/year.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, start, end, reportType, accountIds) }} @@ -67,7 +67,7 @@
- {% include 'reports/partials/tags.twig' %} + {% include 'reports/partials/tags' %}
@@ -123,9 +123,12 @@ var accountIds = '{{ accountIds }}'; - var accountReportUrl = '{{ route('reports.data.accountReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; - var inOutReportUrl = '{{ route('reports.data.inOutReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; - var budgetYearOverviewUrl = '{{ route('reports.data.budgetYearOverview', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var accountReportUri = '{{ route('reports.data.accountReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var incomeReportUri = '{{ route('reports.data.incomeReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var expenseReportUri = '{{ route('reports.data.expenseReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var incExpReportUri = '{{ route('reports.data.incExpReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + + var budgetYearOverviewUri = '{{ route('reports.data.budgetYearOverview', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; diff --git a/resources/views/reports/index.twig b/resources/views/reports/index.twig index 0fe5cfbbc7..4877fffc62 100644 --- a/resources/views/reports/index.twig +++ b/resources/views/reports/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/reports/partials/budget-multi-year.twig b/resources/views/reports/partials/budget-multi-year.twig new file mode 100644 index 0000000000..7fc30ea9f3 --- /dev/null +++ b/resources/views/reports/partials/budget-multi-year.twig @@ -0,0 +1,33 @@ + + + + + {% for year in years %} + + {% endfor %} + + + + + {% for id, info in budgetMultiYear %} + + + {% for amount in info.entries %} + + {% endfor %} + + + {% endfor %} + +
{{ 'budget'|_ }}{{ year }}{{ 'sum'|_ }}
+ {% if id == 0 %} + {{ info.name }} + + {% else %} + {{ info.name }} + {% endif %} + + {{ amount|formatAmount }} + + {{ info.sum|formatAmount }} +
\ No newline at end of file diff --git a/resources/views/rules/index.twig b/resources/views/rules/index.twig index 5c0dee4ef9..803575e88a 100644 --- a/resources/views/rules/index.twig +++ b/resources/views/rules/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} {% endblock %} diff --git a/resources/views/rules/rule-group/create.twig b/resources/views/rules/rule-group/create.twig index 5cb21384f7..e8243d4d4c 100644 --- a/resources/views/rules/rule-group/create.twig +++ b/resources/views/rules/rule-group/create.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/rules/rule-group/delete.twig b/resources/views/rules/rule-group/delete.twig index 123977b5c1..6690c83efc 100644 --- a/resources/views/rules/rule-group/delete.twig +++ b/resources/views/rules/rule-group/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, ruleGroup) }} diff --git a/resources/views/rules/rule-group/edit.twig b/resources/views/rules/rule-group/edit.twig index 4156879820..e556feabb5 100644 --- a/resources/views/rules/rule-group/edit.twig +++ b/resources/views/rules/rule-group/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, ruleGroup) }} diff --git a/resources/views/rules/rule-group/select-transactions.twig b/resources/views/rules/rule-group/select-transactions.twig index 2dccd88379..90a85ee0d7 100644 --- a/resources/views/rules/rule-group/select-transactions.twig +++ b/resources/views/rules/rule-group/select-transactions.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, ruleGroup) }} diff --git a/resources/views/rules/rule/create.twig b/resources/views/rules/rule/create.twig index 7b1e55736f..15b80d88af 100644 --- a/resources/views/rules/rule/create.twig +++ b/resources/views/rules/rule/create.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, ruleGroup) }} @@ -71,7 +71,7 @@ - {% include '/rules/partials/test-trigger-modal.twig' %} + {% include '/rules/partials/test-trigger-modal' %}
diff --git a/resources/views/rules/rule/delete.twig b/resources/views/rules/rule/delete.twig index 3c37f0da08..0d01f303b8 100644 --- a/resources/views/rules/rule/delete.twig +++ b/resources/views/rules/rule/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, rule) }} diff --git a/resources/views/rules/rule/edit.twig b/resources/views/rules/rule/edit.twig index 69559d895d..1260c34b52 100644 --- a/resources/views/rules/rule/edit.twig +++ b/resources/views/rules/rule/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, rule) }} @@ -69,7 +69,7 @@
- {% include '/rules/partials/test-trigger-modal.twig' %} + {% include '/rules/partials/test-trigger-modal' %}
diff --git a/resources/views/search/index.twig b/resources/views/search/index.twig index 2f9b8afdc5..88107c52d6 100644 --- a/resources/views/search/index.twig +++ b/resources/views/search/index.twig @@ -1,23 +1,39 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, query) }} {% endblock %} {% block content %} - {% if query %} + {% if query == "" %}
+
+

{{ 'no_results_for_empty_search'|_ }}

+
+
+ {% endif %} + {% if query %} + +
+
+

{{ trans('firefly.results_limited', {count: limit}) }}

+
+
+ +
+ + {% if result.transactions|length > 0 %}
-

Transactions

+

{{ 'transactions'|_ }}

-
- {% include 'list.journals-tiny' with {'transactions' : result.transactions} %} -
-
@@ -26,19 +42,13 @@
-

Categories

+

{{ 'categories'|_ }}

-
-
- {% for category in result.categories %} - - {{ category.name }} - - {% endfor %} -
-
-
@@ -47,13 +57,13 @@
-

Tags

+

{{ 'tags'|_ }}

-
-

Bla bla

-
-
@@ -62,19 +72,13 @@
-

Accounts

+

{{ 'accounts'|_ }}

-
-
- {% for account in result.accounts %} - - {{ account.name }} - - {% endfor %} -
-
-
@@ -83,19 +87,13 @@
-

Budgets

+

{{ 'budgets'|_ }}

-
- {% for budget in result.budgets %} - - {{ budget.name }} - - {% endfor %} -
-
-
@@ -106,8 +104,8 @@ {% endblock %} -{% block scripts %} - -{% endblock %} + {% block scripts %} + + {% endblock %} diff --git a/resources/views/search/partials/accounts.twig b/resources/views/search/partials/accounts.twig new file mode 100644 index 0000000000..e21b443d59 --- /dev/null +++ b/resources/views/search/partials/accounts.twig @@ -0,0 +1,34 @@ + + + + + + + + + + + + {% for account in result.accounts %} + + + + + + + + {% endfor %} + + +
{{ trans('list.name') }}
{{ account.name }}{{ trans('firefly.'~account.accountType.type) }}
diff --git a/resources/views/search/partials/budgets.twig b/resources/views/search/partials/budgets.twig new file mode 100644 index 0000000000..ad14e57747 --- /dev/null +++ b/resources/views/search/partials/budgets.twig @@ -0,0 +1,22 @@ + + + + + + + + + {% for budget in result.budgets %} + + + + + {% endfor %} + + +
{{ trans('list.name') }}
{{ budget.name }}
diff --git a/resources/views/search/partials/categories.twig b/resources/views/search/partials/categories.twig new file mode 100644 index 0000000000..4176de3240 --- /dev/null +++ b/resources/views/search/partials/categories.twig @@ -0,0 +1,22 @@ + + + + + + + + + {% for category in result.categories %} + + + + + {% endfor %} + + +
{{ trans('list.name') }}
{{ category.name }}
diff --git a/resources/views/search/partials/tags.twig b/resources/views/search/partials/tags.twig new file mode 100644 index 0000000000..c4b3e21a2c --- /dev/null +++ b/resources/views/search/partials/tags.twig @@ -0,0 +1,24 @@ + + + + + + + + + + {% for tag in result.tags %} + + + + + + {% endfor %} + + +
{{ trans('list.name') }}{{ trans('list.type') }}
{{ tag.tag }}{{ ('tag'~tag.tagMode)|_ }}
diff --git a/resources/views/search/partials/transactions.twig b/resources/views/search/partials/transactions.twig new file mode 100644 index 0000000000..c20da1ff07 --- /dev/null +++ b/resources/views/search/partials/transactions.twig @@ -0,0 +1,81 @@ +{{ journals.render|raw }} + + + + + + + + + + + + {% for transaction in transactions %} + + + + + + + + {% endfor %} + +
{{ trans('list.description') }}{{ trans('list.amount') }}
+ + + {% if transaction.transaction_description|length > 0 %} + {{ transaction.transaction_description }} ({{ transaction.description }}) + {% else %} + {{ transaction.description }} + {% endif %} + + {{ splitJournalIndicator(transaction.journal_id) }} + + {% if transaction.transactionJournal.attachments|length > 0 %} + + {% endif %} + + + + {{ formatAmountWithCode(transaction.transaction_amount, transaction.transaction_currency_code) }} + + {{ optionalJournalAmount(transaction.journal_id, transaction.transaction_amount, + transaction.transaction_currency_code, transaction.transaction_type_type) }} + + +
+ +
+
+ {{ journals.render|raw }} +
+
+ diff --git a/resources/views/tags/create.twig b/resources/views/tags/create.twig index ea9c4e12ac..95b99f7d9d 100644 --- a/resources/views/tags/create.twig +++ b/resources/views/tags/create.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/tags/delete.twig b/resources/views/tags/delete.twig index 966ad7e011..e6cd27f096 100644 --- a/resources/views/tags/delete.twig +++ b/resources/views/tags/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, tag) }} diff --git a/resources/views/tags/edit.twig b/resources/views/tags/edit.twig index 0fc6ec210d..e6a089d269 100644 --- a/resources/views/tags/edit.twig +++ b/resources/views/tags/edit.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, tag) }} diff --git a/resources/views/tags/index.twig b/resources/views/tags/index.twig index 22358ac738..78512e056d 100644 --- a/resources/views/tags/index.twig +++ b/resources/views/tags/index.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} diff --git a/resources/views/tags/show.twig b/resources/views/tags/show.twig index 47c7f5c54b..e78b69f5b1 100644 --- a/resources/views/tags/show.twig +++ b/resources/views/tags/show.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, tag) }} @@ -95,7 +95,7 @@
- {% include 'list/journals.twig' %} + {% include 'list/journals-tasker' %}
diff --git a/resources/views/transactions/convert.twig b/resources/views/transactions/convert.twig index c5c7c95429..40c8429b71 100644 --- a/resources/views/transactions/convert.twig +++ b/resources/views/transactions/convert.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, destinationType, journal) }} diff --git a/resources/views/transactions/create.twig b/resources/views/transactions/create.twig index 78de00ecd4..38c961ccc0 100644 --- a/resources/views/transactions/create.twig +++ b/resources/views/transactions/create.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, what) }} diff --git a/resources/views/transactions/delete.twig b/resources/views/transactions/delete.twig index 0ce9edbec9..69e019d0e8 100644 --- a/resources/views/transactions/delete.twig +++ b/resources/views/transactions/delete.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, journal) }} diff --git a/resources/views/transactions/edit-split.twig b/resources/views/transactions/edit-split.twig index 5f71a30c78..1ceb75baf3 100644 --- a/resources/views/transactions/edit-split.twig +++ b/resources/views/transactions/edit-split.twig @@ -1,4 +1,4 @@ -{% extends "./layout/default.twig" %} +{% extends "./layout/default" %} {% block breadcrumbs %} {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, journal) }} @@ -212,8 +212,6 @@ {% endif %}
{{ trans('list.category') }}
- {% if journal.source_account_type == 'Cash account' %} - (cash) - {% else %} - {{ journal.source_account_name }} - {% endif %} + {{ sourceAccount(journal)|raw }} - {% if journal.destination_account_type == 'Cash account' %} - (cash) - {% else %} - {{ journal.destination_account_name }} - {% endif %} + {{ destinationAccount(journal)|raw }}
+ + + + + + + + + {% for account in accounts %} + + + + + + {% endfor %} + +
{{ 'name'|_ }}{{ 'earned'|_ }}{{ 'spent'|_ }}
+ {{ account.name }} + + {% if accountSummary[account.id] %} + {{ accountSummary[account.id].earned|formatAmount }} + {% else %} + {{ 0|formatAmount }} + {% endif %} + + {% if accountSummary[account.id] %} + {{ accountSummary[account.id].spent|formatAmount }} + {% else %} + {{ 0|formatAmount }} + {% endif %} +
+
+ + +
+
+

{{ 'categories'|_ }}

+
+
+ + + + + + + + + + {% for category in categories %} + + + + + + {% endfor %} + +
{{ 'name'|_ }}{{ 'earned'|_ }}{{ 'spent'|_ }}
+ {{ category.name }} + + {% if categorySummary[category.id] %} + {{ categorySummary[category.id].earned|formatAmount }} + {% else %} + {{ 0|formatAmount }} + {% endif %} + + {% if categorySummary[category.id] %} + {{ categorySummary[category.id].spent|formatAmount }} + {% else %} + {{ 0|formatAmount }} + {% endif %} +
+
+
-
- Pie chart with spending (aka all withdrawals in category). Optional checkbox to include all other transactions. + {% if categories.count > 1 %} +
+
+
+

{{ 'income_per_category'|_ }}

+
+
+ + +
+
-
- Pie chart with income (aka all deposits in category). Optional checkbox to include all other transactions (for comparison). +
+
+
+

{{ 'expense_per_category'|_ }}

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

{{ 'income_per_account'|_ }}

+
+
+
+
+
+
+
+
+

{{ 'expense_per_account'|_ }}

+
+
+
+
+
+ {% endif %} + {#Pie chart with income (aka all deposits in category). Optional checkbox to include all other transactions (for comparison).#}
@@ -59,6 +180,23 @@ {% endblock %} {% block scripts %} + + + + + + + + + {% endblock %} {% block styles %} diff --git a/routes/web.php b/routes/web.php index 464999178f..6be7a49bc5 100755 --- a/routes/web.php +++ b/routes/web.php @@ -204,11 +204,14 @@ Route::group( // categories: Route::get('/chart/category/frontpage', ['uses' => 'Chart\CategoryController@frontpage']); - Route::get('/chart/category/{category}/period', ['uses' => 'Chart\CategoryController@currentPeriod']); Route::get('/chart/category/{category}/period/{date}', ['uses' => 'Chart\CategoryController@specificPeriod']); Route::get('/chart/category/{category}/all', ['uses' => 'Chart\CategoryController@all']); + // these charts are used in reports: + Route::get('/chart/category/{accountList}/{categoryList}/{start_date}/{end_date}/{others}/income', ['uses' => 'Chart\CategoryController@incomePieChart']); + Route::get('/chart/category/{accountList}/{categoryList}/{start_date}/{end_date}/{others}/expense', ['uses' => 'Chart\CategoryController@expensePieChart']); + // piggy banks: Route::get('/chart/piggy-bank/{piggyBank}', ['uses' => 'Chart\PiggyBankController@history']); From 04515da0bcf4b269b475651dc03fa95381bbb673 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 12 Nov 2016 07:02:32 +0100 Subject: [PATCH 080/709] Fixed the charts --- .../Report/Category/MonthReportGenerator.php | 108 +++++++++++------- .../Controllers/Chart/CategoryController.php | 77 ++++++++++--- 2 files changed, 128 insertions(+), 57 deletions(-) diff --git a/app/Generator/Report/Category/MonthReportGenerator.php b/app/Generator/Report/Category/MonthReportGenerator.php index 623d65fef1..298d1ca278 100644 --- a/app/Generator/Report/Category/MonthReportGenerator.php +++ b/app/Generator/Report/Category/MonthReportGenerator.php @@ -38,6 +38,68 @@ class MonthReportGenerator implements ReportGeneratorInterface /** @var Carbon */ private $start; + /** + * @param Collection $collection + * @param array $accounts + * + * @return Collection + */ + public static function filterExpenses(Collection $collection, array $accounts): Collection + { + $result = $collection->filter( + function (Transaction $transaction) use ($accounts) { + $opposing = $transaction->opposing_account_id; + // remove internal transfer + if (in_array($opposing, $accounts)) { + Log::debug(sprintf('Filtered #%d because its opposite is in accounts.', $transaction->id)); + + return null; + } + // remove positive amount + if (bccomp($transaction->transaction_amount, '0') === 1) { + Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount)); + + return null; + } + + return $transaction; + } + ); + + return $result; + } + + /** + * @param Collection $collection + * @param array $accounts + * + * @return Collection + */ + public static function filterIncome(Collection $collection, array $accounts): Collection + { + $result = $collection->filter( + function (Transaction $transaction) use ($accounts) { + $opposing = $transaction->opposing_account_id; + // remove internal transfer + if (in_array($opposing, $accounts)) { + Log::debug(sprintf('Filtered #%d because its opposite is in accounts.', $transaction->id)); + + return null; + } + // remove positive amount + if (bccomp($transaction->transaction_amount, '0') === -1) { + Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount)); + + return null; + } + + return $transaction; + } + ); + + return $result; + } + /** * @return string */ @@ -186,7 +248,7 @@ class MonthReportGenerator implements ReportGeneratorInterface */ private function getEarnedAccountSummary(): array { - $transactions = $this->getIncomes(); + $transactions = $this->getIncome(); $result = []; /** @var Transaction $transaction */ foreach ($transactions as $transaction) { @@ -203,7 +265,7 @@ class MonthReportGenerator implements ReportGeneratorInterface */ private function getEarnedCategorySummary(): array { - $transactions = $this->getIncomes(); + $transactions = $this->getIncome(); $result = []; /** @var Transaction $transaction */ foreach ($transactions as $transaction) { @@ -230,26 +292,8 @@ class MonthReportGenerator implements ReportGeneratorInterface $accountIds = $this->accounts->pluck('id')->toArray(); $transactions = $collector->getJournals(); + $transactions = self::filterExpenses($transactions, $accountIds); - $transactions = $transactions->filter( - function (Transaction $transaction) use ($accountIds) { - $opposing = $transaction->opposing_account_id; - // remove internal transfer - if (in_array($opposing, $accountIds)) { - Log::debug(sprintf('Filtered #%d because its opposite is in accounts.', $transaction->id)); - - return null; - } - // remove positive amount - if (bccomp($transaction->transaction_amount, '0') === 1) { - Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount)); - - return null; - } - - return $transaction; - } - ); return $transactions; } @@ -257,7 +301,7 @@ class MonthReportGenerator implements ReportGeneratorInterface /** * @return Collection */ - private function getIncomes(): Collection + private function getIncome(): Collection { $collector = new JournalCollector(auth()->user()); $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) @@ -265,25 +309,7 @@ class MonthReportGenerator implements ReportGeneratorInterface ->setCategories($this->categories)->getOpposingAccount(); $accountIds = $this->accounts->pluck('id')->toArray(); $transactions = $collector->getJournals(); - $transactions = $transactions->filter( - function (Transaction $transaction) use ($accountIds) { - $opposing = $transaction->opposing_account_id; - // remove internal transfer - if (in_array($opposing, $accountIds)) { - Log::debug(sprintf('Filtered #%d because its opposite is in accounts.', $transaction->id)); - - return null; - } - // remove positive amount - if (bccomp($transaction->transaction_amount, '0') === -1) { - Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount)); - - return null; - } - - return $transaction; - } - ); + $transactions = self::filterIncome($transactions, $accountIds); return $transactions; } diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index d38be5f4db..45741349b0 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -16,10 +16,12 @@ namespace FireflyIII\Http\Controllers\Chart; use Carbon\Carbon; use FireflyIII\Generator\Chart\Category\CategoryChartGeneratorInterface; +use FireflyIII\Generator\Report\Category\MonthReportGenerator; use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\AccountType; use FireflyIII\Models\Category; +use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; @@ -124,29 +126,50 @@ class CategoryController extends Controller { /** @var CategoryRepositoryInterface $repository */ $repository = app(CategoryRepositoryInterface::class); - $others = intval($others) === 1; - $names = []; - $collector = new JournalCollector(auth()->user()); - $collector->setAccounts($accounts)->setRange($start, $end) - ->setTypes([TransactionType::WITHDRAWAL]) - ->setCategories($categories); - $set = $collector->getSumPerCategory(); + /** @var bool $others */ + $others = intval($others) === 1; + $names = []; + + // collect journals (just like the category report does): + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) + ->setCategories($categories)->getOpposingAccount()->disableFilter(); + $accountIds = $accounts->pluck('id')->toArray(); + $transactions = $collector->getJournals(); + $set = MonthReportGenerator::filterExpenses($transactions, $accountIds); + + // group by category ID: + $grouped = []; + /** @var Transaction $transaction */ + foreach ($set as $transaction) { + $jrnlCatId = intval($transaction->transaction_journal_category_id); + $transCatId = intval($transaction->transaction_category_id); + $categoryId = max($jrnlCatId, $transCatId); + + $grouped[$categoryId] = $grouped[$categoryId] ?? '0'; + $amount = bcmul($transaction->transaction_amount, '-1'); + $grouped[$categoryId] = bcadd($amount, $grouped[$categoryId]); + } + + // loop and show the grouped results: $result = []; $total = '0'; - foreach ($set as $categoryId => $amount) { + foreach ($grouped as $categoryId => $amount) { if (!isset($names[$categoryId])) { $category = $repository->find(intval($categoryId)); $names[$categoryId] = $category->name; } - $amount = bcmul($amount, '-1'); $total = bcadd($total, $amount); $result[] = ['name' => $names[$categoryId], 'id' => $categoryId, 'amount' => $amount]; } + // also collect others? + // TODO include transfers if ($others) { $collector = new JournalCollector(auth()->user()); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]); - $sum = bcmul($collector->getSum(), '-1'); + $journals = $collector->getJournals(); + $sum = bcmul(strval($journals->sum('transaction_amount')), '-1'); $sum = bcsub($sum, $total); $result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum]; } @@ -214,14 +237,33 @@ class CategoryController extends Controller /** @var CategoryRepositoryInterface $repository */ $repository = app(CategoryRepositoryInterface::class); /** @var bool $others */ - $others = intval($others) === 1; - $names = []; + $others = intval($others) === 1; + $names = []; + + // collect journals (just like the category report does): $collector = new JournalCollector(auth()->user()); - $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setCategories($categories); - $set = $collector->getSumPerCategory(); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) + ->setCategories($categories)->getOpposingAccount(); + $accountIds = $accounts->pluck('id')->toArray(); + $transactions = $collector->getJournals(); + $set = MonthReportGenerator::filterIncome($transactions, $accountIds); + + // group by category ID: + $grouped = []; + /** @var Transaction $transaction */ + foreach ($set as $transaction) { + $jrnlCatId = intval($transaction->transaction_journal_category_id); + $transCatId = intval($transaction->transaction_category_id); + $categoryId = max($jrnlCatId, $transCatId); + + $grouped[$categoryId] = $grouped[$categoryId] ?? '0'; + $grouped[$categoryId] = bcadd($transaction->transaction_amount, $grouped[$categoryId]); + } + + // loop and show the grouped results: $result = []; $total = '0'; - foreach ($set as $categoryId => $amount) { + foreach ($grouped as $categoryId => $amount) { if (!isset($names[$categoryId])) { $category = $repository->find(intval($categoryId)); $names[$categoryId] = $category->name; @@ -230,10 +272,13 @@ class CategoryController extends Controller $result[] = ['name' => $names[$categoryId], 'id' => $categoryId, 'amount' => $amount]; } + // also collect others? + // TODO include transfers if ($others) { $collector = new JournalCollector(auth()->user()); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]); - $sum = $collector->getSum(); + $journals = $collector->getJournals(); + $sum = strval($journals->sum('transaction_amount')); $sum = bcsub($sum, $total); $result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum]; } From a294f757ff75ae9f8513361f3cb5b06b68802f0a Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 12 Nov 2016 10:14:20 +0100 Subject: [PATCH 081/709] Fixes all charts in future category report. --- .../ChartJsCategoryChartGenerator.php | 29 +- .../Report/Category/MonthReportGenerator.php | 64 +--- app/Generator/Report/Category/Support.php | 91 +++++ .../Controllers/Chart/CategoryController.php | 137 +------ .../Chart/CategoryReportController.php | 360 ++++++++++++++++++ public/js/ff/reports/category/month.js | 58 +-- resources/views/reports/category/month.twig | 8 + routes/web.php | 13 +- 8 files changed, 522 insertions(+), 238 deletions(-) create mode 100644 app/Generator/Report/Category/Support.php create mode 100644 app/Http/Controllers/Chart/CategoryReportController.php diff --git a/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php b/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php index 9e6221f24b..eb03606c2c 100644 --- a/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php +++ b/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php @@ -118,6 +118,18 @@ class ChartJsCategoryChartGenerator implements CategoryChartGeneratorInterface return $data; } + /** + * + * @param Collection $entries + * + * @return array + */ + public function period(Collection $entries): array + { + return $this->all($entries); + + } + /** * @param array $entries * @@ -133,6 +145,11 @@ class ChartJsCategoryChartGenerator implements CategoryChartGeneratorInterface ]; $index = 0; foreach ($entries as $entry) { + + if (bccomp($entry['amount'], '0') === -1) { + $entry['amount'] = bcmul($entry['amount'], '-1'); + } + $data['datasets'][0]['data'][] = round($entry['amount'], 2); $data['datasets'][0]['backgroundColor'][] = ChartColour::getColour($index); $data['labels'][] = $entry['name']; @@ -142,18 +159,6 @@ class ChartJsCategoryChartGenerator implements CategoryChartGeneratorInterface return $data; } - /** - * - * @param Collection $entries - * - * @return array - */ - public function period(Collection $entries): array - { - return $this->all($entries); - - } - /** * @param Collection $categories * @param Collection $entries diff --git a/app/Generator/Report/Category/MonthReportGenerator.php b/app/Generator/Report/Category/MonthReportGenerator.php index 298d1ca278..09e5fc31e1 100644 --- a/app/Generator/Report/Category/MonthReportGenerator.php +++ b/app/Generator/Report/Category/MonthReportGenerator.php @@ -27,7 +27,7 @@ use Log; * * @package FireflyIII\Generator\Report\Category */ -class MonthReportGenerator implements ReportGeneratorInterface +class MonthReportGenerator extends Support implements ReportGeneratorInterface { /** @var Collection */ private $accounts; @@ -38,68 +38,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** @var Carbon */ private $start; - /** - * @param Collection $collection - * @param array $accounts - * - * @return Collection - */ - public static function filterExpenses(Collection $collection, array $accounts): Collection - { - $result = $collection->filter( - function (Transaction $transaction) use ($accounts) { - $opposing = $transaction->opposing_account_id; - // remove internal transfer - if (in_array($opposing, $accounts)) { - Log::debug(sprintf('Filtered #%d because its opposite is in accounts.', $transaction->id)); - - return null; - } - // remove positive amount - if (bccomp($transaction->transaction_amount, '0') === 1) { - Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount)); - - return null; - } - - return $transaction; - } - ); - - return $result; - } - - /** - * @param Collection $collection - * @param array $accounts - * - * @return Collection - */ - public static function filterIncome(Collection $collection, array $accounts): Collection - { - $result = $collection->filter( - function (Transaction $transaction) use ($accounts) { - $opposing = $transaction->opposing_account_id; - // remove internal transfer - if (in_array($opposing, $accounts)) { - Log::debug(sprintf('Filtered #%d because its opposite is in accounts.', $transaction->id)); - - return null; - } - // remove positive amount - if (bccomp($transaction->transaction_amount, '0') === -1) { - Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount)); - - return null; - } - - return $transaction; - } - ); - - return $result; - } - /** * @return string */ diff --git a/app/Generator/Report/Category/Support.php b/app/Generator/Report/Category/Support.php new file mode 100644 index 0000000000..f568cd4282 --- /dev/null +++ b/app/Generator/Report/Category/Support.php @@ -0,0 +1,91 @@ +filter( + function (Transaction $transaction) use ($accounts) { + $opposing = $transaction->opposing_account_id; + // remove internal transfer + if (in_array($opposing, $accounts)) { + Log::debug(sprintf('Filtered #%d because its opposite is in accounts.', $transaction->id)); + + return null; + } + // remove positive amount + if (bccomp($transaction->transaction_amount, '0') === 1) { + Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount)); + + return null; + } + + return $transaction; + } + ); + + return $result; + } + + /** + * @param Collection $collection + * @param array $accounts + * + * @return Collection + */ + public static function filterIncome(Collection $collection, array $accounts): Collection + { + $result = $collection->filter( + function (Transaction $transaction) use ($accounts) { + $opposing = $transaction->opposing_account_id; + // remove internal transfer + if (in_array($opposing, $accounts)) { + Log::debug(sprintf('Filtered #%d because its opposite is in accounts.', $transaction->id)); + + return null; + } + // remove positive amount + if (bccomp($transaction->transaction_amount, '0') === -1) { + Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount)); + + return null; + } + + return $transaction; + } + ); + + return $result; + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 45741349b0..02645460dc 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -16,15 +16,10 @@ namespace FireflyIII\Http\Controllers\Chart; use Carbon\Carbon; use FireflyIII\Generator\Chart\Category\CategoryChartGeneratorInterface; -use FireflyIII\Generator\Report\Category\MonthReportGenerator; -use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\AccountType; use FireflyIII\Models\Category; -use FireflyIII\Models\Transaction; -use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; @@ -113,72 +108,6 @@ class CategoryController extends Controller return Response::json($data); } - /** - * @param Collection $accounts - * @param Collection $categories - * @param Carbon $start - * @param Carbon $end - * @param string $others - * - * @return \Illuminate\Http\JsonResponse - */ - public function expensePieChart(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others) - { - /** @var CategoryRepositoryInterface $repository */ - $repository = app(CategoryRepositoryInterface::class); - /** @var bool $others */ - $others = intval($others) === 1; - $names = []; - - // collect journals (just like the category report does): - $collector = new JournalCollector(auth()->user()); - $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) - ->setCategories($categories)->getOpposingAccount()->disableFilter(); - $accountIds = $accounts->pluck('id')->toArray(); - $transactions = $collector->getJournals(); - $set = MonthReportGenerator::filterExpenses($transactions, $accountIds); - - // group by category ID: - $grouped = []; - /** @var Transaction $transaction */ - foreach ($set as $transaction) { - $jrnlCatId = intval($transaction->transaction_journal_category_id); - $transCatId = intval($transaction->transaction_category_id); - $categoryId = max($jrnlCatId, $transCatId); - - $grouped[$categoryId] = $grouped[$categoryId] ?? '0'; - $amount = bcmul($transaction->transaction_amount, '-1'); - $grouped[$categoryId] = bcadd($amount, $grouped[$categoryId]); - } - - // loop and show the grouped results: - $result = []; - $total = '0'; - foreach ($grouped as $categoryId => $amount) { - if (!isset($names[$categoryId])) { - $category = $repository->find(intval($categoryId)); - $names[$categoryId] = $category->name; - } - $total = bcadd($total, $amount); - $result[] = ['name' => $names[$categoryId], 'id' => $categoryId, 'amount' => $amount]; - } - - // also collect others? - // TODO include transfers - if ($others) { - $collector = new JournalCollector(auth()->user()); - $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]); - $journals = $collector->getJournals(); - $sum = bcmul(strval($journals->sum('transaction_amount')), '-1'); - $sum = bcsub($sum, $total); - $result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum]; - } - - $data = $this->generator->pieChart($result); - - return Response::json($data); - } - /** * @param CRI $repository * @param AccountRepositoryInterface $accountRepository @@ -223,71 +152,6 @@ class CategoryController extends Controller } - /** - * @param Collection $accounts - * @param Collection $categories - * @param Carbon $start - * @param Carbon $end - * @param string $others - * - * @return \Illuminate\Http\JsonResponse - */ - public function incomePieChart(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others) - { - /** @var CategoryRepositoryInterface $repository */ - $repository = app(CategoryRepositoryInterface::class); - /** @var bool $others */ - $others = intval($others) === 1; - $names = []; - - // collect journals (just like the category report does): - $collector = new JournalCollector(auth()->user()); - $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) - ->setCategories($categories)->getOpposingAccount(); - $accountIds = $accounts->pluck('id')->toArray(); - $transactions = $collector->getJournals(); - $set = MonthReportGenerator::filterIncome($transactions, $accountIds); - - // group by category ID: - $grouped = []; - /** @var Transaction $transaction */ - foreach ($set as $transaction) { - $jrnlCatId = intval($transaction->transaction_journal_category_id); - $transCatId = intval($transaction->transaction_category_id); - $categoryId = max($jrnlCatId, $transCatId); - - $grouped[$categoryId] = $grouped[$categoryId] ?? '0'; - $grouped[$categoryId] = bcadd($transaction->transaction_amount, $grouped[$categoryId]); - } - - // loop and show the grouped results: - $result = []; - $total = '0'; - foreach ($grouped as $categoryId => $amount) { - if (!isset($names[$categoryId])) { - $category = $repository->find(intval($categoryId)); - $names[$categoryId] = $category->name; - } - $total = bcadd($total, $amount); - $result[] = ['name' => $names[$categoryId], 'id' => $categoryId, 'amount' => $amount]; - } - - // also collect others? - // TODO include transfers - if ($others) { - $collector = new JournalCollector(auth()->user()); - $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]); - $journals = $collector->getJournals(); - $sum = strval($journals->sum('transaction_amount')); - $sum = bcsub($sum, $total); - $result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum]; - } - - $data = $this->generator->pieChart($result); - - return Response::json($data); - } - /** * @param CRI $repository * @param Category $category @@ -307,6 +171,7 @@ class CategoryController extends Controller return Response::json($data); } + /** * @param CRI $repository * @param Category $category diff --git a/app/Http/Controllers/Chart/CategoryReportController.php b/app/Http/Controllers/Chart/CategoryReportController.php new file mode 100644 index 0000000000..b19d03c156 --- /dev/null +++ b/app/Http/Controllers/Chart/CategoryReportController.php @@ -0,0 +1,360 @@ +middleware( + function ($request, $next) { + $this->generator = app(CategoryChartGeneratorInterface::class); + $this->categoryRepository = app(CategoryRepositoryInterface::class); + $this->accountRepository = app(AccountRepositoryInterface::class); + + return $next($request); + } + ); + } + + /** + * @param Collection $accounts + * @param Collection $categories + * @param Carbon $start + * @param Carbon $end + * @param string $others + * + * @return \Illuminate\Http\JsonResponse + */ + public function accountExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others) + { + /** @var bool $others */ + $others = intval($others) === 1; + $names = []; + + // collect journals (just like the category report does): + $set = $this->getExpenses($accounts, $categories, $start, $end); + $grouped = $this->groupByOpposingAccount($set); + + // show the grouped results: + $result = []; + $total = '0'; + foreach ($grouped as $accountId => $amount) { + if (!isset($names[$accountId])) { + $account = $this->accountRepository->find(intval($accountId)); + $names[$accountId] = $account->name; + } + $amount = bcmul($amount, '-1'); + $total = bcadd($total, $amount); + $result[] = ['name' => $names[$accountId], 'id' => $accountId, 'amount' => $amount]; + } + + // also collect all transactions NOT in these categories. + // TODO include transfers + if ($others) { + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]); + $journals = $collector->getJournals(); + $sum = strval($journals->sum('transaction_amount')); + $sum = bcmul($sum, '-1'); + Log::debug(sprintf('Sum of others in accountExpense is %f', $sum)); + $sum = bcsub($sum, $total); + $result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum]; + } + + $data = $this->generator->pieChart($result); + + return Response::json($data); + } + + /** + * @param Collection $accounts + * @param Collection $categories + * @param Carbon $start + * @param Carbon $end + * @param string $others + * + * @return \Illuminate\Http\JsonResponse + */ + public function accountIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others) + { + /** @var bool $others */ + $others = intval($others) === 1; + $names = []; + + // collect journals (just like the category report does): + $set = $this->getIncome($accounts, $categories, $start, $end); + $grouped = $this->groupByOpposingAccount($set); + + // loop and show the grouped results: + $result = []; + $total = '0'; + foreach ($grouped as $accountId => $amount) { + if (!isset($names[$accountId])) { + $account = $this->accountRepository->find(intval($accountId)); + $names[$accountId] = $account->name; + } + $total = bcadd($total, $amount); + $result[] = ['name' => $names[$accountId], 'id' => $accountId, 'amount' => $amount]; + } + + // also collect others? + // TODO include transfers + if ($others) { + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]); + $journals = $collector->getJournals(); + $sum = strval($journals->sum('transaction_amount')); + Log::debug(sprintf('Sum of others in accountIncome is %f', $sum)); + $sum = bcsub($sum, $total); + $result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum]; + } + + $data = $this->generator->pieChart($result); + + return Response::json($data); + } + + /** + * @param Collection $accounts + * @param Collection $categories + * @param Carbon $start + * @param Carbon $end + * @param string $others + * + * @return \Illuminate\Http\JsonResponse + */ + public function categoryExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others) + { + /** @var bool $others */ + $others = intval($others) === 1; + $names = []; + + // collect journals (just like the category report does): + $set = $this->getExpenses($accounts, $categories, $start, $end); + $grouped = $this->groupByCategory($set); + + // show the grouped results: + $result = []; + $total = '0'; + foreach ($grouped as $categoryId => $amount) { + if (!isset($names[$categoryId])) { + $category = $this->categoryRepository->find(intval($categoryId)); + $names[$categoryId] = $category->name; + } + $amount = bcmul($amount, '-1'); + $total = bcadd($total, $amount); + $result[] = ['name' => $names[$categoryId], 'id' => $categoryId, 'amount' => $amount]; + } + + // also collect all transactions NOT in these categories. + // TODO include transfers + if ($others) { + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]); + $journals = $collector->getJournals(); + $sum = strval($journals->sum('transaction_amount')); + $sum = bcmul($sum, '-1'); + Log::debug(sprintf('Sum of others in categoryExpense is %f', $sum)); + $sum = bcsub($sum, $total); + $result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum]; + } + + $data = $this->generator->pieChart($result); + + return Response::json($data); + } + + /** + * @param Collection $accounts + * @param Collection $categories + * @param Carbon $start + * @param Carbon $end + * @param string $others + * + * @return \Illuminate\Http\JsonResponse + */ + public function categoryIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others) + { + /** @var bool $others */ + $others = intval($others) === 1; + $names = []; + + // collect journals (just like the category report does): + $set = $this->getIncome($accounts, $categories, $start, $end); + $grouped = $this->groupByCategory($set); + + // loop and show the grouped results: + $result = []; + $total = '0'; + foreach ($grouped as $categoryId => $amount) { + if (!isset($names[$categoryId])) { + $category = $this->categoryRepository->find(intval($categoryId)); + $names[$categoryId] = $category->name; + } + $total = bcadd($total, $amount); + $result[] = ['name' => $names[$categoryId], 'id' => $categoryId, 'amount' => $amount]; + } + + // also collect others? + // TODO include transfers + if ($others) { + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]); + $journals = $collector->getJournals(); + $sum = strval($journals->sum('transaction_amount')); + Log::debug(sprintf('Sum of others in categoryIncome is %f', $sum)); + $sum = bcsub($sum, $total); + $result[] = ['name' => trans('firefly.everything_else'), 'id' => 0, 'amount' => $sum]; + } + + $data = $this->generator->pieChart($result); + + return Response::json($data); + } + + /** + * @param Collection $accounts + * @param Collection $categories + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + private function getExpenses(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): Collection + { + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) + ->setCategories($categories)->getOpposingAccount()->disableFilter(); + $accountIds = $accounts->pluck('id')->toArray(); + $transactions = $collector->getJournals(); + $set = MonthReportGenerator::filterExpenses($transactions, $accountIds); + + return $set; + } + + /** + * @param Collection $accounts + * @param Collection $categories + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + private function getIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): Collection + { + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) + ->setCategories($categories)->getOpposingAccount(); + $accountIds = $accounts->pluck('id')->toArray(); + $transactions = $collector->getJournals(); + $set = MonthReportGenerator::filterIncome($transactions, $accountIds); + + return $set; + } + + /** + * @param Collection $set + * + * @return array + */ + private function groupByAccount(Collection $set): array + { + // group by category ID: + $grouped = []; + /** @var Transaction $transaction */ + foreach ($set as $transaction) { + $accountId = $transaction->account_id; + $grouped[$accountId] = $grouped[$accountId] ?? '0'; + $grouped[$accountId] = bcadd($transaction->transaction_amount, $grouped[$accountId]); + } + + return $grouped; + } + + /** + * @param Collection $set + * + * @return array + */ + private function groupByCategory(Collection $set): array + { + // group by category ID: + $grouped = []; + /** @var Transaction $transaction */ + foreach ($set as $transaction) { + $jrnlCatId = intval($transaction->transaction_journal_category_id); + $transCatId = intval($transaction->transaction_category_id); + $categoryId = max($jrnlCatId, $transCatId); + $grouped[$categoryId] = $grouped[$categoryId] ?? '0'; + $grouped[$categoryId] = bcadd($transaction->transaction_amount, $grouped[$categoryId]); + } + + return $grouped; + } + + /** + * @param Collection $set + * + * @return array + */ + private function groupByOpposingAccount(Collection $set): array + { + $grouped = []; + /** @var Transaction $transaction */ + foreach ($set as $transaction) { + $accountId = $transaction->opposing_account_id; + $grouped[$accountId] = $grouped[$accountId] ?? '0'; + $grouped[$accountId] = bcadd($transaction->transaction_amount, $grouped[$accountId]); + } + + return $grouped; + } +} \ No newline at end of file diff --git a/public/js/ff/reports/category/month.js b/public/js/ff/reports/category/month.js index 250ca75b53..46bbf5cb47 100644 --- a/public/js/ff/reports/category/month.js +++ b/public/js/ff/reports/category/month.js @@ -8,11 +8,33 @@ * See the LICENSE file for details. */ +// it's hard coded, but what you're gonna do? +var catInUri = 'chart/category/' + accountIds + '/' + categoryIds + '/' + startDate + '/' + endDate + '/OTHERS/income'; +var catOutUri = 'chart/category/' + accountIds + '/' + categoryIds + '/' + startDate + '/' + endDate + '/OTHERS/expense'; +var accInUri = 'chart/account/' + accountIds + '/' + categoryIds + '/' + startDate + '/' + endDate + '/OTHERS/income'; +var accOutUri = 'chart/account/' + accountIds + '/' + categoryIds + '/' + startDate + '/' + endDate + '/OTHERS/expense'; + + $(function () { "use strict"; drawChart(); - $('#categories-in-pie-chart-checked').on('change', redrawCatInPie); - $('#categories-out-pie-chart-checked').on('change', redrawCatOutPie); + + $('#categories-in-pie-chart-checked').on('change', function () { + redrawPieChart('categories-in-pie-chart', catInUri); + }); + + $('#categories-out-pie-chart-checked').on('change', function () { + redrawPieChart('categories-out-pie-chart', catOutUri); + }); + + $('#accounts-in-pie-chart-checked').on('change', function () { + redrawPieChart('accounts-in-pie-chart', accInUri); + }); + + $('#accounts-out-pie-chart-checked').on('change', function () { + redrawPieChart('accounts-out-pie-chart', accOutUri); + }); + }); @@ -22,36 +44,24 @@ function drawChart() { // month view: // draw pie chart of income, depending on "show other transactions too": - redrawCatInPie(); - redrawCatOutPie(); + redrawPieChart('categories-in-pie-chart', catInUri); + redrawPieChart('categories-out-pie-chart', catOutUri); + redrawPieChart('accounts-in-pie-chart', accInUri); + redrawPieChart('accounts-out-pie-chart', accOutUri); } -function redrawCatOutPie() { +function redrawPieChart(container, uri) { "use strict"; - var checkbox = $('#categories-out-pie-chart-checked'); - var container = 'categories-out-pie-chart'; + var checkbox = $('#' + container + '-checked'); - // var others = '0'; // check if box is checked: if (checkbox.prop('checked')) { others = '1'; } + uri = uri.replace('OTHERS', others); + console.log('URI for ' + container + ' is ' + uri); + + pieChart(uri, container); - pieChart('chart/category/' + accountIds + '/' + categoryIds + '/' + startDate + '/' + endDate + '/' + others + '/expense', container); } - -function redrawCatInPie() { - "use strict"; - var checkbox = $('#categories-in-pie-chart-checked'); - var container = 'categories-in-pie-chart'; - - // - var others = '0'; - // check if box is checked: - if (checkbox.prop('checked')) { - others = '1'; - } - - pieChart('chart/category/' + accountIds + '/' + categoryIds + '/' + startDate + '/' + endDate + '/' + others + '/income', container); -} \ No newline at end of file diff --git a/resources/views/reports/category/month.twig b/resources/views/reports/category/month.twig index 71ac4b5921..9308f9f440 100644 --- a/resources/views/reports/category/month.twig +++ b/resources/views/reports/category/month.twig @@ -123,6 +123,10 @@

{{ 'income_per_account'|_ }}

+ +
@@ -132,6 +136,10 @@

{{ 'expense_per_account'|_ }}

+ +
diff --git a/routes/web.php b/routes/web.php index 6be7a49bc5..795b82a17d 100755 --- a/routes/web.php +++ b/routes/web.php @@ -208,9 +208,16 @@ Route::group( Route::get('/chart/category/{category}/period/{date}', ['uses' => 'Chart\CategoryController@specificPeriod']); Route::get('/chart/category/{category}/all', ['uses' => 'Chart\CategoryController@all']); - // these charts are used in reports: - Route::get('/chart/category/{accountList}/{categoryList}/{start_date}/{end_date}/{others}/income', ['uses' => 'Chart\CategoryController@incomePieChart']); - Route::get('/chart/category/{accountList}/{categoryList}/{start_date}/{end_date}/{others}/expense', ['uses' => 'Chart\CategoryController@expensePieChart']); + // these charts are used in reports (category reports): + Route::get('/chart/category/{accountList}/{categoryList}/{start_date}/{end_date}/{others}/income', + ['uses' => 'Chart\CategoryReportController@categoryIncome']); + Route::get('/chart/category/{accountList}/{categoryList}/{start_date}/{end_date}/{others}/expense', + ['uses' => 'Chart\CategoryReportController@categoryExpense']); + + Route::get('/chart/account/{accountList}/{categoryList}/{start_date}/{end_date}/{others}/income', + ['uses' => 'Chart\CategoryReportController@accountIncome']); + Route::get('/chart/account/{accountList}/{categoryList}/{start_date}/{end_date}/{others}/expense', + ['uses' => 'Chart\CategoryReportController@accountExpense']); // piggy banks: Route::get('/chart/piggy-bank/{piggyBank}', ['uses' => 'Chart\PiggyBankController@history']); From c5d2fabfec8e01ba3af6d0756f4e006f7ec74cab Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 12 Nov 2016 12:12:11 +0100 Subject: [PATCH 082/709] Optimize some views for category report --- .../Report/Category/MonthReportGenerator.php | 2 -- public/js/ff/reports/index.js | 25 ++++++++++++++++--- resources/lang/en_US/firefly.php | 1 + resources/views/reports/options/category.twig | 2 +- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/app/Generator/Report/Category/MonthReportGenerator.php b/app/Generator/Report/Category/MonthReportGenerator.php index 09e5fc31e1..6420f24078 100644 --- a/app/Generator/Report/Category/MonthReportGenerator.php +++ b/app/Generator/Report/Category/MonthReportGenerator.php @@ -20,7 +20,6 @@ use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionType; use Illuminate\Support\Collection; -use Log; /** * Class MonthReportGenerator @@ -232,7 +231,6 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface $transactions = $collector->getJournals(); $transactions = self::filterExpenses($transactions, $accountIds); - return $transactions; } diff --git a/public/js/ff/reports/index.js b/public/js/ff/reports/index.js index 5df1bd03cc..9eb918d86a 100644 --- a/public/js/ff/reports/index.js +++ b/public/js/ff/reports/index.js @@ -17,7 +17,6 @@ $(function () { } ); - // set values from cookies, if any: if (readCookie('report-type') !== null) { $('select[name="report_type"]').val(readCookie('report-type')); @@ -26,7 +25,7 @@ $(function () { if ((readCookie('report-accounts') !== null)) { var arr = readCookie('report-accounts').split(','); arr.forEach(function (val) { - $('input[type="checkbox"][value="' + val + '"]').prop('checked', true); + $('input[class="account-checkbox"][type="checkbox"][value="' + val + '"]').prop('checked', true); }); } @@ -55,13 +54,24 @@ function getReportOptions() { $('#extra-options').empty(); $('#extra-options').addClass('loading'); console.log('Changed report type to ' + reportType); + $.getJSON('reports/options/' + reportType, function (data) { $('#extra-options').removeClass('loading').html(data.html); + setOptionalFromCookies(); }).fail(function () { $('#extra-options').removeClass('loading').addClass('error'); }); } +function setOptionalFromCookies() { + if ((readCookie('report-categories') !== null)) { + var arr = readCookie('report-categories').split(','); + arr.forEach(function (val) { + $('input[class="category-checkbox"][type="checkbox"][value="' + val + '"]').prop('checked', true); + }); + } +} + function catchSubmit() { "use strict"; // date, processed: @@ -78,7 +88,15 @@ function catchSubmit() { } }); - // all category ids to come + // all category ids: + //category-checkbox + var categories = []; + $.each($('.category-checkbox'), function (i, v) { + var c = $(v); + if (c.prop('checked')) { + categories.push(c.val()); + } + }); // remember all @@ -86,6 +104,7 @@ function catchSubmit() { // 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-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 0700a0f643..2cc9fae536 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -669,6 +669,7 @@ return [ 'report_type' => 'Report type', 'report_type_default' => 'Default financial report', 'report_type_audit' => 'Transaction history overview (audit)', + 'report_type_category' => 'Category 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', diff --git a/resources/views/reports/options/category.twig b/resources/views/reports/options/category.twig index 19ab935805..1487c42ed0 100644 --- a/resources/views/reports/options/category.twig +++ b/resources/views/reports/options/category.twig @@ -3,6 +3,6 @@

{% for category in categories %} {% endfor %} From 85b3c4683b37c0b42922e97a82a9b0e417ffa6d5 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 12 Nov 2016 12:23:55 +0100 Subject: [PATCH 083/709] Fix redraw bug in category report. --- public/js/ff/charts.js | 4 ++- resources/lang/en_US/firefly.php | 7 ++++ resources/views/reports/category/month.twig | 37 +++++++++++---------- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/public/js/ff/charts.js b/public/js/ff/charts.js index 07ea8ca68c..6478a8bf5e 100644 --- a/public/js/ff/charts.js +++ b/public/js/ff/charts.js @@ -91,7 +91,9 @@ var defaultPieOptions = { return data.labels[tooltipItem.index] + ': ' + accounting.formatMoney(value); } } - } + }, + maintainAspectRatio: true, + responsive: true }; diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 2cc9fae536..058a239131 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -624,6 +624,7 @@ return [ // reports: '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', 'quick_link_reports' => 'Quick links', 'quick_link_default_report' => 'Default financial report', 'quick_link_audit_report' => 'Transaction history overview', @@ -689,6 +690,12 @@ 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.', + 'income_per_category' => 'Income per category', + 'expense_per_category' => 'Expense per category', + 'income_per_account' => 'Income per account', + 'expense_per_account' => 'Expense per account', + 'include_not_in_category' => 'Include transactions not selected for this report', + 'everything_else' => 'Everything else', // charts: 'chart' => 'Chart', diff --git a/resources/views/reports/category/month.twig b/resources/views/reports/category/month.twig index 9308f9f440..4a7ca59700 100644 --- a/resources/views/reports/category/month.twig +++ b/resources/views/reports/category/month.twig @@ -7,7 +7,7 @@ {% block content %}
-
+

{{ 'accounts'|_ }}

@@ -89,66 +89,67 @@
{% if categories.count > 1 %} -
+

{{ 'income_per_category'|_ }}

- -
-
+

{{ 'expense_per_category'|_ }}

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

{{ 'income_per_account'|_ }}

- -
-
+

{{ 'expense_per_account'|_ }}

- -
{% endif %} - {#Pie chart with income (aka all deposits in category). Optional checkbox to include all other transactions (for comparison).#}
+ + big chart here Show income / expenses per period. Differs per report: month = per day, year = per month, multi-year = per year. From 64364c3e772cb1a5c4c4932521ba9ba6c8a163c9 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 12 Nov 2016 19:11:59 +0100 Subject: [PATCH 084/709] Sending of error email message is optional but enabled. --- .env.example | 2 +- app/Exceptions/Handler.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index e8ebe3c838..1333892862 100755 --- a/.env.example +++ b/.env.example @@ -35,7 +35,7 @@ MAIL_PASSWORD=null MAIL_ENCRYPTION=null SEND_REGISTRATION_MAIL=true - +SEND_ERROR_MESSAGE=true SHOW_INCOMPLETE_TRANSLATIONS=false ANALYTICS_ID= diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 4f9d8b6675..c8b15f984d 100755 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -77,7 +77,8 @@ class Handler extends ExceptionHandler */ public function report(Exception $exception) { - if ($exception instanceof FireflyException || $exception instanceof ErrorException) { + $doMailError = env('SEND_ERROR_MESSAGE', true); + if (($exception instanceof FireflyException || $exception instanceof ErrorException) && $doMailError) { $userData = [ 'id' => 0, 'email' => 'unknown@example.com', From da49afa37b7ad7b1c9579772353fb2ce13c6ed32 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 12 Nov 2016 19:12:16 +0100 Subject: [PATCH 085/709] Removed duplicate code. --- app/Handlers/Events/BudgetEventHandler.php | 55 ++++------- app/Handlers/Events/UserEventHandler.php | 102 +++++++++------------ app/Import/Converter/TagSplit.php | 83 +++++++++++++++++ app/Import/Converter/TagsComma.php | 45 +-------- app/Import/Converter/TagsSpace.php | 45 +-------- 5 files changed, 146 insertions(+), 184 deletions(-) create mode 100644 app/Import/Converter/TagSplit.php diff --git a/app/Handlers/Events/BudgetEventHandler.php b/app/Handlers/Events/BudgetEventHandler.php index 86491613f3..b82aa6d749 100644 --- a/app/Handlers/Events/BudgetEventHandler.php +++ b/app/Handlers/Events/BudgetEventHandler.php @@ -14,8 +14,10 @@ declare(strict_types = 1); namespace FireflyIII\Handlers\Events; +use Carbon\Carbon; use FireflyIII\Events\StoredBudgetLimit; use FireflyIII\Events\UpdatedBudgetLimit; +use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\LimitRepetition; use Illuminate\Database\QueryException; use Log; @@ -38,37 +40,9 @@ class BudgetEventHandler */ public function storeRepetition(StoredBudgetLimit $event):bool { - $budgetLimit = $event->budgetLimit; - $end = $event->end; - $set = $budgetLimit->limitrepetitions() - ->where('startdate', $budgetLimit->startdate->format('Y-m-d 00:00:00')) - ->where('enddate', $end->format('Y-m-d 00:00:00')) - ->get(); - if ($set->count() == 0) { - $repetition = new LimitRepetition; - $repetition->startdate = $budgetLimit->startdate; - $repetition->enddate = $end; - $repetition->amount = $budgetLimit->amount; - $repetition->budgetLimit()->associate($budgetLimit); - - try { - $repetition->save(); - } catch (QueryException $e) { - Log::error('Trying to save new LimitRepetition failed: ' . $e->getMessage()); - } - } - - if ($set->count() == 1) { - $repetition = $set->first(); - $repetition->amount = $budgetLimit->amount; - $repetition->save(); - - } - - return true; + return $this->processRepetitionChange($event->budgetLimit, $event->end); } - /** * Updates, if present the budget limit repetition part of a budget limit. * @@ -78,16 +52,25 @@ class BudgetEventHandler */ public function updateRepetition(UpdatedBudgetLimit $event): bool { - $budgetLimit = $event->budgetLimit; - $end = $event->end; - $set = $budgetLimit->limitrepetitions() - ->where('startdate', $budgetLimit->startdate->format('Y-m-d 00:00:00')) - ->where('enddate', $end->format('Y-m-d 00:00:00')) - ->get(); + return $this->processRepetitionChange($event->budgetLimit, $event->end); + } + + /** + * @param BudgetLimit $budgetLimit + * @param Carbon $date + * + * @return bool + */ + private function processRepetitionChange(BudgetLimit $budgetLimit, Carbon $date):bool + { + $set = $budgetLimit->limitrepetitions() + ->where('startdate', $budgetLimit->startdate->format('Y-m-d 00:00:00')) + ->where('enddate', $date->format('Y-m-d 00:00:00')) + ->get(); if ($set->count() == 0) { $repetition = new LimitRepetition; $repetition->startdate = $budgetLimit->startdate; - $repetition->enddate = $end; + $repetition->enddate = $date; $repetition->amount = $budgetLimit->amount; $repetition->budgetLimit()->associate($budgetLimit); diff --git a/app/Handlers/Events/UserEventHandler.php b/app/Handlers/Events/UserEventHandler.php index c6235d0a6d..bf10bb71ea 100644 --- a/app/Handlers/Events/UserEventHandler.php +++ b/app/Handlers/Events/UserEventHandler.php @@ -19,6 +19,7 @@ use FireflyIII\Events\ConfirmedUser; use FireflyIII\Events\RegisteredUser; use FireflyIII\Events\ResentConfirmation; use FireflyIII\Repositories\User\UserRepositoryInterface; +use FireflyIII\User; use Illuminate\Mail\Message; use Log; use Mail; @@ -82,36 +83,7 @@ class UserEventHandler */ public function sendConfirmationMessage(RegisteredUser $event): bool { - $user = $event->user; - $ipAddress = $event->ipAddress; - $mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data; - if ($mustConfirmAccount === false) { - Preferences::setForUser($user, 'user_confirmed', true); - Preferences::setForUser($user, 'user_confirmed_last_mail', 0); - Preferences::mark(); - - return true; - } - $email = $user->email; - $code = str_random(16); - $route = route('do_confirm_account', [$code]); - Preferences::setForUser($user, 'user_confirmed', false); - Preferences::setForUser($user, 'user_confirmed_last_mail', time()); - Preferences::setForUser($user, 'user_confirmed_code', $code); - try { - Mail::send( - ['emails.confirm-account-html', 'emails.confirm-account'], ['route' => $route, 'ip' => $ipAddress], - function (Message $message) use ($email) { - $message->to($email, $email)->subject('Please confirm your Firefly III account'); - } - ); - } catch (Swift_TransportException $e) { - Log::error($e->getMessage()); - } catch (Exception $e) { - Log::error($e->getMessage()); - } - - return true; + return $this->sendConfirmation($event->user, $event->ipAddress); } /** @@ -125,36 +97,7 @@ class UserEventHandler */ function sendConfirmationMessageAgain(ResentConfirmation $event): bool { - $user = $event->user; - $ipAddress = $event->ipAddress; - $mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data; - if ($mustConfirmAccount === false) { - Preferences::setForUser($user, 'user_confirmed', true); - Preferences::setForUser($user, 'user_confirmed_last_mail', 0); - Preferences::mark(); - - return true; - } - $email = $user->email; - $code = str_random(16); - $route = route('do_confirm_account', [$code]); - Preferences::setForUser($user, 'user_confirmed', false); - Preferences::setForUser($user, 'user_confirmed_last_mail', time()); - Preferences::setForUser($user, 'user_confirmed_code', $code); - try { - Mail::send( - ['emails.confirm-account-html', 'emails.confirm-account'], ['route' => $route, 'ip' => $ipAddress], - function (Message $message) use ($email) { - $message->to($email, $email)->subject('Please confirm your Firefly III account'); - } - ); - } catch (Swift_TransportException $e) { - Log::error($e->getMessage()); - } catch (Exception $e) { - Log::error($e->getMessage()); - } - - return true; + return $this->sendConfirmation($event->user, $event->ipAddress); } @@ -223,4 +166,43 @@ class UserEventHandler } + + /** + * @param User $user + * @param string $ipAddress + * + * @return bool + */ + private function sendConfirmation(User $user, string $ipAddress): bool + { + $mustConfirmAccount = FireflyConfig::get('must_confirm_account', config('firefly.configuration.must_confirm_account'))->data; + if ($mustConfirmAccount === false) { + Preferences::setForUser($user, 'user_confirmed', true); + Preferences::setForUser($user, 'user_confirmed_last_mail', 0); + Preferences::mark(); + + return true; + } + $email = $user->email; + $code = str_random(16); + $route = route('do_confirm_account', [$code]); + Preferences::setForUser($user, 'user_confirmed', false); + Preferences::setForUser($user, 'user_confirmed_last_mail', time()); + Preferences::setForUser($user, 'user_confirmed_code', $code); + try { + Mail::send( + ['emails.confirm-account-html', 'emails.confirm-account'], ['route' => $route, 'ip' => $ipAddress], + function (Message $message) use ($email) { + $message->to($email, $email)->subject('Please confirm your Firefly III account'); + } + ); + } catch (Swift_TransportException $e) { + Log::error($e->getMessage()); + } catch (Exception $e) { + Log::error($e->getMessage()); + } + + return true; + } + } diff --git a/app/Import/Converter/TagSplit.php b/app/Import/Converter/TagSplit.php new file mode 100644 index 0000000000..ab1cdf21be --- /dev/null +++ b/app/Import/Converter/TagSplit.php @@ -0,0 +1,83 @@ + $part, 'map' => $mapping[$part]]); + $tag = $repository->find(intval($mapping[$part])); + if (!is_null($tag->id)) { + Log::debug('Found tag by ID', ['id' => $tag->id]); + + $set->push($tag); + continue; + } + } + // not mapped? Still try to find it first: + $tag = $repository->findByTag($part); + if (!is_null($tag->id)) { + Log::debug('Found tag by name ', ['id' => $tag->id]); + + $set->push($tag); + } + if (is_null($tag->id)) { + // create new tag + $tag = $repository->store( + [ + 'tag' => $part, + 'date' => null, + 'description' => $part, + 'latitude' => null, + 'longitude' => null, + 'zoomLevel' => null, + 'tagMode' => 'nothing', + ] + ); + Log::debug('Created new tag', ['name' => $part, 'id' => $tag->id]); + $set->push($tag); + } + } + return $set; + } + +} \ No newline at end of file diff --git a/app/Import/Converter/TagsComma.php b/app/Import/Converter/TagsComma.php index d4e4687e3a..e9fbbaeecd 100644 --- a/app/Import/Converter/TagsComma.php +++ b/app/Import/Converter/TagsComma.php @@ -13,7 +13,6 @@ declare(strict_types = 1); namespace FireflyIII\Import\Converter; -use FireflyIII\Repositories\Tag\TagRepositoryInterface; use Illuminate\Support\Collection; use Log; @@ -41,49 +40,7 @@ class TagsComma extends BasicConverter implements ConverterInterface return new Collection; } $parts = array_unique(explode(',', $value)); - $set = new Collection; - Log::debug('Exploded parts.', $parts); - - /** @var TagRepositoryInterface $repository */ - $repository = app(TagRepositoryInterface::class, [$this->user]); - - - /** @var string $part */ - foreach ($parts as $part) { - if (isset($this->mapping[$part])) { - Log::debug('Found tag in mapping. Should exist.', ['value' => $part, 'map' => $this->mapping[$part]]); - $tag = $repository->find(intval($this->mapping[$part])); - if (!is_null($tag->id)) { - Log::debug('Found tag by ID', ['id' => $tag->id]); - - $set->push($tag); - continue; - } - } - // not mapped? Still try to find it first: - $tag = $repository->findByTag($part); - if (!is_null($tag->id)) { - Log::debug('Found tag by name ', ['id' => $tag->id]); - - $set->push($tag); - } - if (is_null($tag->id)) { - // create new tag - $tag = $repository->store( - [ - 'tag' => $part, - 'date' => null, - 'description' => $part, - 'latitude' => null, - 'longitude' => null, - 'zoomLevel' => null, - 'tagMode' => 'nothing', - ] - ); - Log::debug('Created new tag', ['name' => $part, 'id' => $tag->id]); - $set->push($tag); - } - } + $set = TagSplit::createSetFromSplits($this->user, $this->mapping, $parts); $this->setCertainty(100); return $set; diff --git a/app/Import/Converter/TagsSpace.php b/app/Import/Converter/TagsSpace.php index d2d86eb8eb..3c437bd94b 100644 --- a/app/Import/Converter/TagsSpace.php +++ b/app/Import/Converter/TagsSpace.php @@ -13,7 +13,6 @@ declare(strict_types = 1); namespace FireflyIII\Import\Converter; -use FireflyIII\Repositories\Tag\TagRepositoryInterface; use Illuminate\Support\Collection; use Log; @@ -41,49 +40,7 @@ class TagsSpace extends BasicConverter implements ConverterInterface return new Collection; } $parts = array_unique(explode(' ', $value)); - $set = new Collection; - Log::debug('Exploded parts.', $parts); - - /** @var TagRepositoryInterface $repository */ - $repository = app(TagRepositoryInterface::class, [$this->user]); - - - /** @var string $part */ - foreach ($parts as $part) { - if (isset($this->mapping[$part])) { - Log::debug('Found tag in mapping. Should exist.', ['value' => $part, 'map' => $this->mapping[$part]]); - $tag = $repository->find(intval($this->mapping[$part])); - if (!is_null($tag->id)) { - Log::debug('Found tag by ID', ['id' => $tag->id]); - - $set->push($tag); - continue; - } - } - // not mapped? Still try to find it first: - $tag = $repository->findByTag($part); - if (!is_null($tag->id)) { - Log::debug('Found tag by name ', ['id' => $tag->id]); - - $set->push($tag); - } - if (is_null($tag->id)) { - // create new tag - $tag = $repository->store( - [ - 'tag' => $part, - 'date' => null, - 'description' => $part, - 'latitude' => null, - 'longitude' => null, - 'zoomLevel' => null, - 'tagMode' => 'nothing', - ] - ); - Log::debug('Created new tag', ['name' => $part, 'id' => $tag->id]); - $set->push($tag); - } - } + $set = TagSplit::createSetFromSplits($this->user, $this->mapping, $parts); $this->setCertainty(100); return $set; From 98d6c90e90f55de3f576466c853066a832c07b45 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 12 Nov 2016 19:22:03 +0100 Subject: [PATCH 086/709] Removed some duplicate code. --- .../Controllers/Chart/ReportController.php | 83 +++++++++---------- public/js/ff/reports/default/year.js | 4 +- routes/web.php | 36 +++++--- 3 files changed, 68 insertions(+), 55 deletions(-) diff --git a/app/Http/Controllers/Chart/ReportController.php b/app/Http/Controllers/Chart/ReportController.php index 941d66da86..c8fecbdad2 100644 --- a/app/Http/Controllers/Chart/ReportController.php +++ b/app/Http/Controllers/Chart/ReportController.php @@ -92,52 +92,36 @@ class ReportController extends Controller /** - * @param AccountTaskerInterface $accountTasker - * @param string $reportType - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * * @return \Illuminate\Http\JsonResponse - * @internal param AccountRepositoryInterface $repository */ - public function yearInOut(AccountTaskerInterface $accountTasker, string $reportType, Carbon $start, Carbon $end, Collection $accounts) + public function yearInOut(Carbon $start, Carbon $end, Collection $accounts) { // chart properties for cache: $cache = new CacheProperties; $cache->addProperty('yearInOut'); $cache->addProperty($start); - $cache->addProperty($reportType); $cache->addProperty($accounts); $cache->addProperty($end); if ($cache->has()) { return Response::json($cache->get()); } - // always per month. - $currentStart = clone $start; - $spentArray = []; - $earnedArray = []; - while ($currentStart <= $end) { - $currentEnd = Navigation::endOfPeriod($currentStart, '1M'); - $date = $currentStart->format('Y-m'); - $spent = $accountTasker->amountOutInPeriod($accounts, $accounts, $currentStart, $currentEnd); - $earned = $accountTasker->amountInInPeriod($accounts, $accounts, $currentStart, $currentEnd); - $spentArray[$date] = bcmul($spent, '-1'); - $earnedArray[$date] = $earned; - $currentStart = Navigation::addPeriod($currentStart, '1M', 0); - } + $chartSource = $this->getYearData($accounts, $start, $end); if ($start->diffInMonths($end) > 12) { // data = method X - $data = $this->multiYearInOut($earnedArray, $spentArray, $start, $end); + $data = $this->multiYearInOut($chartSource['earned'], $chartSource['spent'], $start, $end); $cache->store($data); return Response::json($data); } // data = method Y - $data = $this->singleYearInOut($earnedArray, $spentArray, $start, $end); + $data = $this->singleYearInOut($chartSource['earned'], $chartSource['spent'], $start, $end); $cache->store($data); return Response::json($data); @@ -146,8 +130,6 @@ class ReportController extends Controller } /** - * @param AccountTaskerInterface $accountTasker - * @param string $reportType * @param Carbon $start * @param Carbon $end * @param Collection $accounts @@ -155,7 +137,7 @@ class ReportController extends Controller * @return \Illuminate\Http\JsonResponse * @internal param AccountRepositoryInterface $repository */ - public function yearInOutSummarized(AccountTaskerInterface $accountTasker, string $reportType, Carbon $start, Carbon $end, Collection $accounts) + public function yearInOutSummarized(Carbon $start, Carbon $end, Collection $accounts) { // chart properties for cache: @@ -163,35 +145,21 @@ class ReportController extends Controller $cache->addProperty('yearInOutSummarized'); $cache->addProperty($start); $cache->addProperty($end); - $cache->addProperty($reportType); $cache->addProperty($accounts); if ($cache->has()) { return Response::json($cache->get()); } - - // always per month. - $currentStart = clone $start; - $spentArray = []; - $earnedArray = []; - while ($currentStart <= $end) { - $currentEnd = Navigation::endOfPeriod($currentStart, '1M'); - $date = $currentStart->format('Y-m'); - $spent = $accountTasker->amountOutInPeriod($accounts, $accounts, $currentStart, $currentEnd); - $earned = $accountTasker->amountInInPeriod($accounts, $accounts, $currentStart, $currentEnd); - $spentArray[$date] = bcmul($spent, '-1'); - $earnedArray[$date] = $earned; - $currentStart = Navigation::addPeriod($currentStart, '1M', 0); - } + $chartSource = $this->getYearData($accounts, $start, $end); if ($start->diffInMonths($end) > 12) { // per year - $data = $this->multiYearInOutSummarized($earnedArray, $spentArray, $start, $end); + $data = $this->multiYearInOutSummarized($chartSource['earned'], $chartSource['spent'], $start, $end); $cache->store($data); return Response::json($data); } // per month! - $data = $this->singleYearInOutSummarized($earnedArray, $spentArray, $start, $end); + $data = $this->singleYearInOutSummarized($chartSource['earned'], $chartSource['spent'], $start, $end); $cache->store($data); return Response::json($data); @@ -342,4 +310,33 @@ class ReportController extends Controller return $sum; } + + /** + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + private function getYearData(Collection $accounts, Carbon $start, Carbon $end): array + { + $tasker = app(AccountTaskerInterface::class); + $currentStart = clone $start; + $spentArray = []; + $earnedArray = []; + while ($currentStart <= $end) { + $currentEnd = Navigation::endOfPeriod($currentStart, '1M'); + $date = $currentStart->format('Y-m'); + $spent = $tasker->amountOutInPeriod($accounts, $accounts, $currentStart, $currentEnd); + $earned = $tasker->amountInInPeriod($accounts, $accounts, $currentStart, $currentEnd); + $spentArray[$date] = bcmul($spent, '-1'); + $earnedArray[$date] = $earned; + $currentStart = Navigation::addPeriod($currentStart, '1M', 0); + } + + return [ + 'spent' => $spentArray, + 'earned' => $earnedArray, + ]; + } } diff --git a/public/js/ff/reports/default/year.js b/public/js/ff/reports/default/year.js index 0609105bf6..1e2893f8e5 100644 --- a/public/js/ff/reports/default/year.js +++ b/public/js/ff/reports/default/year.js @@ -14,8 +14,8 @@ function drawChart() { "use strict"; lineChart('chart/report/net-worth/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'net-worth'); - columnChart('chart/report/in-out/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-chart'); - columnChart('chart/report/in-out-sum/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-sum-chart'); + columnChart('chart/report/in-out/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-chart'); + columnChart('chart/report/in-out-sum/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-sum-chart'); } diff --git a/routes/web.php b/routes/web.php index 795b82a17d..bc61e74842 100755 --- a/routes/web.php +++ b/routes/web.php @@ -209,22 +209,38 @@ Route::group( Route::get('/chart/category/{category}/all', ['uses' => 'Chart\CategoryController@all']); // these charts are used in reports (category reports): - Route::get('/chart/category/{accountList}/{categoryList}/{start_date}/{end_date}/{others}/income', - ['uses' => 'Chart\CategoryReportController@categoryIncome']); - Route::get('/chart/category/{accountList}/{categoryList}/{start_date}/{end_date}/{others}/expense', - ['uses' => 'Chart\CategoryReportController@categoryExpense']); + Route::get( + '/chart/category/{accountList}/{categoryList}/{start_date}/{end_date}/{others}/income', + ['uses' => 'Chart\CategoryReportController@categoryIncome'] + ); + Route::get( + '/chart/category/{accountList}/{categoryList}/{start_date}/{end_date}/{others}/expense', + ['uses' => 'Chart\CategoryReportController@categoryExpense'] + ); - Route::get('/chart/account/{accountList}/{categoryList}/{start_date}/{end_date}/{others}/income', - ['uses' => 'Chart\CategoryReportController@accountIncome']); - Route::get('/chart/account/{accountList}/{categoryList}/{start_date}/{end_date}/{others}/expense', - ['uses' => 'Chart\CategoryReportController@accountExpense']); + Route::get( + '/chart/account/{accountList}/{categoryList}/{start_date}/{end_date}/{others}/income', + ['uses' => 'Chart\CategoryReportController@accountIncome'] + ); + Route::get( + '/chart/account/{accountList}/{categoryList}/{start_date}/{end_date}/{others}/expense', + ['uses' => 'Chart\CategoryReportController@accountExpense'] + ); + Route::get( + '/chart/category-report-income/{accountList}/{categoryList}/{start_date}/{end_date}', + ['uses' => 'Chart\CategoryReportController@mainIncomeChart'] + ); + Route::get( + '/chart/category-report-expenses/{accountList}/{categoryList}/{start_date}/{end_date}', + ['uses' => 'Chart\CategoryReportController@mainExpenseChart'] + ); // piggy banks: Route::get('/chart/piggy-bank/{piggyBank}', ['uses' => 'Chart\PiggyBankController@history']); // reports: - Route::get('/chart/report/in-out/{reportType}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@yearInOut']); - Route::get('/chart/report/in-out-sum/{reportType}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@yearInOutSummarized']); + Route::get('/chart/report/in-out/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@yearInOut']); + Route::get('/chart/report/in-out-sum/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@yearInOutSummarized']); Route::get('/chart/report/net-worth/{reportType}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@netWorth']); From 7e7ac264d2bd397affafe46355cfe8c1f3fdcd08 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 12 Nov 2016 20:29:16 +0100 Subject: [PATCH 087/709] Fixed category chart --- .../CategoryChartGeneratorInterface.php | 7 +++ .../ChartJsCategoryChartGenerator.php | 45 ++++++++++++++++ .../Chart/CategoryReportController.php | 53 +++++++++++++++++++ public/js/ff/reports/category/month.js | 3 ++ resources/views/reports/category/month.twig | 20 ++++--- routes/web.php | 8 +-- 6 files changed, 123 insertions(+), 13 deletions(-) diff --git a/app/Generator/Chart/Category/CategoryChartGeneratorInterface.php b/app/Generator/Chart/Category/CategoryChartGeneratorInterface.php index e0444c1cba..2b95ba820b 100644 --- a/app/Generator/Chart/Category/CategoryChartGeneratorInterface.php +++ b/app/Generator/Chart/Category/CategoryChartGeneratorInterface.php @@ -37,6 +37,13 @@ interface CategoryChartGeneratorInterface */ public function all(Collection $entries): array; + /** + * @param array $entries + * + * @return array + */ + public function mainReportChart(array $entries): array; + /** * @param Collection $categories * @param Collection $entries diff --git a/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php b/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php index eb03606c2c..7aea9c65cd 100644 --- a/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php +++ b/app/Generator/Chart/Category/ChartJsCategoryChartGenerator.php @@ -118,6 +118,51 @@ class ChartJsCategoryChartGenerator implements CategoryChartGeneratorInterface return $data; } + /** + * @param array $entries + * + * @return array + */ + public function mainReportChart(array $entries): array + { + + $data = [ + 'count' => 0, + 'labels' => array_keys($entries), + 'datasets' => [], + ]; + + + foreach ($entries as $row) { + foreach ($row['in'] as $categoryId => $amount) { + // get in: + $data['datasets'][$categoryId . 'in']['data'][] = round($amount, 2); + + // get out: + $opposite = $row['out'][$categoryId]; + $data['datasets'][$categoryId . 'out']['data'][] = round($opposite, 2); + + // set name: + $data['datasets'][$categoryId . 'out']['label'] = $row['name'][$categoryId] . ' (' . strtolower(strval(trans('firefly.expenses'))) . ')'; + $data['datasets'][$categoryId . 'in']['label'] = $row['name'][$categoryId] . ' (' . strtolower(strval(trans('firefly.income'))) . ')'; + + } + } + + // remove empty rows: + foreach ($data['datasets'] as $key => $content) { + if (array_sum($content['data']) === 0.0) { + unset($data['datasets'][$key]); + } + } + + // re-key the datasets array: + $data['datasets'] = array_values($data['datasets']); + $data['count'] = count($data['datasets']); + + return $data; + } + /** * * @param Collection $entries diff --git a/app/Http/Controllers/Chart/CategoryReportController.php b/app/Http/Controllers/Chart/CategoryReportController.php index b19d03c156..1d376cf841 100644 --- a/app/Http/Controllers/Chart/CategoryReportController.php +++ b/app/Http/Controllers/Chart/CategoryReportController.php @@ -19,12 +19,14 @@ use FireflyIII\Generator\Chart\Category\CategoryChartGeneratorInterface; use FireflyIII\Generator\Report\Category\MonthReportGenerator; use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Models\Category; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use Illuminate\Support\Collection; use Log; +use Navigation; use Response; @@ -260,6 +262,57 @@ class CategoryReportController extends Controller return Response::json($data); } + /** + * @param Collection $accounts + * @param Collection $categories + * @param Carbon $start + * @param Carbon $end + * + * @return \Illuminate\Http\JsonResponse + */ + public function mainChart(Collection $accounts, Collection $categories, Carbon $start, Carbon $end) + { + // determin optimal period: + $period = '1D'; + $format = 'month_and_day'; + if ($start->diffInMonths($end) > 1) { + $period = '1M'; + $format = 'month'; + } + if ($start->diffInMonths($end) > 13) { + $period = '1Y'; + $format = 'year'; + } + Log::debug(sprintf('Period is %s', $period)); + $data = []; + $current = clone $start; + while ($current < $end) { + $currentEnd = Navigation::endOfPeriod($current, $period); + $expenses = $this->groupByCategory($this->getExpenses($accounts, $categories, $current, $currentEnd)); + $income = $this->groupByCategory($this->getIncome($accounts, $categories, $current, $currentEnd)); + $label = $current->formatLocalized(strval(trans('config.' . $format))); + $data[$label] = [ + 'in' => [], + 'out' => [], + ]; + + /** @var Category $category */ + foreach ($categories as $category) { + // get sum, and get label: + $categoryId = $category->id; + $data[$label]['name'][$categoryId] = $category->name; + $data[$label]['in'][$categoryId] = $income[$categoryId] ?? '0'; + $data[$label]['out'][$categoryId] = $expenses[$categoryId] ?? '0'; + } + + $current = Navigation::addPeriod($current, $period, 0); + } + + $data = $this->generator->mainReportChart($data); + return Response::json($data); + } + + /** * @param Collection $accounts * @param Collection $categories diff --git a/public/js/ff/reports/category/month.js b/public/js/ff/reports/category/month.js index 46bbf5cb47..b9786d68fc 100644 --- a/public/js/ff/reports/category/month.js +++ b/public/js/ff/reports/category/month.js @@ -42,12 +42,15 @@ function drawChart() { "use strict"; // month view: + stackedColumnChart('chart/category-report-in-out/' + accountIds + '/' + categoryIds + '/' + startDate + '/' + endDate, 'in-out-chart'); // draw pie chart of income, depending on "show other transactions too": redrawPieChart('categories-in-pie-chart', catInUri); redrawPieChart('categories-out-pie-chart', catOutUri); redrawPieChart('accounts-in-pie-chart', accInUri); redrawPieChart('accounts-out-pie-chart', accOutUri); + + } function redrawPieChart(container, uri) { diff --git a/resources/views/reports/category/month.twig b/resources/views/reports/category/month.twig index 4a7ca59700..4f07f6d1c6 100644 --- a/resources/views/reports/category/month.twig +++ b/resources/views/reports/category/month.twig @@ -148,15 +148,21 @@
- - - big chart here - - Show income / expenses per period. Differs per report: month = per day, year = per month, multi-year = per year. - In a bar chart, possibly grouped by expense/revenue account. - +
+
+

{{ 'income_and_expenses'|_ }}

+
+
+ +
+
+ {# + big chart here + Show income / expenses per period. Differs per report: month = per day, year = per month, multi-year = per year. + In a bar chart, possibly grouped by expense/revenue account. + #}
diff --git a/routes/web.php b/routes/web.php index bc61e74842..bff941d52f 100755 --- a/routes/web.php +++ b/routes/web.php @@ -227,12 +227,8 @@ Route::group( ['uses' => 'Chart\CategoryReportController@accountExpense'] ); Route::get( - '/chart/category-report-income/{accountList}/{categoryList}/{start_date}/{end_date}', - ['uses' => 'Chart\CategoryReportController@mainIncomeChart'] - ); - Route::get( - '/chart/category-report-expenses/{accountList}/{categoryList}/{start_date}/{end_date}', - ['uses' => 'Chart\CategoryReportController@mainExpenseChart'] + '/chart/category-report-in-out/{accountList}/{categoryList}/{start_date}/{end_date}', + ['uses' => 'Chart\CategoryReportController@mainChart'] ); // piggy banks: From 2f47c58df5edd0a3f1ce528788819eb1137616c0 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 12 Nov 2016 20:30:39 +0100 Subject: [PATCH 088/709] New string for translation [skip ci] --- resources/lang/en_US/firefly.php | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 058a239131..50cc4d0e55 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -696,6 +696,7 @@ return [ 'expense_per_account' => 'Expense per account', 'include_not_in_category' => 'Include transactions not selected for this report', 'everything_else' => 'Everything else', + 'income_and_expenses' => 'Income and expenses', // charts: 'chart' => 'Chart', From f7579db4ad40da66bf0e88790144bd71b7e8bd3d Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 12 Nov 2016 20:41:15 +0100 Subject: [PATCH 089/709] Clean up config [skip ci] --- config/app.php | 255 ++++++++++++------------------------------------- 1 file changed, 60 insertions(+), 195 deletions(-) diff --git a/config/app.php b/config/app.php index 478fae8b8d..312a96e7a8 100755 --- a/config/app.php +++ b/config/app.php @@ -12,141 +12,18 @@ declare(strict_types = 1); return [ - - /* - |-------------------------------------------------------------------------- - | Application Name - |-------------------------------------------------------------------------- - | - | This value is the name of your application. This value is used when the - | framework needs to place the application's name in a notification or - | any other location as required by the application or its packages. - */ - - 'name' => 'Firefly III', - - /* - |-------------------------------------------------------------------------- - | Application Environment - |-------------------------------------------------------------------------- - | - | This value determines the "environment" your application is currently - | running in. This may determine how you prefer to configure various - | services your application utilizes. Set this in your ".env" file. - | - */ - - 'env' => env('APP_ENV', 'production'), - - /* - |-------------------------------------------------------------------------- - | Application Debug Mode - |-------------------------------------------------------------------------- - | - | When your application is in debug mode, detailed error messages with - | stack traces will be shown on every error that occurs within your - | application. If disabled, a simple generic error page is shown. - | - */ - - 'debug' => env('APP_DEBUG', false), - - /* - |-------------------------------------------------------------------------- - | Application URL - |-------------------------------------------------------------------------- - | - | This URL is used by the console to properly generate URLs when using - | the Artisan command line tool. You should set this to the root of - | your application so that it is used when running Artisan tasks. - | - */ - - 'url' => env('APP_URL', 'http://localhost'), - - /* - |-------------------------------------------------------------------------- - | Application Timezone - |-------------------------------------------------------------------------- - | - | Here you may specify the default timezone for your application, which - | will be used by the PHP date and date-time functions. We have gone - | ahead and set this to a sensible default for you out of the box. - | - */ - - 'timezone' => 'UTC', - - /* - |-------------------------------------------------------------------------- - | Application Locale Configuration - |-------------------------------------------------------------------------- - | - | The application locale determines the default locale that will be used - | by the translation service provider. You are free to set this value - | to any of the locales which will be supported by the application. - | - */ - - 'locale' => 'en_US', - - /* - |-------------------------------------------------------------------------- - | Application Fallback Locale - |-------------------------------------------------------------------------- - | - | The fallback locale determines the locale to use when the current one - | is not available. You may change the value to correspond to any of - | the language folders that are provided through your application. - | - */ - + 'name' => 'Firefly III', + 'env' => env('APP_ENV', 'production'), + 'debug' => env('APP_DEBUG', false), + 'url' => env('APP_URL', 'http://localhost'), + 'timezone' => 'UTC', + 'locale' => 'en_US', 'fallback_locale' => 'en_US', - - /* - |-------------------------------------------------------------------------- - | Encryption Key - |-------------------------------------------------------------------------- - | - | This key is used by the Illuminate encrypter service and should be set - | to a random, 32 character string, otherwise these encrypted strings - | will not be safe. Please do this before deploying an application! - | - */ - - 'key' => env('APP_KEY'), - - 'cipher' => 'AES-256-CBC', - - /* - |-------------------------------------------------------------------------- - | Logging Configuration - |-------------------------------------------------------------------------- - | - | Here you may configure the log settings for your application. Out of - | the box, Laravel uses the Monolog PHP logging library. This gives - | you a variety of powerful log handlers / formatters to utilize. - | - | Available Settings: "single", "daily", "syslog", "errorlog" - | - */ - - 'log' => env('APP_LOG', 'daily'), - - 'log_level' => env('APP_LOG_LEVEL', 'info'), - - /* - |-------------------------------------------------------------------------- - | Autoloaded Service Providers - |-------------------------------------------------------------------------- - | - | The service providers listed here will be automatically loaded on the - | request to your application. Feel free to add your own services to - | this array to grant expanded functionality to your applications. - | - */ - - 'providers' => [ + 'key' => env('APP_KEY'), + 'cipher' => 'AES-256-CBC', + 'log' => env('APP_LOG', 'daily'), + 'log_level' => env('APP_LOG_LEVEL', 'info'), + 'providers' => [ /* * Laravel Framework Service Providers... @@ -188,15 +65,15 @@ return [ // own stuff: -// Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class, -// Barryvdh\Debugbar\ServiceProvider::class, + //Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class, + //Barryvdh\Debugbar\ServiceProvider::class, DaveJamesMiller\Breadcrumbs\ServiceProvider::class, TwigBridge\ServiceProvider::class, 'PragmaRX\Google2FA\Vendor\Laravel\ServiceProvider', /* -* More service providers. -*/ + * More service providers. + */ FireflyIII\Providers\CrudServiceProvider::class, FireflyIII\Providers\AccountServiceProvider::class, FireflyIII\Providers\AttachmentServiceProvider::class, @@ -213,64 +90,52 @@ return [ ], + 'aliases' => [ - /* - |-------------------------------------------------------------------------- - | Class Aliases - |-------------------------------------------------------------------------- - | - | This array of class aliases will be registered when this application - | is started. However, feel free to register as many as you wish as - | the aliases are "lazy" loaded so they don't hinder performance. - | - */ - - 'aliases' => [ - - 'App' => Illuminate\Support\Facades\App::class, - 'Artisan' => Illuminate\Support\Facades\Artisan::class, - 'Auth' => Illuminate\Support\Facades\Auth::class, - 'Blade' => Illuminate\Support\Facades\Blade::class, - 'Cache' => Illuminate\Support\Facades\Cache::class, - 'Config' => Illuminate\Support\Facades\Config::class, - 'Cookie' => Illuminate\Support\Facades\Cookie::class, - 'Crypt' => Illuminate\Support\Facades\Crypt::class, - 'DB' => Illuminate\Support\Facades\DB::class, - 'Eloquent' => Illuminate\Database\Eloquent\Model::class, - 'Event' => Illuminate\Support\Facades\Event::class, - 'File' => Illuminate\Support\Facades\File::class, - 'Gate' => Illuminate\Support\Facades\Gate::class, - 'Hash' => Illuminate\Support\Facades\Hash::class, - 'Lang' => Illuminate\Support\Facades\Lang::class, - 'Log' => Illuminate\Support\Facades\Log::class, - 'Mail' => Illuminate\Support\Facades\Mail::class, - 'Notification' => Illuminate\Support\Facades\Notification::class, - 'Password' => Illuminate\Support\Facades\Password::class, - 'Queue' => Illuminate\Support\Facades\Queue::class, - 'Redirect' => Illuminate\Support\Facades\Redirect::class, - 'Redis' => Illuminate\Support\Facades\Redis::class, - 'Request' => Illuminate\Support\Facades\Request::class, - 'Response' => Illuminate\Support\Facades\Response::class, - 'Route' => Illuminate\Support\Facades\Route::class, - 'Schema' => Illuminate\Support\Facades\Schema::class, - 'Session' => Illuminate\Support\Facades\Session::class, - 'Storage' => Illuminate\Support\Facades\Storage::class, - 'URL' => Illuminate\Support\Facades\URL::class, - 'Validator' => Illuminate\Support\Facades\Validator::class, - 'View' => Illuminate\Support\Facades\View::class, - 'Twig' => 'TwigBridge\Facade\Twig', - 'Form' => Collective\Html\FormFacade::class, - 'Html' => Collective\Html\HtmlFacade::class, - 'Breadcrumbs' => 'DaveJamesMiller\Breadcrumbs\Facade', - 'Preferences' => 'FireflyIII\Support\Facades\Preferences', - 'FireflyConfig' => 'FireflyIII\Support\Facades\FireflyConfig', - 'Navigation' => 'FireflyIII\Support\Facades\Navigation', - 'Amount' => 'FireflyIII\Support\Facades\Amount', - 'Steam' => 'FireflyIII\Support\Facades\Steam', - 'ExpandedForm' => 'FireflyIII\Support\Facades\ExpandedForm', - 'Entrust' => 'Zizaco\Entrust\EntrustFacade', - 'Input' => 'Illuminate\Support\Facades\Input', - 'Google2FA' => 'PragmaRX\Google2FA\Vendor\Laravel\Facade', + 'App' => Illuminate\Support\Facades\App::class, + 'Artisan' => Illuminate\Support\Facades\Artisan::class, + 'Auth' => Illuminate\Support\Facades\Auth::class, + 'Blade' => Illuminate\Support\Facades\Blade::class, + 'Cache' => Illuminate\Support\Facades\Cache::class, + 'Config' => Illuminate\Support\Facades\Config::class, + 'Cookie' => Illuminate\Support\Facades\Cookie::class, + 'Crypt' => Illuminate\Support\Facades\Crypt::class, + 'DB' => Illuminate\Support\Facades\DB::class, + 'Eloquent' => Illuminate\Database\Eloquent\Model::class, + 'Event' => Illuminate\Support\Facades\Event::class, + 'File' => Illuminate\Support\Facades\File::class, + 'Gate' => Illuminate\Support\Facades\Gate::class, + 'Hash' => Illuminate\Support\Facades\Hash::class, + 'Lang' => Illuminate\Support\Facades\Lang::class, + 'Log' => Illuminate\Support\Facades\Log::class, + 'Mail' => Illuminate\Support\Facades\Mail::class, + 'Notification' => Illuminate\Support\Facades\Notification::class, + 'Password' => Illuminate\Support\Facades\Password::class, + 'Queue' => Illuminate\Support\Facades\Queue::class, + 'Redirect' => Illuminate\Support\Facades\Redirect::class, + 'Redis' => Illuminate\Support\Facades\Redis::class, + 'Request' => Illuminate\Support\Facades\Request::class, + 'Response' => Illuminate\Support\Facades\Response::class, + 'Route' => Illuminate\Support\Facades\Route::class, + 'Schema' => Illuminate\Support\Facades\Schema::class, + 'Session' => Illuminate\Support\Facades\Session::class, + 'Storage' => Illuminate\Support\Facades\Storage::class, + 'URL' => Illuminate\Support\Facades\URL::class, + 'Validator' => Illuminate\Support\Facades\Validator::class, + 'View' => Illuminate\Support\Facades\View::class, + 'Twig' => 'TwigBridge\Facade\Twig', + 'Form' => Collective\Html\FormFacade::class, + 'Html' => Collective\Html\HtmlFacade::class, + 'Breadcrumbs' => 'DaveJamesMiller\Breadcrumbs\Facade', + 'Preferences' => 'FireflyIII\Support\Facades\Preferences', + 'FireflyConfig' => 'FireflyIII\Support\Facades\FireflyConfig', + 'Navigation' => 'FireflyIII\Support\Facades\Navigation', + 'Amount' => 'FireflyIII\Support\Facades\Amount', + 'Steam' => 'FireflyIII\Support\Facades\Steam', + 'ExpandedForm' => 'FireflyIII\Support\Facades\ExpandedForm', + 'Entrust' => 'Zizaco\Entrust\EntrustFacade', + 'Input' => 'Illuminate\Support\Facades\Input', + 'Google2FA' => 'PragmaRX\Google2FA\Vendor\Laravel\Facade', ], From 7bbca7f6a8391f863a710420ce6152a44ba09b44 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 12 Nov 2016 20:48:29 +0100 Subject: [PATCH 090/709] Final code check for something with the debug bar [skip ci] --- config/app.php | 2 -- resources/views/reports/category/month.twig | 3 --- 2 files changed, 5 deletions(-) diff --git a/config/app.php b/config/app.php index 312a96e7a8..68c0f500a9 100755 --- a/config/app.php +++ b/config/app.php @@ -136,8 +136,6 @@ return [ 'Entrust' => 'Zizaco\Entrust\EntrustFacade', 'Input' => 'Illuminate\Support\Facades\Input', 'Google2FA' => 'PragmaRX\Google2FA\Vendor\Laravel\Facade', - - ], ]; diff --git a/resources/views/reports/category/month.twig b/resources/views/reports/category/month.twig index 4f07f6d1c6..f4ef4a6f04 100644 --- a/resources/views/reports/category/month.twig +++ b/resources/views/reports/category/month.twig @@ -173,9 +173,6 @@ order by size - linked to chart - - use reset button to reset after chart was clicked.
List of spending (withdrawals) by transaction, if relevant. Not grouped
From 9ce28fdd2e1b73cee4d0680ac49c98532b2517f8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 12 Nov 2016 20:54:05 +0100 Subject: [PATCH 091/709] Remove unused route. [skip ci] --- routes/api.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/routes/api.php b/routes/api.php index f89551ddfc..5f4cc3f27e 100755 --- a/routes/api.php +++ b/routes/api.php @@ -20,7 +20,3 @@ use Illuminate\Http\Request; | is assigned the "api" middleware group. Enjoy building your API! | */ - -Route::get('/user', function (Request $request) { - return $request->user(); -})->middleware('auth:api'); From 750b9d80382f7cb3341239f056278542419fa269 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 12 Nov 2016 21:06:48 +0100 Subject: [PATCH 092/709] Reduce number of queries. --- .../Report/Category/MonthReportGenerator.php | 4 +- app/Helpers/Collector/JournalCollector.php | 78 ++++++++++--------- .../Collector/JournalCollectorInterface.php | 2 +- .../Chart/CategoryReportController.php | 4 +- .../Controllers/TransactionController.php | 3 +- app/Support/Twig/Transaction.php | 14 +++- 6 files changed, 60 insertions(+), 45 deletions(-) diff --git a/app/Generator/Report/Category/MonthReportGenerator.php b/app/Generator/Report/Category/MonthReportGenerator.php index 6420f24078..a46802b1e6 100644 --- a/app/Generator/Report/Category/MonthReportGenerator.php +++ b/app/Generator/Report/Category/MonthReportGenerator.php @@ -225,7 +225,7 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface $collector = new JournalCollector(auth()->user()); $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) ->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) - ->setCategories($this->categories)->getOpposingAccount()->disableFilter(); + ->setCategories($this->categories)->withOpposingAccount()->disableFilter(); $accountIds = $this->accounts->pluck('id')->toArray(); $transactions = $collector->getJournals(); @@ -242,7 +242,7 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface $collector = new JournalCollector(auth()->user()); $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) ->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) - ->setCategories($this->categories)->getOpposingAccount(); + ->setCategories($this->categories)->withOpposingAccount(); $accountIds = $this->accounts->pluck('id')->toArray(); $transactions = $collector->getJournals(); $transactions = self::filterIncome($transactions, $accountIds); diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index 349b1d2b99..a4237d311d 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -148,42 +148,6 @@ class JournalCollector implements JournalCollectorInterface return $set; } - /** - * @return JournalCollectorInterface - */ - public function getOpposingAccount(): JournalCollectorInterface - { - $this->joinOpposingTables(); - - $accountIds = $this->accountIds; - $this->query->where( - function (EloquentBuilder $q1) use ($accountIds) { - // set 1 - $q1->where( - function (EloquentBuilder $q2) use ($accountIds) { - // transactions.account_id in set - $q2->whereIn('transactions.account_id', $accountIds); - // opposing.account_id not in set - $q2->whereNotIn('opposing.account_id', $accountIds); - - } - ); - // or set 2 - $q1->orWhere( - function (EloquentBuilder $q3) use ($accountIds) { - // transactions.account_id not in set - $q3->whereNotIn('transactions.account_id', $accountIds); - // B in set - // opposing.account_id not in set - $q3->whereIn('opposing.account_id', $accountIds); - } - ); - } - ); - - return $this; - } - /** * @return LengthAwarePaginator * @throws FireflyException @@ -416,6 +380,42 @@ class JournalCollector implements JournalCollectorInterface return $this; } + /** + * @return JournalCollectorInterface + */ + public function withOpposingAccount(): JournalCollectorInterface + { + $this->joinOpposingTables(); + + $accountIds = $this->accountIds; + $this->query->where( + function (EloquentBuilder $q1) use ($accountIds) { + // set 1 + $q1->where( + function (EloquentBuilder $q2) use ($accountIds) { + // transactions.account_id in set + $q2->whereIn('transactions.account_id', $accountIds); + // opposing.account_id not in set + $q2->whereNotIn('opposing.account_id', $accountIds); + + } + ); + // or set 2 + $q1->orWhere( + function (EloquentBuilder $q3) use ($accountIds) { + // transactions.account_id not in set + $q3->whereNotIn('transactions.account_id', $accountIds); + // B in set + // opposing.account_id not in set + $q3->whereIn('opposing.account_id', $accountIds); + } + ); + } + ); + + return $this; + } + /** * @return JournalCollectorInterface */ @@ -538,8 +538,14 @@ class JournalCollector implements JournalCollectorInterface ->where('opposing.amount', '=', DB::raw('transactions.amount * -1')); } ); + $this->query->leftJoin('accounts as opposing_accounts', 'opposing.account_id', '=', 'opposing_accounts.id'); + $this->query->leftJoin('account_types as opposing_account_types', 'opposing_accounts.account_type_id', '=', 'opposing_account_types.id'); $this->fields[] = 'opposing.account_id as opposing_account_id'; + $this->fields[] = 'opposing_accounts.name as opposing_account_name'; + $this->fields[] = 'opposing_accounts.encrypted as opposing_account_encrypted'; + $this->fields[] = 'opposing_account_types.type as opposing_account_type'; + } } diff --git a/app/Helpers/Collector/JournalCollectorInterface.php b/app/Helpers/Collector/JournalCollectorInterface.php index b451a111ae..da72c8f809 100644 --- a/app/Helpers/Collector/JournalCollectorInterface.php +++ b/app/Helpers/Collector/JournalCollectorInterface.php @@ -46,7 +46,7 @@ interface JournalCollectorInterface /** * @return JournalCollectorInterface */ - public function getOpposingAccount(): JournalCollectorInterface; + public function withOpposingAccount(): JournalCollectorInterface; /** * @return LengthAwarePaginator diff --git a/app/Http/Controllers/Chart/CategoryReportController.php b/app/Http/Controllers/Chart/CategoryReportController.php index 1d376cf841..2aa1fa55f8 100644 --- a/app/Http/Controllers/Chart/CategoryReportController.php +++ b/app/Http/Controllers/Chart/CategoryReportController.php @@ -325,7 +325,7 @@ class CategoryReportController extends Controller { $collector = new JournalCollector(auth()->user()); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) - ->setCategories($categories)->getOpposingAccount()->disableFilter(); + ->setCategories($categories)->withOpposingAccount()->disableFilter(); $accountIds = $accounts->pluck('id')->toArray(); $transactions = $collector->getJournals(); $set = MonthReportGenerator::filterExpenses($transactions, $accountIds); @@ -345,7 +345,7 @@ class CategoryReportController extends Controller { $collector = new JournalCollector(auth()->user()); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) - ->setCategories($categories)->getOpposingAccount(); + ->setCategories($categories)->withOpposingAccount(); $accountIds = $accounts->pluck('id')->toArray(); $transactions = $collector->getJournals(); $set = MonthReportGenerator::filterIncome($transactions, $accountIds); diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 264524d20e..5b6caeb74d 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -63,7 +63,8 @@ class TransactionController extends Controller $subTitle = trans('firefly.title_' . $what); $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); $collector = new JournalCollector(auth()->user()); - $collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts(); + $collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts() + ->withOpposingAccount(); $journals = $collector->getPaginatedJournals(); $journals->setPath('transactions/' . $what); diff --git a/app/Support/Twig/Transaction.php b/app/Support/Twig/Transaction.php index 65e76090ac..1d9d9c73fe 100644 --- a/app/Support/Twig/Transaction.php +++ b/app/Support/Twig/Transaction.php @@ -252,13 +252,21 @@ class Transaction extends Twig_Extension return new Twig_SimpleFunction( 'transactionSourceAccount', function (TransactionModel $transaction): string { + // if the amount is negative, assume that the current account (the one in $transaction) is indeed the source account. $name = intval($transaction->account_encrypted) === 1 ? Crypt::decrypt($transaction->account_name) : $transaction->account_name; $id = intval($transaction->account_id); $type = $transaction->account_type; - // if the amount is negative, assume that the current account (the one in $transaction) is indeed the source account. - if (bccomp($transaction->transaction_amount, '0') === 1) { - // if the amount is positive, find the opposing account and use that one: + // name is present in object, use that one: + if (bccomp($transaction->transaction_amount, '0') === 1 && !is_null($transaction->opposing_account_id)) { + + $name = intval($transaction->opposing_account_encrypted) === 1 ? Crypt::decrypt($transaction->opposing_account_name) + : $transaction->opposing_account_name; + $id = intval($transaction->opposing_account_id); + $type = intval($transaction->opposing_account_type); + } + // Find the opposing account and use that one: + if (bccomp($transaction->transaction_amount, '0') === 1 && is_null($transaction->opposing_account_id)) { $journalId = $transaction->journal_id; /** @var TransactionModel $other */ $other = TransactionModel From eecb6c6679fa5e0794acd4e0f72f6f93d0a0c2f3 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 12 Nov 2016 21:08:14 +0100 Subject: [PATCH 093/709] Optimize again for account name [skip ci] --- app/Support/Twig/Transaction.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/Support/Twig/Transaction.php b/app/Support/Twig/Transaction.php index 1d9d9c73fe..37a964e959 100644 --- a/app/Support/Twig/Transaction.php +++ b/app/Support/Twig/Transaction.php @@ -189,9 +189,18 @@ class Transaction extends Twig_Extension $name = intval($transaction->account_encrypted) === 1 ? Crypt::decrypt($transaction->account_name) : $transaction->account_name; $id = intval($transaction->account_id); $type = $transaction->account_type; - // if the amount is positive, assume that the current account (the one in $transaction) is indeed the destination account. - if (bccomp($transaction->transaction_amount, '0') === -1) { + // name is present in object, use that one: + if (bccomp($transaction->transaction_amount, '0') === -1 && !is_null($transaction->opposing_account_id)) { + + $name = intval($transaction->opposing_account_encrypted) === 1 ? Crypt::decrypt($transaction->opposing_account_name) + : $transaction->opposing_account_name; + $id = intval($transaction->opposing_account_id); + $type = intval($transaction->opposing_account_type); + } + + // Find the opposing account and use that one: + if (bccomp($transaction->transaction_amount, '0') === -1 && is_null($transaction->opposing_account_id)) { // if the amount is negative, find the opposing account and use that one: $journalId = $transaction->journal_id; /** @var TransactionModel $other */ From 0c0f2109f6cbe9022a4b9786f6555c86a28471b0 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 13 Nov 2016 11:31:48 +0100 Subject: [PATCH 094/709] Fix chart size [skip ci] --- resources/views/index.twig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/resources/views/index.twig b/resources/views/index.twig index 679b8a1581..f50e0e1c12 100644 --- a/resources/views/index.twig +++ b/resources/views/index.twig @@ -51,7 +51,10 @@
- +
+ +
+
From 0bb07e1eebd8ceda079f3eb3d72907766aaca62f Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 13 Nov 2016 20:18:01 +0100 Subject: [PATCH 095/709] Small extension of category report. --- .../Report/Category/MonthReportGenerator.php | 79 +++++++- resources/views/reports/category/month.twig | 173 ++++++++++++------ 2 files changed, 197 insertions(+), 55 deletions(-) diff --git a/app/Generator/Report/Category/MonthReportGenerator.php b/app/Generator/Report/Category/MonthReportGenerator.php index a46802b1e6..a3d2abb4ee 100644 --- a/app/Generator/Report/Category/MonthReportGenerator.php +++ b/app/Generator/Report/Category/MonthReportGenerator.php @@ -15,11 +15,13 @@ namespace FireflyIII\Generator\Report\Category; use Carbon\Carbon; +use Crypt; use FireflyIII\Generator\Report\ReportGeneratorInterface; use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionType; use Illuminate\Support\Collection; +use Log; /** * Class MonthReportGenerator @@ -34,9 +36,22 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface private $categories; /** @var Carbon */ private $end; + /** @var Collection */ + private $expenses; + /** @var Collection */ + private $income; /** @var Carbon */ private $start; + /** + * MonthReportGenerator constructor. + */ + public function __construct() + { + $this->income = new Collection; + $this->expenses = new Collection; + } + /** * @return string */ @@ -47,9 +62,10 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface $reportType = 'category'; $accountSummary = $this->getAccountSummary(); $categorySummary = $this->getCategorySummary(); + $averageExpenses = $this->getAverageExpenses(); // render! - return view('reports.category.month', compact('accountIds', 'categoryIds', 'reportType', 'accountSummary', 'categorySummary')) + return view('reports.category.month', compact('accountIds', 'categoryIds', 'reportType', 'accountSummary', 'categorySummary','averageExpenses')) ->with('start', $this->start)->with('end', $this->end) ->with('categories', $this->categories) ->with('accounts', $this->accounts) @@ -143,6 +159,49 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface } + /** + * @return array + */ + private function getAverageExpenses(): array + { + $expenses = $this->getExpenses(); + $result = []; + /** @var Transaction $transaction */ + foreach ($expenses as $transaction) { + // opposing name and ID: + $opposingId = $transaction->opposing_account_id; + + // is not set? + if (!isset($result[$opposingId])) { + $name = $transaction->opposing_account_name; + $encrypted = intval($transaction->opposing_account_encrypted); + $name = $encrypted === 1 ? Crypt::decrypt($name) : $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, SORT_ASC, $result); + + return $result; + + } + /** * @return array */ @@ -222,14 +281,21 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface */ 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); + $accountIds = $this->accounts->pluck('id')->toArray(); + $transactions = $collector->getJournals(); + $transactions = self::filterExpenses($transactions, $accountIds); + $this->expenses = $transactions; return $transactions; } @@ -239,6 +305,10 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface */ 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]) @@ -246,6 +316,7 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface $accountIds = $this->accounts->pluck('id')->toArray(); $transactions = $collector->getJournals(); $transactions = self::filterIncome($transactions, $accountIds); + $this->income = $transactions; return $transactions; } diff --git a/resources/views/reports/category/month.twig b/resources/views/reports/category/month.twig index f4ef4a6f04..46357c569d 100644 --- a/resources/views/reports/category/month.twig +++ b/resources/views/reports/category/month.twig @@ -89,73 +89,77 @@
{% if categories.count > 1 %} -
-
-
-

{{ 'income_per_category'|_ }}

-
-
- - +
+
+
+

{{ 'income_per_category'|_ }}

+
+
+ + +
-
-
-
-
-

{{ 'expense_per_category'|_ }}

-
-
- - +
+
+
+

{{ 'expense_per_category'|_ }}

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

{{ 'income_per_account'|_ }}

-
-
- - +
+
+
+

{{ 'income_per_account'|_ }}

+
+
+ + +
-
-
-
-
-

{{ 'expense_per_account'|_ }}

-
-
- - +
+
+
+

{{ 'expense_per_account'|_ }}

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

{{ 'income_and_expenses'|_ }}

-
-
- -
+
+
+

{{ 'income_and_expenses'|_ }}

+
+ +
+
{# @@ -164,6 +168,73 @@ In a bar chart, possibly grouped by expense/revenue account. #} +
+
+
+
+

{{ 'average_spending_per_account'|_ }}

+
+
+ + + + + + + + + + {% for row in averageExpenses %} + + + + + + {% endfor %} + +
{{ 'account'|_ }}{{ 'spent_average'|_ }}{{ 'transaction_count'|_ }}
+ {{ row.name }} + + {{ row.average|formatAmount }} + + {{ row.count }} +
+
+
+
+
+
+
+

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

+
+
+
+
+
+
+
+ +
+
+
+

{{ 'average_income_per_account'|_ }}

+
+
+
+
+
+
+
+
+

{{ 'income'|_ }} ({{ trans('firefly.topX', {number: 10}) }})

+
+
+
+
+
+
+ +
List of spending (withdrawals) by account, if relevant. Grouped:
From 4ef324cf24bfd0c58228f796282954a1050b2afd Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 16 Nov 2016 20:35:25 +0100 Subject: [PATCH 096/709] Optimized chart code. --- public/js/ff/charts.defaults.js | 46 ++ public/js/ff/charts.js | 413 +++++------------- public/js/ff/index.js | 54 +-- public/js/ff/reports/default/all.js | 58 +-- public/js/ff/reports/default/multi-year.js | 6 +- public/js/ff/reports/default/year.js | 3 - public/js/ff/reports/index.js | 3 +- resources/views/accounts/show.twig | 1 + resources/views/accounts/show_with_date.twig | 1 + resources/views/bills/show.twig | 1 + resources/views/budgets/show.twig | 1 + resources/views/categories/index.twig | 1 + resources/views/categories/show.twig | 1 + .../views/categories/show_with_date.twig | 1 + resources/views/index.twig | 1 + resources/views/piggy-banks/show.twig | 1 + resources/views/reports/category/month.twig | 1 + resources/views/reports/default/month.twig | 1 + .../views/reports/default/multi-year.twig | 1 + resources/views/reports/default/year.twig | 1 + 20 files changed, 191 insertions(+), 405 deletions(-) create mode 100644 public/js/ff/charts.defaults.js diff --git a/public/js/ff/charts.defaults.js b/public/js/ff/charts.defaults.js new file mode 100644 index 0000000000..f763bb6c15 --- /dev/null +++ b/public/js/ff/charts.defaults.js @@ -0,0 +1,46 @@ +var defaultChartOptions = { + scales: { + xAxes: [ + { + gridLines: { + display: false + } + } + ], + yAxes: [{ + display: true, + ticks: { + callback: function (tickValue, index, ticks) { + "use strict"; + return accounting.formatMoney(tickValue); + + }, + beginAtZero: true + } + + }] + }, + tooltips: { + mode: 'label', + callbacks: { + label: function (tooltipItem, data) { + "use strict"; + return data.datasets[tooltipItem.datasetIndex].label + ': ' + accounting.formatMoney(tooltipItem.yLabel); + } + } + } +}; + +var defaultPieOptions = { + tooltips: { + callbacks: { + label: function (tooltipItem, data) { + "use strict"; + var value = data.datasets[0].data[tooltipItem.index]; + return data.labels[tooltipItem.index] + ': ' + accounting.formatMoney(value); + } + } + }, + maintainAspectRatio: true, + responsive: true +}; \ No newline at end of file diff --git a/public/js/ff/charts.js b/public/js/ff/charts.js index 6478a8bf5e..98d1ad463e 100644 --- a/public/js/ff/charts.js +++ b/public/js/ff/charts.js @@ -47,355 +47,138 @@ Chart.defaults.global.animation.duration = 0; Chart.defaults.global.responsive = true; Chart.defaults.global.maintainAspectRatio = false; -/* - Set default options: - */ -var defaultAreaOptions = { - scales: { - xAxes: [ - { - gridLines: { - display: false - } - } - ], - yAxes: [{ - display: true, - ticks: { - callback: function (tickValue, index, ticks) { - "use strict"; - return accounting.formatMoney(tickValue); - - } - } - }] - }, - tooltips: { - mode: 'label', - callbacks: { - label: function (tooltipItem, data) { - "use strict"; - return data.datasets[tooltipItem.datasetIndex].label + ': ' + accounting.formatMoney(tooltipItem.yLabel); - } - } - } -}; - - -var defaultPieOptions = { - tooltips: { - callbacks: { - label: function (tooltipItem, data) { - "use strict"; - var value = data.datasets[0].data[tooltipItem.index]; - return data.labels[tooltipItem.index] + ': ' + accounting.formatMoney(value); - } - } - }, - maintainAspectRatio: true, - responsive: true -}; - - -var defaultLineOptions = { - scales: { - xAxes: [ - { - gridLines: { - display: false - } - } - ], - yAxes: [{ - display: true, - ticks: { - callback: function (tickValue, index, ticks) { - "use strict"; - return accounting.formatMoney(tickValue); - - } - } - }] - }, - tooltips: { - mode: 'label', - callbacks: { - label: function (tooltipItem, data) { - "use strict"; - return data.datasets[tooltipItem.datasetIndex].label + ': ' + accounting.formatMoney(tooltipItem.yLabel); - } - } - } -}; - -var defaultColumnOptions = { - scales: { - xAxes: [ - { - gridLines: { - display: false - } - } - ], - yAxes: [{ - ticks: { - callback: function (tickValue, index, ticks) { - "use strict"; - return accounting.formatMoney(tickValue); - }, - beginAtZero: true - } - }] - }, - elements: { - line: { - fill: false - } - }, - tooltips: { - mode: 'label', - callbacks: { - label: function (tooltipItem, data) { - "use strict"; - return data.datasets[tooltipItem.datasetIndex].label + ': ' + accounting.formatMoney(tooltipItem.yLabel); - } - } - } -}; - -var defaultStackedColumnOptions = { - stacked: true, - scales: { - xAxes: [{ - stacked: true, - gridLines: { - display: false - } - }], - yAxes: [{ - stacked: true, - ticks: { - callback: function (tickValue, index, ticks) { - "use strict"; - return accounting.formatMoney(tickValue); - - } - } - }] - }, - tooltips: { - mode: 'label', - callbacks: { - label: function (tooltipItem, data) { - "use strict"; - return data.datasets[tooltipItem.datasetIndex].label + ': ' + accounting.formatMoney(tooltipItem.yLabel); - } - } - } -}; /** - * Function to draw a line chart: - * @param URL - * @param container - * @param options + * + * @param data + * @returns {{}} */ -function lineChart(URL, container, options) { - "use strict"; - $.getJSON(URL).done(function (data) { +function colorizeData(data) { + var newData = {}; + newData.datasets = []; - var ctx = document.getElementById(container).getContext("2d"); - var newData = {}; - newData.datasets = []; - - for (var i = 0; i < data.count; i++) { - newData.labels = data.labels; - var dataset = data.datasets[i]; - dataset.backgroundColor = fillColors[i]; - newData.datasets.push(dataset); - } - - new Chart(ctx, { - type: 'line', - data: data, - options: defaultLineOptions - }); - - }).fail(function () { - $('#' + container).addClass('general-chart-error'); - }); - console.log('URL for line chart : ' + URL); + for (var i = 0; i < data.count; i++) { + newData.labels = data.labels; + var dataset = data.datasets[i]; + dataset.backgroundColor = fillColors[i]; + newData.datasets.push(dataset); + } + return newData; } /** - * Function to draw an area chart: - * - * @param URL + * @param URI * @param container + * @param chartType * @param options + * @param colorData */ -function areaChart(URL, container, options) { - "use strict"; - +function drawAChart(URI, container, chartType, options, colorData) { if ($('#' + container).length === 0) { console.log('No container called ' + container + ' was found.'); return; } - $.getJSON(URL).done(function (data) { - var ctx = document.getElementById(container).getContext("2d"); - var newData = {}; - newData.datasets = []; + // var result = true; + // if (options.beforeDraw) { + // result = options.beforeDraw(data, {url: URL, container: container}); + // } + // if (result === false) { + // return; + // } - for (var i = 0; i < data.count; i++) { - newData.labels = data.labels; - var dataset = data.datasets[i]; - dataset.backgroundColor = fillColors[i]; - newData.datasets.push(dataset); + $.getJSON(URI).done(function (data) { + + if (colorData) { + data = colorizeData(data); } - new Chart(ctx, { - type: 'line', - data: newData, - options: defaultAreaOptions - }); - - }).fail(function () { - $('#' + container).addClass('general-chart-error'); - }); - - console.log('URL for area chart: ' + URL); -} - -/** - * - * @param URL - * @param container - * @param options - */ -function columnChart(URL, container, options) { - "use strict"; - - options = options || {}; - - $.getJSON(URL).done(function (data) { - - var result = true; - if (options.beforeDraw) { - result = options.beforeDraw(data, {url: URL, container: container}); - } - if (result === false) { - return; - } - console.log('Will draw columnChart(' + URL + ')'); - - var ctx = document.getElementById(container).getContext("2d"); - var newData = {}; - newData.datasets = []; - - for (var i = 0; i < data.count; i++) { - newData.labels = data.labels; - var dataset = data.datasets[i]; - dataset.backgroundColor = fillColors[i]; - newData.datasets.push(dataset); - } - new Chart(ctx, { - type: 'bar', - data: data, - options: defaultColumnOptions - }); - - }).fail(function () { - $('#' + container).addClass('general-chart-error'); - }); - console.log('URL for column chart : ' + URL); -} - -/** - * - * @param URL - * @param container - * @param options - */ -function stackedColumnChart(URL, container, options) { - "use strict"; - - options = options || {}; - - - $.getJSON(URL).done(function (data) { - - var result = true; - if (options.beforeDraw) { - result = options.beforeDraw(data, {url: URL, container: container}); - } - if (result === false) { - return; - } - - - var ctx = document.getElementById(container).getContext("2d"); - var newData = {}; - newData.datasets = []; - - for (var i = 0; i < data.count; i++) { - newData.labels = data.labels; - var dataset = data.datasets[i]; - dataset.backgroundColor = fillColors[i]; - newData.datasets.push(dataset); - } - new Chart(ctx, { - type: 'bar', - data: data, - options: defaultStackedColumnOptions - }); - - - }).fail(function () { - $('#' + container).addClass('general-chart-error'); - }); - console.log('URL for stacked column chart : ' + URL); -} - -/** - * - * @param URL - * @param container - * @param options - */ -function pieChart(URL, container, options) { - "use strict"; - - if ($('#' + container).length === 0) { - console.log('No container called ' + container + ' was found.'); - return; - } - - $.getJSON(URL).done(function (data) { - if (allCharts.hasOwnProperty(container)) { - console.log('Will draw updated pie chart'); - + console.log('Will draw updated ' + chartType + ' chart'); allCharts[container].data.datasets = data.datasets; allCharts[container].data.labels = data.labels; allCharts[container].update(); } else { // new chart! - console.log('Will draw new pie chart'); + console.log('Will draw new ' + chartType + 'chart'); var ctx = document.getElementById(container).getContext("2d"); allCharts[container] = new Chart(ctx, { - type: 'pie', + type: chartType, data: data, - options: defaultPieOptions + options: options }); } - }).fail(function () { + console.log('Failed to draw ' + chartType + ' in container ' + container); $('#' + container).addClass('general-chart-error'); }); + console.log('URL for ' + chartType + ' chart : ' + URL); +} - console.log('URL for pie chart : ' + URL); +/** + * Function to draw a line chart: + * @param URI + * @param container + */ +function lineChart(URI, container) { + "use strict"; + + var colorData = true; + var options = defaultChartOptions; + var chartType = 'line'; + + drawAChart(URI, container, chartType, options, colorData); +} + +/** + * + * @param URI + * @param container + */ +function columnChart(URI, container) { + "use strict"; + + var colorData = true; + var options = defaultChartOptions; + var chartType = 'bar'; + + drawAChart(URI, container, chartType, options, colorData); + +} + +/** + * + * @param URI + * @param container + */ +function stackedColumnChart(URI, container) { + "use strict"; + + var colorData = true; + var options = defaultChartOptions; + + options.stacked = true; + options.scales.xAxes[0].stacked = true; + + var chartType = 'bar'; + + drawAChart(URI, container, chartType, options, colorData); +} + +/** + * + * @param URI + * @param container + */ +function pieChart(URI, container) { + "use strict"; + + var colorData = false; + var options = defaultPieOptions; + var chartType = 'pie'; + + drawAChart(URI, container, chartType, options, colorData); } diff --git a/public/js/ff/index.js b/public/js/ff/index.js index a6a7e5d795..3621132011 100644 --- a/public/js/ff/index.js +++ b/public/js/ff/index.js @@ -1,4 +1,4 @@ - /* globals $, columnChart,showTour, Tour, google, lineChart, pieChart, stackedColumnChart, areaChart */ + /* globals $, columnChart,showTour, Tour, google, pieChart, stackedColumnChart */ $(function () { "use strict"; @@ -32,38 +32,38 @@ function endTheTour() { function drawChart() { "use strict"; - areaChart('chart/account/frontpage', 'accounts-chart'); + lineChart('chart/account/frontpage', 'accounts-chart'); pieChart('chart/bill/frontpage', 'bills-chart'); - stackedColumnChart('chart/budget/frontpage', 'budgets-chart', {beforeDraw: beforeDrawIsEmpty}); - columnChart('chart/category/frontpage', 'categories-chart', {beforeDraw: beforeDrawIsEmpty}); - columnChart('chart/account/expense', 'expense-accounts-chart', {beforeDraw: beforeDrawIsEmpty}); - columnChart('chart/account/revenue', 'revenue-accounts-chart', {beforeDraw: beforeDrawIsEmpty}); + stackedColumnChart('chart/budget/frontpage', 'budgets-chart'); + columnChart('chart/category/frontpage', 'categories-chart'); + columnChart('chart/account/expense', 'expense-accounts-chart'); + columnChart('chart/account/revenue', 'revenue-accounts-chart'); getBoxAmounts(); } -/** - * Removes a chart container if there is nothing for the chart to draw. - * - * @param data - * @param options - * @returns {boolean} - */ -function beforeDrawIsEmpty(data, options) { - "use strict"; - - // check if chart holds data. - if (data.labels.length === 0) { - // remove the chart container + parent - console.log(options.container + ' appears empty. Removed.'); - $('#' + options.container).parent().parent().remove(); - - // return false so script stops. - return false; - } - return true; -} +// /** +// * Removes a chart box if there is nothing for the chart to draw. +// * +// * @param data +// * @param options +// * @returns {boolean} +// */ +// function beforeDrawIsEmpty(data, options) { +// "use strict"; +// +// // check if chart holds data. +// if (data.labels.length === 0) { +// // remove the chart container + parent +// console.log(options.container + ' appears empty. Removed.'); +// $('#' + options.container).parent().parent().remove(); +// +// // return false so script stops. +// return false; +// } +// return true; +// } function getBoxAmounts() { diff --git a/public/js/ff/reports/default/all.js b/public/js/ff/reports/default/all.js index 9ab3c6c8d7..3b47c14cf9 100644 --- a/public/js/ff/reports/default/all.js +++ b/public/js/ff/reports/default/all.js @@ -130,63 +130,9 @@ function clickBudgetChart(e) { "use strict"; var link = $(e.target); var budgetId = link.data('budget'); + var URL = 'chart/budget/period/' + budgetId + '/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds; var container = 'budget_chart'; - // if chart drawn is false, draw the first one, then - // set to true - if (chartDrawn == false) { - // do new chart: - - - $.getJSON(URL).done(function (data) { - console.log('Will draw new columnChart(' + URL + ')'); - - var ctx = document.getElementById(container).getContext("2d"); - var newData = {}; - newData.datasets = []; - - for (var i = 0; i < data.count; i++) { - newData.labels = data.labels; - var dataset = data.datasets[i]; - dataset.backgroundColor = fillColors[i]; - newData.datasets.push(dataset); - } - // completely new chart. - budgetChart = new Chart(ctx, { - type: 'bar', - data: data, - options: defaultColumnOptions - }); - - }).fail(function () { - $('#' + container).addClass('general-chart-error'); - }); - console.log('URL for column chart : ' + URL); - chartDrawn = true; - } else { - console.log('Will now handle remove data and add new!'); - $.getJSON(URL).done(function (data) { - console.log('Will draw updated columnChart(' + URL + ')'); - var newData = {}; - newData.datasets = []; - - for (var i = 0; i < data.count; i++) { - newData.labels = data.labels; - var dataset = data.datasets[i]; - dataset.backgroundColor = fillColors[i]; - newData.datasets.push(dataset); - } - // update the chart - console.log('Now update chart thing.'); - budgetChart.data.datasets = newData.datasets; - budgetChart.update(); - - }).fail(function () { - $('#' + container).addClass('general-chart-error'); - }); - - - } - + columnChart(URL, container); return false; } \ No newline at end of file diff --git a/public/js/ff/reports/default/multi-year.js b/public/js/ff/reports/default/multi-year.js index 93ecb50039..0c8213785b 100644 --- a/public/js/ff/reports/default/multi-year.js +++ b/public/js/ff/reports/default/multi-year.js @@ -12,7 +12,7 @@ function drawChart() { "use strict"; // income and expense over multi year: - lineChart('chart/report/net-worth/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'net-worth'); - columnChart('chart/report/in-out/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-chart'); - columnChart('chart/report/in-out-sum/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-sum-chart'); + lineChart('chart/report/net-worth/' + startDate + '/' + endDate + '/' + accountIds, 'net-worth'); + columnChart('chart/report/in-out/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-chart'); + columnChart('chart/report/in-out-sum/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-sum-chart'); } diff --git a/public/js/ff/reports/default/year.js b/public/js/ff/reports/default/year.js index 1e2893f8e5..a28ac3f8c4 100644 --- a/public/js/ff/reports/default/year.js +++ b/public/js/ff/reports/default/year.js @@ -1,10 +1,7 @@ /* globals google, accountIds, budgetYearOverviewUri */ -var chartDrawn; -var budgetChart; $(function () { "use strict"; - chartDrawn = false; drawChart(); loadAjaxPartial('budgetOverview',budgetYearOverviewUri); diff --git a/public/js/ff/reports/index.js b/public/js/ff/reports/index.js index 9eb918d86a..bd949eeac6 100644 --- a/public/js/ff/reports/index.js +++ b/public/js/ff/reports/index.js @@ -18,7 +18,8 @@ $(function () { ); // set values from cookies, if any: - if (readCookie('report-type') !== null) { + if (!(readCookie('report-type') === null)) { + console.log(readCookie('report-type')); $('select[name="report_type"]').val(readCookie('report-type')); } diff --git a/resources/views/accounts/show.twig b/resources/views/accounts/show.twig index ac18d8a0cd..b69befe609 100644 --- a/resources/views/accounts/show.twig +++ b/resources/views/accounts/show.twig @@ -81,6 +81,7 @@ var accountID = {{ account.id }}; + diff --git a/resources/views/accounts/show_with_date.twig b/resources/views/accounts/show_with_date.twig index 957e3b5e98..ae3011c52f 100644 --- a/resources/views/accounts/show_with_date.twig +++ b/resources/views/accounts/show_with_date.twig @@ -55,6 +55,7 @@ var dateString = "{{ date }}"; + diff --git a/resources/views/bills/show.twig b/resources/views/bills/show.twig index e2ea0f42a8..f2db32ebf2 100644 --- a/resources/views/bills/show.twig +++ b/resources/views/bills/show.twig @@ -119,6 +119,7 @@ var billID = {{ bill.id }}; + diff --git a/resources/views/budgets/show.twig b/resources/views/budgets/show.twig index b0331ae279..a85ae2061b 100644 --- a/resources/views/budgets/show.twig +++ b/resources/views/budgets/show.twig @@ -107,6 +107,7 @@ + diff --git a/resources/views/categories/index.twig b/resources/views/categories/index.twig index 18a1e08314..d4cfc7c335 100644 --- a/resources/views/categories/index.twig +++ b/resources/views/categories/index.twig @@ -35,6 +35,7 @@ {% block scripts %} + diff --git a/resources/views/categories/show.twig b/resources/views/categories/show.twig index 311b857d6e..f6b18c08b4 100644 --- a/resources/views/categories/show.twig +++ b/resources/views/categories/show.twig @@ -76,6 +76,7 @@ var categoryID = {{ category.id }}; + diff --git a/resources/views/categories/show_with_date.twig b/resources/views/categories/show_with_date.twig index 624a933957..2514b08214 100644 --- a/resources/views/categories/show_with_date.twig +++ b/resources/views/categories/show_with_date.twig @@ -44,6 +44,7 @@ var categoryDate = "{{ carbon.format('Y-m-d') }}"; + diff --git a/resources/views/index.twig b/resources/views/index.twig index f50e0e1c12..1e72dc442f 100644 --- a/resources/views/index.twig +++ b/resources/views/index.twig @@ -132,6 +132,7 @@ + {% endblock %} diff --git a/resources/views/piggy-banks/show.twig b/resources/views/piggy-banks/show.twig index 5e240a242d..031aefb4ba 100644 --- a/resources/views/piggy-banks/show.twig +++ b/resources/views/piggy-banks/show.twig @@ -107,6 +107,7 @@ + {% endblock %} diff --git a/resources/views/reports/category/month.twig b/resources/views/reports/category/month.twig index 46357c569d..2048b765b3 100644 --- a/resources/views/reports/category/month.twig +++ b/resources/views/reports/category/month.twig @@ -264,6 +264,7 @@ {% block scripts %} + + + + + @@ -284,4 +378,5 @@ {% endblock %} {% block styles %} + {% endblock %} \ No newline at end of file diff --git a/resources/views/reports/partials/categories.twig b/resources/views/reports/partials/categories.twig index a09cbe2c7c..281fd6ca5f 100644 --- a/resources/views/reports/partials/categories.twig +++ b/resources/views/reports/partials/categories.twig @@ -25,7 +25,7 @@ {% endfor %} - {% if categories.getCategories.count > expenseTopLength %} + {% if categories.getCategories.count > listLength %} {{ trans('firefly.show_full_list',{number:incomeTopLength}) }} diff --git a/resources/views/reports/partials/expenses.twig b/resources/views/reports/partials/expenses.twig index 3491b33564..f3cf12e4aa 100644 --- a/resources/views/reports/partials/expenses.twig +++ b/resources/views/reports/partials/expenses.twig @@ -24,7 +24,7 @@ {% endfor %} - {% if expenses.getExpenses|length > expenseTopLength %} + {% if expenses.getExpenses|length > listLength %} {{ trans('firefly.show_full_list',{number:incomeTopLength}) }} From e10fc4a854dd1f9808253e88cee4e3be44337b8c Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 17 Nov 2016 20:04:53 +0100 Subject: [PATCH 100/709] Forgotten a translation. --- resources/lang/en_US/firefly.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index db1eaa3010..5935bdfc48 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -702,6 +702,10 @@ return [ 'transaction_count' => 'Transaction count', 'average_spending_per_account' => 'Average spending per account', 'average_income_per_account' => 'Average income per account', + 'total' => 'Total', + 'description' => 'Description', + + // charts: 'chart' => 'Chart', 'dayOfMonth' => 'Day of the month', From a31926442821a9c46429441b4d8eb6a2f49149ea Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 18 Nov 2016 18:58:48 +0100 Subject: [PATCH 101/709] fixed #406 --- app/Repositories/Journal/JournalRepository.php | 18 ++++++++++++++++-- public/js/ff/split/journal/from-store.js | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php index 0e8c73942d..1643007904 100644 --- a/app/Repositories/Journal/JournalRepository.php +++ b/app/Repositories/Journal/JournalRepository.php @@ -510,20 +510,28 @@ class JournalRepository implements JournalRepositoryInterface */ private function storeDepositAccounts(array $data): array { + Log::debug('Now in storeDepositAccounts().'); $destinationAccount = Account::where('user_id', $this->user->id)->where('id', $data['destination_account_id'])->first(['accounts.*']); + Log::debug(sprintf('Destination account is #%d ("%s")', $destinationAccount->id, $destinationAccount->name)); + if (strlen($data['source_account_name']) > 0) { $sourceType = AccountType::where('type', 'Revenue account')->first(); $sourceAccount = Account::firstOrCreateEncrypted( ['user_id' => $this->user->id, 'account_type_id' => $sourceType->id, 'name' => $data['source_account_name'], 'active' => 1] ); + Log::debug(sprintf('source account name is "%s", account is %d', $data['source_account_name'], $sourceAccount->id)); + return [ 'source' => $sourceAccount, 'destination' => $destinationAccount, ]; } - $sourceType = AccountType::where('type', 'Cash account')->first(); + + Log::debug('source_account_name is empty, so default to cash account!'); + + $sourceType = AccountType::where('type', AccountType::CASH)->first(); $sourceAccount = Account::firstOrCreateEncrypted( ['user_id' => $this->user->id, 'account_type_id' => $sourceType->id, 'name' => 'Cash account', 'active' => 1] ); @@ -618,8 +626,11 @@ class JournalRepository implements JournalRepositoryInterface */ private function storeWithdrawalAccounts(array $data): array { + Log::debug('Now in storeWithdrawalAccounts().'); $sourceAccount = Account::where('user_id', $this->user->id)->where('id', $data['source_account_id'])->first(['accounts.*']); + Log::debug(sprintf('Source account is #%d ("%s")', $sourceAccount->id, $sourceAccount->name)); + if (strlen($data['destination_account_name']) > 0) { $destinationType = AccountType::where('type', AccountType::EXPENSE)->first(); $destinationAccount = Account::firstOrCreateEncrypted( @@ -631,12 +642,15 @@ class JournalRepository implements JournalRepositoryInterface ] ); + Log::debug(sprintf('destination account name is "%s", account is %d', $data['destination_account_name'], $destinationAccount->id)); + return [ 'source' => $sourceAccount, 'destination' => $destinationAccount, ]; } - $destinationType = AccountType::where('type', 'Cash account')->first(); + Log::debug('destination_account_name is empty, so default to cash account!'); + $destinationType = AccountType::where('type', AccountType::CASH)->first(); $destinationAccount = Account::firstOrCreateEncrypted( ['user_id' => $this->user->id, 'account_type_id' => $destinationType->id, 'name' => 'Cash account', 'active' => 1] ); diff --git a/public/js/ff/split/journal/from-store.js b/public/js/ff/split/journal/from-store.js index a6f8a1ef35..e8b99b09d4 100644 --- a/public/js/ff/split/journal/from-store.js +++ b/public/js/ff/split/journal/from-store.js @@ -148,7 +148,7 @@ function resetSplits() { // ends with ][source_account_name] $.each($('input[name$="][source_account_name]"]'), function (i, v) { var input = $(v); - input.attr('name', 'transaction[' + i + '][source_account_name]'); + input.attr('name', 'transactions[' + i + '][source_account_name]'); console.log('source_account_name is now ' + input.attr('name')); }); // ends with ][amount] From 884bed85a1afb328623d3aaf430b8020f6fce048 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 18 Nov 2016 19:54:21 +0100 Subject: [PATCH 102/709] Update composer file. --- composer.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/composer.json b/composer.json index feb695bcff..8bf9f1388a 100755 --- a/composer.json +++ b/composer.json @@ -30,24 +30,24 @@ "ext-intl": "*", "laravel/framework": "5.3.18", "davejamesmiller/laravel-breadcrumbs": "^3.0", - "watson/validating": "^3.0", + "watson/validating": "3.*", "doctrine/dbal": "^2.5", - "league/commonmark": "^0.15.0", - "rcrowe/twigbridge": "^0.9.3", - "league/csv": "^8.1", + "league/commonmark": "0.15.*", + "rcrowe/twigbridge": "0.9.*", + "league/csv": "8.*", "laravelcollective/html": "^5.3", - "rmccue/requests": "^1.6", - "pragmarx/google2fa": "^1.0", - "barryvdh/laravel-debugbar": "^2.2", - "barryvdh/laravel-ide-helper": "^2.2", - "bacon/bacon-qr-code": "^1.0" + "rmccue/requests": "1.*", + "pragmarx/google2fa": "1.*", + "bacon/bacon-qr-code": "1.*" }, "require-dev": { "fzaninotto/faker": "~1.4", "mockery/mockery": "0.9.*", "phpunit/phpunit": "~5.0", "symfony/css-selector": "3.1.*", - "symfony/dom-crawler": "3.1.*" + "symfony/dom-crawler": "3.1.*", + "barryvdh/laravel-debugbar": "2.*", + "barryvdh/laravel-ide-helper": "2.*" }, "autoload": { "classmap": [ From 0c072c7d51579f5b3a0bc31d2c4ac1b9efb84469 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 18 Nov 2016 19:58:06 +0100 Subject: [PATCH 103/709] Some code cleanup. --- app/Models/Account.php | 56 +-- app/Models/AccountMeta.php | 18 - app/Models/AccountType.php | 14 - app/Models/Attachment.php | 37 -- app/Models/Bill.php | 44 -- app/Models/Budget.php | 31 -- app/Models/BudgetLimit.php | 24 - app/Models/Category.php | 27 - app/Models/Configuration.php | 17 - app/Models/ExportJob.php | 19 - app/Models/ImportJob.php | 25 - app/Models/LimitRepetition.php | 24 - app/Models/Note.php | 23 - app/Models/PiggyBank.php | 38 -- app/Models/PiggyBankEvent.php | 21 - app/Models/PiggyBankRepetition.php | 22 - app/Models/Preference.php | 21 - app/Models/Role.php | 20 - app/Models/Rule.php | 32 -- app/Models/RuleAction.php | 25 - app/Models/RuleGroup.php | 26 - app/Models/RuleTrigger.php | 25 - app/Models/Tag.php | 33 -- app/Models/Transaction.php | 32 -- app/Models/TransactionCurrency.php | 20 - app/Models/TransactionGroup.php | 20 - app/Models/TransactionJournal.php | 71 --- app/Models/TransactionJournalMeta.php | 22 - app/Models/TransactionType.php | 16 - composer.lock | 692 +++++++++++++------------- config/app.php | 1 + 31 files changed, 363 insertions(+), 1133 deletions(-) diff --git a/app/Models/Account.php b/app/Models/Account.php index d9325d1dea..b38d14c2b0 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -26,47 +26,7 @@ use Illuminate\Database\Query\JoinClause; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Watson\Validating\ValidatingTrait; -/** - * FireflyIII\Models\Account - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property \Carbon\Carbon $deleted_at - * @property integer $user_id - * @property integer $account_type_id - * @property string $name - * @property boolean $active - * @property boolean $encrypted - * @property float $virtual_balance - * @property string $iban - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\AccountMeta[] $accountMeta - * @property-read \FireflyIII\Models\AccountType $accountType - * @property-read mixed $name_for_editform - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\PiggyBank[] $piggyBanks - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Transaction[] $transactions - * @property-read \FireflyIII\User $user - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account accountTypeIn($types) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account hasMetaValue($name, $value) - * @property string $startBalance - * @property string $endBalance - * @property float $difference - * @property \Carbon\Carbon $lastActivityDate - * @property float $piggyBalance - * @property float $percentage - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereDeletedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereUserId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereAccountTypeId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereName($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereActive($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereEncrypted($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereVirtualBalance($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Account whereIban($value) - * @mixin \Eloquent - */ + class Account extends Model { use SoftDeletes, ValidatingTrait; @@ -264,6 +224,20 @@ class Account extends Model return $journal->date; } + /** + * @return string + */ + public function nameForEdit(): string + { + $name = $this->name; + + if ($this->accountType->type === AccountType::CASH) { + return ''; + } + + return $name; + } + /** * @return HasMany */ diff --git a/app/Models/AccountMeta.php b/app/Models/AccountMeta.php index c6a2c6004a..6c92343c18 100644 --- a/app/Models/AccountMeta.php +++ b/app/Models/AccountMeta.php @@ -16,24 +16,6 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; -/** - * FireflyIII\Models\AccountMeta - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property integer $account_id - * @property string $name - * @property string $data - * @property-read Account $account - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountMeta whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountMeta whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountMeta whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountMeta whereAccountId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountMeta whereName($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountMeta whereData($value) - * @mixin \Eloquent - */ class AccountMeta extends Model { diff --git a/app/Models/AccountType.php b/app/Models/AccountType.php index 920b62a950..06a2c37003 100644 --- a/app/Models/AccountType.php +++ b/app/Models/AccountType.php @@ -16,20 +16,6 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; -/** - * FireflyIII\Models\AccountType - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property string $type - * @property-read \Illuminate\Database\Eloquent\Collection|Account[] $accounts - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountType whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountType whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountType whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\AccountType whereType($value) - * @mixin \Eloquent - */ class AccountType extends Model { const DEFAULT = 'Default account'; diff --git a/app/Models/Attachment.php b/app/Models/Attachment.php index 85d21c16c8..6c798372d8 100644 --- a/app/Models/Attachment.php +++ b/app/Models/Attachment.php @@ -20,43 +20,6 @@ use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -/** - * FireflyIII\Models\Attachment - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property string $deleted_at - * @property integer $attachable_id - * @property string $attachable_type - * @property integer $user_id - * @property string $md5 - * @property string $filename - * @property string $title - * @property string $description - * @property string $notes - * @property string $mime - * @property integer $size - * @property boolean $uploaded - * @property-read Attachment $attachable - * @property-read \FireflyIII\User $user - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereDeletedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereAttachableId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereAttachableType($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereUserId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereMd5($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereFilename($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereTitle($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereDescription($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereNotes($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereMime($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereSize($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Attachment whereUploaded($value) - * @mixin \Eloquent - */ class Attachment extends Model { use SoftDeletes; diff --git a/app/Models/Bill.php b/app/Models/Bill.php index d13f6d372e..ab9f37e99e 100644 --- a/app/Models/Bill.php +++ b/app/Models/Bill.php @@ -20,50 +20,6 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Watson\Validating\ValidatingTrait; -/** - * FireflyIII\Models\Bill - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property integer $user_id - * @property string $name - * @property string $match - * @property float $amount_min - * @property float $amount_max - * @property \Carbon\Carbon $date - * @property boolean $active - * @property boolean $automatch - * @property string $repeat_freq - * @property integer $skip - * @property boolean $name_encrypted - * @property boolean $match_encrypted - * @property-read \Illuminate\Database\Eloquent\Collection|TransactionJournal[] $transactionjournals - * @property-read \FireflyIII\User $user - * @property \Carbon\Carbon $nextExpectedMatch - * @property \Carbon\Carbon $lastFoundMatch - * @property bool $paidInPeriod - * @property string $lastPaidAmount - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereUserId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereName($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereMatch($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereAmountMin($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereAmountMax($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereDate($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereActive($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereAutomatch($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereRepeatFreq($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereSkip($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereNameEncrypted($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereMatchEncrypted($value) - * @mixin \Eloquent - * @property string $deleted_at - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereDeletedAt($value) - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\TransactionJournal[] $transactionJournals - */ class Bill extends Model { diff --git a/app/Models/Budget.php b/app/Models/Budget.php index db69a0da47..ba8cddea4f 100644 --- a/app/Models/Budget.php +++ b/app/Models/Budget.php @@ -20,37 +20,6 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Watson\Validating\ValidatingTrait; -/** - * FireflyIII\Models\Budget - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property \Carbon\Carbon $deleted_at - * @property string $name - * @property integer $user_id - * @property boolean $active - * @property boolean $encrypted - * @property-read \Illuminate\Database\Eloquent\Collection|BudgetLimit[] $budgetlimits - * @property-read \Illuminate\Database\Eloquent\Collection|TransactionJournal[] $transactionjournals - * @property-read \FireflyIII\User $user - * @property string $dateFormatted - * @property string $budgeted - * @property float $amount - * @property \Carbon\Carbon $date - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Budget whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Budget whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Budget whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Budget whereDeletedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Budget whereName($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Budget whereUserId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Budget whereActive($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Budget whereEncrypted($value) - * @mixin \Eloquent - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Transaction[] $transactions - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\LimitRepetition[] $limitrepetitions - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\TransactionJournal[] $transactionJournals - */ class Budget extends Model { diff --git a/app/Models/BudgetLimit.php b/app/Models/BudgetLimit.php index fa1ab82f95..9a2843b1b4 100644 --- a/app/Models/BudgetLimit.php +++ b/app/Models/BudgetLimit.php @@ -15,30 +15,6 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; -/** - * FireflyIII\Models\BudgetLimit - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property integer $budget_id - * @property \Carbon\Carbon $startdate - * @property float $amount - * @property boolean $repeats - * @property string $repeat_freq - * @property-read Budget $budget - * @property int $component_id - * @property-read \Illuminate\Database\Eloquent\Collection|LimitRepetition[] $limitrepetitions - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\BudgetLimit whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\BudgetLimit whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\BudgetLimit whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\BudgetLimit whereBudgetId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\BudgetLimit whereStartdate($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\BudgetLimit whereAmount($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\BudgetLimit whereRepeats($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\BudgetLimit whereRepeatFreq($value) - * @mixin \Eloquent - */ class BudgetLimit extends Model { diff --git a/app/Models/Category.php b/app/Models/Category.php index c51dabc025..5b12d93558 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -20,33 +20,6 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Watson\Validating\ValidatingTrait; -/** - * FireflyIII\Models\Category - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property \Carbon\Carbon $deleted_at - * @property string $name - * @property integer $user_id - * @property boolean $encrypted - * @property-read \Illuminate\Database\Eloquent\Collection|TransactionJournal[] $transactionjournals - * @property-read \FireflyIII\User $user - * @property string $dateFormatted - * @property string $spent - * @property \Carbon\Carbon $lastActivity - * @property string $type - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereDeletedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereName($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereUserId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereEncrypted($value) - * @mixin \Eloquent - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Transaction[] $transactions - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\TransactionJournal[] $transactionJournals - */ class Category extends Model { use SoftDeletes, ValidatingTrait; diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index d74eb7255d..4b10d83860 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -16,23 +16,6 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; -/** - * FireflyIII\Models\Configuration - * - * @mixin \Eloquent - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property \Carbon\Carbon $deleted_at - * @property string $name - * @property string $data - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Configuration whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Configuration whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Configuration whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Configuration whereDeletedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Configuration whereName($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Configuration whereData($value) - */ class Configuration extends Model { use SoftDeletes; diff --git a/app/Models/ExportJob.php b/app/Models/ExportJob.php index 4e9997abe1..5111f8918d 100644 --- a/app/Models/ExportJob.php +++ b/app/Models/ExportJob.php @@ -16,25 +16,6 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -/** - * Class ExportJob - * - * @package FireflyIII - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property integer $user_id - * @property string $key - * @property string $status - * @property-read \FireflyIII\User $user - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ExportJob whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ExportJob whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ExportJob whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ExportJob whereUserId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ExportJob whereKey($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ExportJob whereStatus($value) - * @mixin \Eloquent - */ class ExportJob extends Model { /** diff --git a/app/Models/ImportJob.php b/app/Models/ImportJob.php index c7ba8c4427..75066b734d 100644 --- a/app/Models/ImportJob.php +++ b/app/Models/ImportJob.php @@ -18,31 +18,6 @@ use Illuminate\Database\Eloquent\Model; use Storage; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; - -/** - * FireflyIII\Models\ImportJob - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property integer $user_id - * @property string $key - * @property string $file_type - * @property string $status - * @property array $configuration - * @property-read \FireflyIII\User $user - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereUserId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereKey($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereFileType($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereStatus($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereConfiguration($value) - * @mixin \Eloquent - * @property string $extended_status - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereExtendedStatus($value) - */ class ImportJob extends Model { diff --git a/app/Models/LimitRepetition.php b/app/Models/LimitRepetition.php index 9d2dc5b416..4732b9e950 100644 --- a/app/Models/LimitRepetition.php +++ b/app/Models/LimitRepetition.php @@ -18,30 +18,6 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -/** - * FireflyIII\Models\LimitRepetition - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property integer $budget_limit_id - * @property \Carbon\Carbon $startdate - * @property \Carbon\Carbon $enddate - * @property float $amount - * @property-read BudgetLimit $budgetLimit - * @property int $budget_id - * @property string $spent - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\LimitRepetition whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\LimitRepetition whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\LimitRepetition whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\LimitRepetition whereBudgetLimitId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\LimitRepetition whereStartdate($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\LimitRepetition whereEnddate($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\LimitRepetition whereAmount($value) - * @mixin \Eloquent - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\LimitRepetition after($date) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\LimitRepetition before($date) - */ class LimitRepetition extends Model { diff --git a/app/Models/Note.php b/app/Models/Note.php index c26a397421..4b2c3df254 100644 --- a/app/Models/Note.php +++ b/app/Models/Note.php @@ -16,29 +16,6 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; use League\CommonMark\CommonMarkConverter; - -/** - * FireflyIII\Models\Note - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property string $deleted_at - * @property integer $noteable_id - * @property string $noteable_type - * @property string $title - * @property string $text - * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent $noteable - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Note whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Note whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Note whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Note whereDeletedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Note whereNoteableId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Note whereNoteableType($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Note whereTitle($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Note whereText($value) - * @mixin \Eloquent - */ class Note extends Model { protected $dates = ['created_at', 'updated_at', 'deleted_at']; diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index 9a57917b26..d0c001fb23 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -20,44 +20,6 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Steam; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -/** - * FireflyIII\Models\PiggyBank - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property \Carbon\Carbon $deleted_at - * @property integer $account_id - * @property string $name - * @property float $targetamount - * @property \Carbon\Carbon $startdate - * @property \Carbon\Carbon $targetdate - * @property integer $order - * @property boolean $encrypted - * @property-read Account $account - * @property-read \Illuminate\Database\Eloquent\Collection|PiggyBankRepetition[] $piggyBankRepetitions - * @property-read \Illuminate\Database\Eloquent\Collection|PiggyBankEvent[] $piggyBankEvents - * @property string $reminder - * @property PiggyBankRepetition $currentRep - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereAccountId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereName($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereTargetamount($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereStartdate($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereTargetdate($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereReminder($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereReminderSkip($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereRemindMe($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereOrder($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereDeletedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereEncrypted($value) - * @mixin \Eloquent - * @property boolean $active - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereActive($value) - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Note[] $notes - */ class PiggyBank extends Model { use SoftDeletes; diff --git a/app/Models/PiggyBankEvent.php b/app/Models/PiggyBankEvent.php index 5e959c66c9..b8f589b55f 100644 --- a/app/Models/PiggyBankEvent.php +++ b/app/Models/PiggyBankEvent.php @@ -15,27 +15,6 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; -/** - * FireflyIII\Models\PiggyBankEvent - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property integer $piggy_bank_id - * @property integer $transaction_journal_id - * @property \Carbon\Carbon $date - * @property float $amount - * @property PiggyBank $piggyBank - * @property-read TransactionJournal $transactionJournal - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankEvent whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankEvent whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankEvent whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankEvent wherePiggyBankId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankEvent whereTransactionJournalId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankEvent whereDate($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankEvent whereAmount($value) - * @mixin \Eloquent - */ class PiggyBankEvent extends Model { diff --git a/app/Models/PiggyBankRepetition.php b/app/Models/PiggyBankRepetition.php index 57aff06279..a6a7ed41b5 100644 --- a/app/Models/PiggyBankRepetition.php +++ b/app/Models/PiggyBankRepetition.php @@ -17,28 +17,6 @@ use Carbon\Carbon; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Model; -/** - * FireflyIII\Models\PiggyBankRepetition - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property integer $piggy_bank_id - * @property \Carbon\Carbon $startdate - * @property \Carbon\Carbon $targetdate - * @property float $currentamount - * @property-read PiggyBank $piggyBank - * @method static \Illuminate\Database\Query\Builder|PiggyBankRepetition onDates($start, $target) - * @method static \Illuminate\Database\Query\Builder|PiggyBankRepetition relevantOnDate($date) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankRepetition whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankRepetition whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankRepetition whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankRepetition wherePiggyBankId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankRepetition whereStartdate($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankRepetition whereTargetdate($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBankRepetition whereCurrentamount($value) - * @mixin \Eloquent - */ class PiggyBankRepetition extends Model { diff --git a/app/Models/Preference.php b/app/Models/Preference.php index 7a843e2470..5909ef55aa 100644 --- a/app/Models/Preference.php +++ b/app/Models/Preference.php @@ -19,27 +19,6 @@ use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Database\Eloquent\Model; use Log; -/** - * FireflyIII\Models\Preference - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property integer $user_id - * @property string $name - * @property string $name_encrypted - * @property string $data - * @property-read \FireflyIII\User $user - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Preference whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Preference whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Preference whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Preference whereUserId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Preference whereName($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Preference whereNameEncrypted($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Preference whereData($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Preference whereDataEncrypted($value) - * @mixin \Eloquent - */ class Preference extends Model { diff --git a/app/Models/Role.php b/app/Models/Role.php index c560cd6e3c..b400b69394 100644 --- a/app/Models/Role.php +++ b/app/Models/Role.php @@ -16,26 +16,6 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; - -/** - * Class Role - * - * @package FireflyIII\Models - * @property integer $id - * @property string $name - * @property string $display_name - * @property string $description - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\User[] $users - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Role whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Role whereName($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Role whereDisplayName($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Role whereDescription($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Role whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Role whereUpdatedAt($value) - * @mixin \Eloquent - */ class Role extends Model { diff --git a/app/Models/Rule.php b/app/Models/Rule.php index 43d3631773..3e965f1f40 100644 --- a/app/Models/Rule.php +++ b/app/Models/Rule.php @@ -17,38 +17,6 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -/** - * Class Rule - * - * @package FireflyIII\Models - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property string $deleted_at - * @property integer $user_id - * @property integer $rule_group_id - * @property integer $order - * @property string $title - * @property string $description - * @property boolean $active - * @property boolean $stop_processing - * @property-read \FireflyIII\User $user - * @property-read \FireflyIII\Models\RuleGroup $ruleGroup - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\RuleAction[] $ruleActions - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\RuleTrigger[] $ruleTriggers - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Rule whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Rule whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Rule whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Rule whereDeletedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Rule whereUserId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Rule whereRuleGroupId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Rule whereOrder($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Rule whereActive($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Rule whereStopProcessing($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Rule whereTitle($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Rule whereDescription($value) - * @mixin \Eloquent - */ class Rule extends Model { use SoftDeletes; diff --git a/app/Models/RuleAction.php b/app/Models/RuleAction.php index 2e13c89b9f..271aaf1164 100644 --- a/app/Models/RuleAction.php +++ b/app/Models/RuleAction.php @@ -22,31 +22,6 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; - -/** - * FireflyIII\Models\RuleAction - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property integer $rule_id - * @property integer $order - * @property boolean $active - * @property boolean $stop_processing - * @property string $action_type - * @property string $action_value - * @property-read \FireflyIII\Models\Rule $rule - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleAction whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleAction whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleAction whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleAction whereRuleId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleAction whereOrder($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleAction whereActive($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleAction whereStopProcessing($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleAction whereActionType($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleAction whereActionValue($value) - * @mixin \Eloquent - */ class RuleAction extends Model { /** diff --git a/app/Models/RuleGroup.php b/app/Models/RuleGroup.php index 532ca0eba2..e89a88ee8d 100644 --- a/app/Models/RuleGroup.php +++ b/app/Models/RuleGroup.php @@ -17,32 +17,6 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -/** - * Class RuleGroup - * - * @package FireflyIII\Models - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property string $deleted_at - * @property integer $user_id - * @property integer $order - * @property string $title - * @property string $description - * @property boolean $active - * @property-read \FireflyIII\User $user - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Rule[] $rules - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleGroup whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleGroup whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleGroup whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleGroup whereDeletedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleGroup whereUserId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleGroup whereOrder($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleGroup whereTitle($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleGroup whereDescription($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleGroup whereActive($value) - * @mixin \Eloquent - */ class RuleGroup extends Model { use SoftDeletes; diff --git a/app/Models/RuleTrigger.php b/app/Models/RuleTrigger.php index 684d9a9509..399b262c12 100644 --- a/app/Models/RuleTrigger.php +++ b/app/Models/RuleTrigger.php @@ -15,31 +15,6 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; -/** - * FireflyIII\Models\RuleTrigger - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property integer $rule_id - * @property integer $order - * @property string $title - * @property string $trigger_type - * @property string $trigger_value - * @property boolean $active - * @property boolean $stop_processing - * @property-read \FireflyIII\Models\Rule $rule - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleTrigger whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleTrigger whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleTrigger whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleTrigger whereRuleId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleTrigger whereOrder($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleTrigger whereActive($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleTrigger whereStopProcessing($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleTrigger whereTriggerType($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\RuleTrigger whereTriggerValue($value) - * @mixin \Eloquent - */ class RuleTrigger extends Model { /** diff --git a/app/Models/Tag.php b/app/Models/Tag.php index a89220efea..d69c13eb68 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -18,39 +18,6 @@ use FireflyIII\Support\Models\TagSupport; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Watson\Validating\ValidatingTrait; -/** - * FireflyIII\Models\Tag - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property string $deleted_at - * @property integer $user_id - * @property string $tag - * @property string $tagMode - * @property \Carbon\Carbon $date - * @property string $description - * @property float $latitude - * @property float $longitude - * @property integer $zoomLevel - * @property-read \Illuminate\Database\Eloquent\Collection|TransactionJournal[] $transactionjournals - * @property-read \FireflyIII\User $user - * @property int $account_id - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Tag whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Tag whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Tag whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Tag whereDeletedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Tag whereUserId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Tag whereTag($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Tag whereTagMode($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Tag whereDate($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Tag whereDescription($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Tag whereLatitude($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Tag whereLongitude($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Tag whereZoomLevel($value) - * @mixin \Eloquent - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\TransactionJournal[] $transactionJournals - */ class Tag extends TagSupport { protected $dates = ['created_at', 'updated_at', 'date']; diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index f35efcb180..1033611b21 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -19,38 +19,6 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Watson\Validating\ValidatingTrait; -/** - * FireflyIII\Models\Transaction - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property \Carbon\Carbon $deleted_at - * @property integer $account_id - * @property integer $transaction_journal_id - * @property string $description - * @property float $amount - * @property-read Account $account - * @property-read TransactionJournal $transactionJournal - * @method static \Illuminate\Database\Query\Builder|Transaction after($date) - * @method static \Illuminate\Database\Query\Builder|Transaction before($date) - * @property float $before - * @property float $after - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Transaction whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Transaction whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Transaction whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Transaction whereDeletedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Transaction whereAccountId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Transaction whereTransactionJournalId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Transaction whereDescription($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Transaction whereAmount($value) - * @mixin \Eloquent - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Budget[] $budgets - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Category[] $categories - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Transaction transactionTypes($types) - * @property integer $identifier - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Transaction whereIdentifier($value) - */ class Transaction extends Model { diff --git a/app/Models/TransactionCurrency.php b/app/Models/TransactionCurrency.php index 1cf89fc7de..da1c887cad 100644 --- a/app/Models/TransactionCurrency.php +++ b/app/Models/TransactionCurrency.php @@ -18,26 +18,6 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Watson\Validating\ValidatingTrait; -/** - * FireflyIII\Models\TransactionCurrency - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property \Carbon\Carbon $deleted_at - * @property string $code - * @property string $name - * @property string $symbol - * @property-read \Illuminate\Database\Eloquent\Collection|TransactionJournal[] $transactionJournals - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionCurrency whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionCurrency whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionCurrency whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionCurrency whereDeletedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionCurrency whereCode($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionCurrency whereName($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionCurrency whereSymbol($value) - * @mixin \Eloquent - */ class TransactionCurrency extends Model { use SoftDeletes, ValidatingTrait; diff --git a/app/Models/TransactionGroup.php b/app/Models/TransactionGroup.php index e075cfb63b..bcef0817e5 100644 --- a/app/Models/TransactionGroup.php +++ b/app/Models/TransactionGroup.php @@ -16,26 +16,6 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; -/** - * FireflyIII\Models\TransactionGroup - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property \Carbon\Carbon $deleted_at - * @property integer $user_id - * @property string $relation - * @property-read \Illuminate\Database\Eloquent\Collection|TransactionJournal[] $transactionjournals - * @property-read \FireflyIII\User $user - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionGroup whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionGroup whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionGroup whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionGroup whereDeletedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionGroup whereUserId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionGroup whereRelation($value) - * @mixin \Eloquent - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\TransactionJournal[] $transactionJournals - */ class TransactionGroup extends Model { use SoftDeletes; diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index 3c763b7302..e698c0ab15 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -25,77 +25,6 @@ use Preferences; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Watson\Validating\ValidatingTrait; -/** - * FireflyIII\Models\TransactionJournal - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property \Carbon\Carbon $deleted_at - * @property integer $user_id - * @property integer $transaction_type_id - * @property integer $bill_id - * @property integer $transaction_currency_id - * @property string $description - * @property boolean $completed - * @property \Carbon\Carbon $date - * @property \Carbon\Carbon $interest_date - * @property \Carbon\Carbon $book_date - * @property boolean $encrypted - * @property integer $order - * @property integer $tag_count - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Attachment[] $attachments - * @property-read \FireflyIII\Models\Bill $bill - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Budget[] $budgets - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Category[] $categories - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\PiggyBankEvent[] $piggyBankEvents - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Tag[] $tags - * @property-read \FireflyIII\Models\TransactionCurrency $transactionCurrency - * @property-read \FireflyIII\Models\TransactionType $transactionType - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\TransactionGroup[] $transactiongroups - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\TransactionJournalMeta[] $transactionjournalmeta - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Transaction[] $transactions - * @property-read \FireflyIII\User $user - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal after($date) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal before($date) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal transactionTypes($types) - * @property string $transaction_type_type - * @property-read string $transaction_currency_code - * @property-read string $destination_amount - * @property string $destination_account_id - * @property string $destination_account_name - * @property-read string $destination_account_type - * @property-read string $source_amount - * @property string $source_account_id - * @property string $source_account_name - * @property-read string $source_account_type - * @property \Carbon\Carbon $process_date - * @property int $account_id - * @property float $journalAmount - * @property string $account_name - * @property int $budget_id - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal whereDeletedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal whereUserId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal whereTransactionTypeId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal whereBillId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal whereTransactionCurrencyId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal whereDescription($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal whereCompleted($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal whereDate($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal whereInterestDate($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal whereBookDate($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal whereProcessDate($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal whereEncrypted($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal whereOrder($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal whereTagCount($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal expanded() - * @mixin \Eloquent - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournal sortCorrectly() - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\TransactionJournalMeta[] $transactionJournalMeta - */ class TransactionJournal extends TransactionJournalSupport { use SoftDeletes, ValidatingTrait; diff --git a/app/Models/TransactionJournalMeta.php b/app/Models/TransactionJournalMeta.php index d855897713..86653f07fc 100644 --- a/app/Models/TransactionJournalMeta.php +++ b/app/Models/TransactionJournalMeta.php @@ -16,28 +16,6 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; -/** - * Class TransactionJournalMeta - * - * @package FireflyIII\Models - * @property-read \FireflyIII\Models\TransactionJournal $transactionjournal - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property integer $transaction_journal_id - * @property string $name - * @property string $data - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournalMeta whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournalMeta whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournalMeta whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournalMeta whereTransactionJournalId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournalMeta whereName($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournalMeta whereData($value) - * @mixin \Eloquent - * @property string $hash - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionJournalMeta whereHash($value) - * @property-read \FireflyIII\Models\TransactionJournal $transactionJournal - */ class TransactionJournalMeta extends Model { diff --git a/app/Models/TransactionType.php b/app/Models/TransactionType.php index 51a912ca12..b18b10a7b0 100644 --- a/app/Models/TransactionType.php +++ b/app/Models/TransactionType.php @@ -17,22 +17,6 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -/** - * FireflyIII\Models\TransactionType - * - * @property integer $id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at - * @property \Carbon\Carbon $deleted_at - * @property string $type - * @property-read \Illuminate\Database\Eloquent\Collection|TransactionJournal[] $transactionJournals - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionType whereId($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionType whereCreatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionType whereUpdatedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionType whereDeletedAt($value) - * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\TransactionType whereType($value) - * @mixin \Eloquent - */ class TransactionType extends Model { use SoftDeletes; diff --git a/composer.lock b/composer.lock index 193f6cd621..675c12bb64 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "12c9e9450c824d192b8c049989188b8e", - "content-hash": "473d3c681e5c41989e9dced651a939df", + "hash": "3c2ecf3f444f89ec56e069b5452744fa", + "content-hash": "19ebf906c75d026c93d83cb8186e5d27", "packages": [ { "name": "bacon/bacon-qr-code", @@ -49,175 +49,6 @@ "homepage": "https://github.com/Bacon/BaconQrCode", "time": "2016-01-09 22:55:35" }, - { - "name": "barryvdh/laravel-debugbar", - "version": "v2.3.0", - "source": { - "type": "git", - "url": "https://github.com/barryvdh/laravel-debugbar.git", - "reference": "0c87981df959c7c1943abe227baf607c92f204f9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/0c87981df959c7c1943abe227baf607c92f204f9", - "reference": "0c87981df959c7c1943abe227baf607c92f204f9", - "shasum": "" - }, - "require": { - "illuminate/support": "5.1.*|5.2.*|5.3.*", - "maximebf/debugbar": "~1.13.0", - "php": ">=5.5.9", - "symfony/finder": "~2.7|~3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3-dev" - } - }, - "autoload": { - "psr-4": { - "Barryvdh\\Debugbar\\": "src/" - }, - "files": [ - "src/helpers.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Barry vd. Heuvel", - "email": "barryvdh@gmail.com" - } - ], - "description": "PHP Debugbar integration for Laravel", - "keywords": [ - "debug", - "debugbar", - "laravel", - "profiler", - "webprofiler" - ], - "time": "2016-09-15 14:05:56" - }, - { - "name": "barryvdh/laravel-ide-helper", - "version": "v2.2.1", - "source": { - "type": "git", - "url": "https://github.com/barryvdh/laravel-ide-helper.git", - "reference": "28af7cd19ca41cc0c63dd1de2b46c2b84d31c463" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/28af7cd19ca41cc0c63dd1de2b46c2b84d31c463", - "reference": "28af7cd19ca41cc0c63dd1de2b46c2b84d31c463", - "shasum": "" - }, - "require": { - "barryvdh/reflection-docblock": "^2.0.4", - "illuminate/console": "^5.0,<5.4", - "illuminate/filesystem": "^5.0,<5.4", - "illuminate/support": "^5.0,<5.4", - "php": ">=5.4.0", - "symfony/class-loader": "^2.3|^3.0" - }, - "require-dev": { - "doctrine/dbal": "~2.3", - "phpunit/phpunit": "4.*", - "scrutinizer/ocular": "~1.1", - "squizlabs/php_codesniffer": "~2.3" - }, - "suggest": { - "doctrine/dbal": "Load information from the database about models for phpdocs (~2.3)" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.2-dev" - } - }, - "autoload": { - "psr-4": { - "Barryvdh\\LaravelIdeHelper\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Barry vd. Heuvel", - "email": "barryvdh@gmail.com" - } - ], - "description": "Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.", - "keywords": [ - "autocomplete", - "codeintel", - "helper", - "ide", - "laravel", - "netbeans", - "phpdoc", - "phpstorm", - "sublime" - ], - "time": "2016-07-04 11:52:48" - }, - { - "name": "barryvdh/reflection-docblock", - "version": "v2.0.4", - "source": { - "type": "git", - "url": "https://github.com/barryvdh/ReflectionDocBlock.git", - "reference": "3dcbd98b5d9384a5357266efba8fd29884458e5c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/3dcbd98b5d9384a5357266efba8fd29884458e5c", - "reference": "3dcbd98b5d9384a5357266efba8fd29884458e5c", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.0,<4.5" - }, - "suggest": { - "dflydev/markdown": "~1.0", - "erusev/parsedown": "~1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-0": { - "Barryvdh": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "mike.vanriel@naenius.com" - } - ], - "time": "2016-06-13 19:28:20" - }, { "name": "christian-riesen/base32", "version": "1.3.1", @@ -1206,16 +1037,16 @@ }, { "name": "league/commonmark", - "version": "0.15.0", + "version": "0.15.1", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "19fb96643beba24e681c371dc133e25409742664" + "reference": "20bdba6777e6c63f861711501eec8887e65412fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/19fb96643beba24e681c371dc133e25409742664", - "reference": "19fb96643beba24e681c371dc133e25409742664", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/20bdba6777e6c63f861711501eec8887e65412fc", + "reference": "20bdba6777e6c63f861711501eec8887e65412fc", "shasum": "" }, "require": { @@ -1271,7 +1102,7 @@ "markdown", "parser" ], - "time": "2016-09-14 15:44:35" + "time": "2016-11-08 15:28:32" }, { "name": "league/csv", @@ -1413,67 +1244,6 @@ ], "time": "2016-10-19 20:38:46" }, - { - "name": "maximebf/debugbar", - "version": "v1.13.0", - "source": { - "type": "git", - "url": "https://github.com/maximebf/php-debugbar.git", - "reference": "5f49a5ed6cfde81d31d89378806670d77462526e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/5f49a5ed6cfde81d31d89378806670d77462526e", - "reference": "5f49a5ed6cfde81d31d89378806670d77462526e", - "shasum": "" - }, - "require": { - "php": ">=5.3.0", - "psr/log": "^1.0", - "symfony/var-dumper": "^2.6|^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0|^5.0" - }, - "suggest": { - "kriswallsmith/assetic": "The best way to manage assets", - "monolog/monolog": "Log using Monolog", - "predis/predis": "Redis storage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.13-dev" - } - }, - "autoload": { - "psr-4": { - "DebugBar\\": "src/DebugBar/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Maxime Bouroumeau-Fuseau", - "email": "maxime.bouroumeau@gmail.com", - "homepage": "http://maximebf.com" - }, - { - "name": "Barry vd. Heuvel", - "email": "barryvdh@gmail.com" - } - ], - "description": "Debug bar in the browser for php application", - "homepage": "https://github.com/maximebf/php-debugbar", - "keywords": [ - "debug", - "debugbar" - ], - "time": "2016-09-15 14:01:59" - }, { "name": "monolog/monolog", "version": "1.21.0", @@ -1696,16 +1466,16 @@ }, { "name": "paragonie/random_compat", - "version": "v2.0.3", + "version": "v2.0.4", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "c0125896dbb151380ab47e96c621741e79623beb" + "reference": "a9b97968bcde1c4de2a5ec6cbd06a0f6c919b46e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/c0125896dbb151380ab47e96c621741e79623beb", - "reference": "c0125896dbb151380ab47e96c621741e79623beb", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/a9b97968bcde1c4de2a5ec6cbd06a0f6c919b46e", + "reference": "a9b97968bcde1c4de2a5ec6cbd06a0f6c919b46e", "shasum": "" }, "require": { @@ -1740,7 +1510,7 @@ "pseudorandom", "random" ], - "time": "2016-10-17 15:23:22" + "time": "2016-11-07 23:38:38" }, { "name": "pragmarx/google2fa", @@ -2168,62 +1938,6 @@ ], "time": "2016-07-08 11:51:25" }, - { - "name": "symfony/class-loader", - "version": "v3.1.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/class-loader.git", - "reference": "bcb072aba46ddf3b1a496438c63be6be647739aa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/class-loader/zipball/bcb072aba46ddf3b1a496438c63be6be647739aa", - "reference": "bcb072aba46ddf3b1a496438c63be6be647739aa", - "shasum": "" - }, - "require": { - "php": ">=5.5.9" - }, - "require-dev": { - "symfony/finder": "~2.8|~3.0", - "symfony/polyfill-apcu": "~1.1" - }, - "suggest": { - "symfony/polyfill-apcu": "For using ApcClassLoader on HHVM" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\ClassLoader\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony ClassLoader Component", - "homepage": "https://symfony.com", - "time": "2016-09-06 23:30:54" - }, { "name": "symfony/console", "version": "v3.1.6", @@ -2588,16 +2302,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.2.0", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "dff51f72b0706335131b00a7f49606168c582594" + "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/dff51f72b0706335131b00a7f49606168c582594", - "reference": "dff51f72b0706335131b00a7f49606168c582594", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e79d363049d1c2128f133a2667e4f4190904f7f4", + "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4", "shasum": "" }, "require": { @@ -2609,7 +2323,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.3-dev" } }, "autoload": { @@ -2643,20 +2357,20 @@ "portable", "shim" ], - "time": "2016-05-18 14:26:46" + "time": "2016-11-14 01:06:16" }, { "name": "symfony/polyfill-php56", - "version": "v1.2.0", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php56.git", - "reference": "3edf57a8fbf9a927533344cef65ad7e1cf31030a" + "reference": "1dd42b9b89556f18092f3d1ada22cb05ac85383c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/3edf57a8fbf9a927533344cef65ad7e1cf31030a", - "reference": "3edf57a8fbf9a927533344cef65ad7e1cf31030a", + "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/1dd42b9b89556f18092f3d1ada22cb05ac85383c", + "reference": "1dd42b9b89556f18092f3d1ada22cb05ac85383c", "shasum": "" }, "require": { @@ -2666,7 +2380,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.3-dev" } }, "autoload": { @@ -2699,20 +2413,20 @@ "portable", "shim" ], - "time": "2016-05-18 14:26:46" + "time": "2016-11-14 01:06:16" }, { "name": "symfony/polyfill-util", - "version": "v1.2.0", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-util.git", - "reference": "ef830ce3d218e622b221d6bfad42c751d974bf99" + "reference": "746bce0fca664ac0a575e465f65c6643faddf7fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/ef830ce3d218e622b221d6bfad42c751d974bf99", - "reference": "ef830ce3d218e622b221d6bfad42c751d974bf99", + "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/746bce0fca664ac0a575e465f65c6643faddf7fb", + "reference": "746bce0fca664ac0a575e465f65c6643faddf7fb", "shasum": "" }, "require": { @@ -2721,7 +2435,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.3-dev" } }, "autoload": { @@ -2751,7 +2465,7 @@ "polyfill", "shim" ], - "time": "2016-05-18 14:26:46" + "time": "2016-11-14 01:06:16" }, { "name": "symfony/process", @@ -3006,16 +2720,16 @@ }, { "name": "twig/twig", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "3c6c0033fd3b5679c6e1cb60f4f9766c2b424d97" + "reference": "60ae30368f7ac50a95de032f16c1e882b0f69813" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/3c6c0033fd3b5679c6e1cb60f4f9766c2b424d97", - "reference": "3c6c0033fd3b5679c6e1cb60f4f9766c2b424d97", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/60ae30368f7ac50a95de032f16c1e882b0f69813", + "reference": "60ae30368f7ac50a95de032f16c1e882b0f69813", "shasum": "" }, "require": { @@ -3028,7 +2742,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.27-dev" + "dev-master": "1.28-dev" } }, "autoload": { @@ -3063,7 +2777,7 @@ "keywords": [ "templating" ], - "time": "2016-10-25 19:17:17" + "time": "2016-11-17 17:46:53" }, { "name": "vlucas/phpdotenv", @@ -3167,6 +2881,175 @@ } ], "packages-dev": [ + { + "name": "barryvdh/laravel-debugbar", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/laravel-debugbar.git", + "reference": "0c87981df959c7c1943abe227baf607c92f204f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/0c87981df959c7c1943abe227baf607c92f204f9", + "reference": "0c87981df959c7c1943abe227baf607c92f204f9", + "shasum": "" + }, + "require": { + "illuminate/support": "5.1.*|5.2.*|5.3.*", + "maximebf/debugbar": "~1.13.0", + "php": ">=5.5.9", + "symfony/finder": "~2.7|~3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-4": { + "Barryvdh\\Debugbar\\": "src/" + }, + "files": [ + "src/helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "PHP Debugbar integration for Laravel", + "keywords": [ + "debug", + "debugbar", + "laravel", + "profiler", + "webprofiler" + ], + "time": "2016-09-15 14:05:56" + }, + { + "name": "barryvdh/laravel-ide-helper", + "version": "v2.2.1", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/laravel-ide-helper.git", + "reference": "28af7cd19ca41cc0c63dd1de2b46c2b84d31c463" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/28af7cd19ca41cc0c63dd1de2b46c2b84d31c463", + "reference": "28af7cd19ca41cc0c63dd1de2b46c2b84d31c463", + "shasum": "" + }, + "require": { + "barryvdh/reflection-docblock": "^2.0.4", + "illuminate/console": "^5.0,<5.4", + "illuminate/filesystem": "^5.0,<5.4", + "illuminate/support": "^5.0,<5.4", + "php": ">=5.4.0", + "symfony/class-loader": "^2.3|^3.0" + }, + "require-dev": { + "doctrine/dbal": "~2.3", + "phpunit/phpunit": "4.*", + "scrutinizer/ocular": "~1.1", + "squizlabs/php_codesniffer": "~2.3" + }, + "suggest": { + "doctrine/dbal": "Load information from the database about models for phpdocs (~2.3)" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + }, + "autoload": { + "psr-4": { + "Barryvdh\\LaravelIdeHelper\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.", + "keywords": [ + "autocomplete", + "codeintel", + "helper", + "ide", + "laravel", + "netbeans", + "phpdoc", + "phpstorm", + "sublime" + ], + "time": "2016-07-04 11:52:48" + }, + { + "name": "barryvdh/reflection-docblock", + "version": "v2.0.4", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/ReflectionDocBlock.git", + "reference": "3dcbd98b5d9384a5357266efba8fd29884458e5c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/3dcbd98b5d9384a5357266efba8fd29884458e5c", + "reference": "3dcbd98b5d9384a5357266efba8fd29884458e5c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0,<4.5" + }, + "suggest": { + "dflydev/markdown": "~1.0", + "erusev/parsedown": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Barryvdh": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "mike.vanriel@naenius.com" + } + ], + "time": "2016-06-13 19:28:20" + }, { "name": "doctrine/instantiator", "version": "1.0.5", @@ -3315,17 +3198,78 @@ "time": "2015-05-11 14:41:42" }, { - "name": "mockery/mockery", - "version": "0.9.5", + "name": "maximebf/debugbar", + "version": "v1.13.0", "source": { "type": "git", - "url": "https://github.com/padraic/mockery.git", - "reference": "4db079511a283e5aba1b3c2fb19037c645e70fc2" + "url": "https://github.com/maximebf/php-debugbar.git", + "reference": "5f49a5ed6cfde81d31d89378806670d77462526e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/padraic/mockery/zipball/4db079511a283e5aba1b3c2fb19037c645e70fc2", - "reference": "4db079511a283e5aba1b3c2fb19037c645e70fc2", + "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/5f49a5ed6cfde81d31d89378806670d77462526e", + "reference": "5f49a5ed6cfde81d31d89378806670d77462526e", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/log": "^1.0", + "symfony/var-dumper": "^2.6|^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0|^5.0" + }, + "suggest": { + "kriswallsmith/assetic": "The best way to manage assets", + "monolog/monolog": "Log using Monolog", + "predis/predis": "Redis storage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.13-dev" + } + }, + "autoload": { + "psr-4": { + "DebugBar\\": "src/DebugBar/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maxime Bouroumeau-Fuseau", + "email": "maxime.bouroumeau@gmail.com", + "homepage": "http://maximebf.com" + }, + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "Debug bar in the browser for php application", + "homepage": "https://github.com/maximebf/php-debugbar", + "keywords": [ + "debug", + "debugbar" + ], + "time": "2016-09-15 14:01:59" + }, + { + "name": "mockery/mockery", + "version": "0.9.6", + "source": { + "type": "git", + "url": "https://github.com/padraic/mockery.git", + "reference": "65d4ca18e15cb02eeb1e5336f884e46b9b905be0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/padraic/mockery/zipball/65d4ca18e15cb02eeb1e5336f884e46b9b905be0", + "reference": "65d4ca18e15cb02eeb1e5336f884e46b9b905be0", "shasum": "" }, "require": { @@ -3377,7 +3321,7 @@ "test double", "testing" ], - "time": "2016-05-22 21:52:33" + "time": "2016-09-30 12:09:40" }, { "name": "myclabs/deep-copy", @@ -3826,16 +3770,16 @@ }, { "name": "phpunit/php-token-stream", - "version": "1.4.8", + "version": "1.4.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da" + "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", - "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3b402f65a4cc90abf6e1104e388b896ce209631b", + "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b", "shasum": "" }, "require": { @@ -3871,20 +3815,20 @@ "keywords": [ "tokenizer" ], - "time": "2015-09-15 10:49:45" + "time": "2016-11-15 14:06:22" }, { "name": "phpunit/phpunit", - "version": "5.6.2", + "version": "5.6.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "cd13b23ac5a519a4708e00736c26ee0bb28b2e01" + "reference": "4ddb822f1de421b4cadb47570a525fd7d9359493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/cd13b23ac5a519a4708e00736c26ee0bb28b2e01", - "reference": "cd13b23ac5a519a4708e00736c26ee0bb28b2e01", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4ddb822f1de421b4cadb47570a525fd7d9359493", + "reference": "4ddb822f1de421b4cadb47570a525fd7d9359493", "shasum": "" }, "require": { @@ -3912,7 +3856,9 @@ "symfony/yaml": "~2.1|~3.0" }, "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2" + "phpdocumentor/reflection-docblock": "3.0.2", + "sebastian/object-enumerator": "1.0.1", + "sebastian/recursion-context": "1.0.3 || 1.0.4" }, "require-dev": { "ext-pdo": "*" @@ -3953,7 +3899,7 @@ "testing", "xunit" ], - "time": "2016-10-25 07:40:25" + "time": "2016-11-18 09:50:51" }, { "name": "phpunit/phpunit-mock-objects", @@ -4061,16 +4007,16 @@ }, { "name": "sebastian/comparator", - "version": "1.2.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "937efb279bd37a375bcadf584dec0726f84dbf22" + "reference": "ce2bda23a56456f19e35d98241446b581f648c14" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22", - "reference": "937efb279bd37a375bcadf584dec0726f84dbf22", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/ce2bda23a56456f19e35d98241446b581f648c14", + "reference": "ce2bda23a56456f19e35d98241446b581f648c14", "shasum": "" }, "require": { @@ -4121,7 +4067,7 @@ "compare", "equality" ], - "time": "2015-07-26 15:48:44" + "time": "2016-11-17 14:39:37" }, { "name": "sebastian/diff", @@ -4527,6 +4473,62 @@ "homepage": "https://github.com/sebastianbergmann/version", "time": "2016-02-04 12:56:52" }, + { + "name": "symfony/class-loader", + "version": "v3.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/class-loader.git", + "reference": "bcb072aba46ddf3b1a496438c63be6be647739aa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/class-loader/zipball/bcb072aba46ddf3b1a496438c63be6be647739aa", + "reference": "bcb072aba46ddf3b1a496438c63be6be647739aa", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "require-dev": { + "symfony/finder": "~2.8|~3.0", + "symfony/polyfill-apcu": "~1.1" + }, + "suggest": { + "symfony/polyfill-apcu": "For using ApcClassLoader on HHVM" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\ClassLoader\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony ClassLoader Component", + "homepage": "https://symfony.com", + "time": "2016-09-06 23:30:54" + }, { "name": "symfony/css-selector", "version": "v3.1.6", diff --git a/config/app.php b/config/app.php index 68c0f500a9..bae2903930 100755 --- a/config/app.php +++ b/config/app.php @@ -65,6 +65,7 @@ return [ // own stuff: + //Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class, //Barryvdh\Debugbar\ServiceProvider::class, DaveJamesMiller\Breadcrumbs\ServiceProvider::class, From 73f0cc705ba667ad1dddb81548789e8b34e1fb1e Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 18 Nov 2016 20:06:08 +0100 Subject: [PATCH 104/709] Code cleanup. --- .gitignore | 1 + .../Report/Audit/MonthReportGenerator.php | 1 + .../Report/Category/MonthReportGenerator.php | 3 +++ .../Report/Standard/MonthReportGenerator.php | 1 + .../Standard/MultiYearReportGenerator.php | 1 + .../Report/Standard/YearReportGenerator.php | 1 + .../Chart/CategoryReportController.php | 19 ------------------- app/Http/Controllers/ReportController.php | 3 ++- app/Http/Controllers/SearchController.php | 1 + app/Http/Requests/ReportFormRequest.php | 4 ++++ app/Models/Account.php | 5 +++++ app/Models/AccountMeta.php | 5 +++++ app/Models/AccountType.php | 5 +++++ app/Models/Attachment.php | 5 +++++ app/Models/Bill.php | 5 +++++ app/Models/Budget.php | 5 +++++ app/Models/BudgetLimit.php | 5 +++++ app/Models/Category.php | 5 +++++ app/Models/Configuration.php | 5 +++++ app/Models/ExportJob.php | 5 +++++ app/Models/ImportJob.php | 5 +++++ app/Models/LimitRepetition.php | 5 +++++ app/Models/Note.php | 5 +++++ app/Models/PiggyBank.php | 5 +++++ app/Models/PiggyBankEvent.php | 5 +++++ app/Models/PiggyBankRepetition.php | 5 +++++ app/Models/Preference.php | 5 +++++ app/Models/Role.php | 5 +++++ app/Models/Rule.php | 5 +++++ app/Models/RuleAction.php | 5 +++++ app/Models/RuleGroup.php | 5 +++++ app/Models/RuleTrigger.php | 5 +++++ app/Models/Tag.php | 5 +++++ app/Models/Transaction.php | 5 +++++ app/Models/TransactionCurrency.php | 5 +++++ app/Models/TransactionGroup.php | 5 +++++ app/Models/TransactionJournal.php | 5 +++++ app/Models/TransactionJournalMeta.php | 5 +++++ app/Models/TransactionType.php | 5 +++++ app/Repositories/Journal/JournalTasker.php | 2 -- .../Journal/JournalTaskerInterface.php | 2 -- 41 files changed, 160 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index 31a213bd48..0e45076d94 100755 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ result.html test-import.sh test-import-report.txt public/google*.html +_ide_helper_models.php diff --git a/app/Generator/Report/Audit/MonthReportGenerator.php b/app/Generator/Report/Audit/MonthReportGenerator.php index 3ad211f952..b366042329 100644 --- a/app/Generator/Report/Audit/MonthReportGenerator.php +++ b/app/Generator/Report/Audit/MonthReportGenerator.php @@ -118,6 +118,7 @@ class MonthReportGenerator implements ReportGeneratorInterface */ public function setCategories(Collection $categories): ReportGeneratorInterface { + return $this; } /** diff --git a/app/Generator/Report/Category/MonthReportGenerator.php b/app/Generator/Report/Category/MonthReportGenerator.php index 3e0a82ef8c..3211e4f023 100644 --- a/app/Generator/Report/Category/MonthReportGenerator.php +++ b/app/Generator/Report/Category/MonthReportGenerator.php @@ -211,6 +211,9 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface } + /** + * @return array + */ private function getAverageIncome(): array { $expenses = $this->getIncome(); diff --git a/app/Generator/Report/Standard/MonthReportGenerator.php b/app/Generator/Report/Standard/MonthReportGenerator.php index 4c4ae270f4..fc02233e9c 100644 --- a/app/Generator/Report/Standard/MonthReportGenerator.php +++ b/app/Generator/Report/Standard/MonthReportGenerator.php @@ -95,5 +95,6 @@ class MonthReportGenerator implements ReportGeneratorInterface */ public function setCategories(Collection $categories): ReportGeneratorInterface { + return $this; } } \ No newline at end of file diff --git a/app/Generator/Report/Standard/MultiYearReportGenerator.php b/app/Generator/Report/Standard/MultiYearReportGenerator.php index 8d62c5e85b..64f74f1295 100644 --- a/app/Generator/Report/Standard/MultiYearReportGenerator.php +++ b/app/Generator/Report/Standard/MultiYearReportGenerator.php @@ -67,6 +67,7 @@ class MultiYearReportGenerator implements ReportGeneratorInterface */ public function setCategories(Collection $categories): ReportGeneratorInterface { + return $this; } /** diff --git a/app/Generator/Report/Standard/YearReportGenerator.php b/app/Generator/Report/Standard/YearReportGenerator.php index bd356d02cd..7536b4027f 100644 --- a/app/Generator/Report/Standard/YearReportGenerator.php +++ b/app/Generator/Report/Standard/YearReportGenerator.php @@ -67,6 +67,7 @@ class YearReportGenerator implements ReportGeneratorInterface */ public function setCategories(Collection $categories): ReportGeneratorInterface { + return $this; } /** diff --git a/app/Http/Controllers/Chart/CategoryReportController.php b/app/Http/Controllers/Chart/CategoryReportController.php index 2aa1fa55f8..6c292609a9 100644 --- a/app/Http/Controllers/Chart/CategoryReportController.php +++ b/app/Http/Controllers/Chart/CategoryReportController.php @@ -353,25 +353,6 @@ class CategoryReportController extends Controller return $set; } - /** - * @param Collection $set - * - * @return array - */ - private function groupByAccount(Collection $set): array - { - // group by category ID: - $grouped = []; - /** @var Transaction $transaction */ - foreach ($set as $transaction) { - $accountId = $transaction->account_id; - $grouped[$accountId] = $grouped[$accountId] ?? '0'; - $grouped[$accountId] = bcadd($transaction->transaction_amount, $grouped[$accountId]); - } - - return $grouped; - } - /** * @param Collection $set * diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index 8d29ba1edf..b980e3337d 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -100,8 +100,9 @@ class ReportController extends Controller * @param Carbon $end * @param Collection $accounts * + * @param Collection $categories + * * @return string - * @throws FireflyException */ public function categoryReport(Carbon $start, Carbon $end, Collection $accounts, Collection $categories) { diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index e9b63f4b17..4b47777c62 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -35,6 +35,7 @@ class SearchController extends Controller /** * Results always come in the form of an array [results, count, fullCount] * + * @param Request $request * @param SearchInterface $searcher * * @return $this diff --git a/app/Http/Requests/ReportFormRequest.php b/app/Http/Requests/ReportFormRequest.php index 6219cce4ea..7d97a37b76 100644 --- a/app/Http/Requests/ReportFormRequest.php +++ b/app/Http/Requests/ReportFormRequest.php @@ -79,6 +79,10 @@ class ReportFormRequest extends Request return $collection; } + /** + * @return Carbon + * @throws FireflyException + */ public function getEndDate(): Carbon { $date = new Carbon; diff --git a/app/Models/Account.php b/app/Models/Account.php index b38d14c2b0..316da495b8 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -27,6 +27,11 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Watson\Validating\ValidatingTrait; +/** + * Class Account + * + * @package FireflyIII\Models + */ class Account extends Model { use SoftDeletes, ValidatingTrait; diff --git a/app/Models/AccountMeta.php b/app/Models/AccountMeta.php index 6c92343c18..d8c08233a5 100644 --- a/app/Models/AccountMeta.php +++ b/app/Models/AccountMeta.php @@ -16,6 +16,11 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +/** + * Class AccountMeta + * + * @package FireflyIII\Models + */ class AccountMeta extends Model { diff --git a/app/Models/AccountType.php b/app/Models/AccountType.php index 06a2c37003..0bf346f471 100644 --- a/app/Models/AccountType.php +++ b/app/Models/AccountType.php @@ -16,6 +16,11 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; +/** + * Class AccountType + * + * @package FireflyIII\Models + */ class AccountType extends Model { const DEFAULT = 'Default account'; diff --git a/app/Models/Attachment.php b/app/Models/Attachment.php index 6c798372d8..eff2095f78 100644 --- a/app/Models/Attachment.php +++ b/app/Models/Attachment.php @@ -20,6 +20,11 @@ use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +/** + * Class Attachment + * + * @package FireflyIII\Models + */ class Attachment extends Model { use SoftDeletes; diff --git a/app/Models/Bill.php b/app/Models/Bill.php index ab9f37e99e..7c6ca35924 100644 --- a/app/Models/Bill.php +++ b/app/Models/Bill.php @@ -20,6 +20,11 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Watson\Validating\ValidatingTrait; +/** + * Class Bill + * + * @package FireflyIII\Models + */ class Bill extends Model { diff --git a/app/Models/Budget.php b/app/Models/Budget.php index ba8cddea4f..c20df153b0 100644 --- a/app/Models/Budget.php +++ b/app/Models/Budget.php @@ -20,6 +20,11 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Watson\Validating\ValidatingTrait; +/** + * Class Budget + * + * @package FireflyIII\Models + */ class Budget extends Model { diff --git a/app/Models/BudgetLimit.php b/app/Models/BudgetLimit.php index 9a2843b1b4..86322743bd 100644 --- a/app/Models/BudgetLimit.php +++ b/app/Models/BudgetLimit.php @@ -15,6 +15,11 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; +/** + * Class BudgetLimit + * + * @package FireflyIII\Models + */ class BudgetLimit extends Model { diff --git a/app/Models/Category.php b/app/Models/Category.php index 5b12d93558..e56adb3f2a 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -20,6 +20,11 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Watson\Validating\ValidatingTrait; +/** + * Class Category + * + * @package FireflyIII\Models + */ class Category extends Model { use SoftDeletes, ValidatingTrait; diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index 4b10d83860..9bbee9e894 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -16,6 +16,11 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; +/** + * Class Configuration + * + * @package FireflyIII\Models + */ class Configuration extends Model { use SoftDeletes; diff --git a/app/Models/ExportJob.php b/app/Models/ExportJob.php index 5111f8918d..9a3807c728 100644 --- a/app/Models/ExportJob.php +++ b/app/Models/ExportJob.php @@ -16,6 +16,11 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +/** + * Class ExportJob + * + * @package FireflyIII\Models + */ class ExportJob extends Model { /** diff --git a/app/Models/ImportJob.php b/app/Models/ImportJob.php index 75066b734d..131fc8a1b0 100644 --- a/app/Models/ImportJob.php +++ b/app/Models/ImportJob.php @@ -18,6 +18,11 @@ use Illuminate\Database\Eloquent\Model; use Storage; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +/** + * Class ImportJob + * + * @package FireflyIII\Models + */ class ImportJob extends Model { diff --git a/app/Models/LimitRepetition.php b/app/Models/LimitRepetition.php index 4732b9e950..1ce71bb237 100644 --- a/app/Models/LimitRepetition.php +++ b/app/Models/LimitRepetition.php @@ -18,6 +18,11 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +/** + * Class LimitRepetition + * + * @package FireflyIII\Models + */ class LimitRepetition extends Model { diff --git a/app/Models/Note.php b/app/Models/Note.php index 4b2c3df254..1465008ecf 100644 --- a/app/Models/Note.php +++ b/app/Models/Note.php @@ -16,6 +16,11 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; use League\CommonMark\CommonMarkConverter; +/** + * Class Note + * + * @package FireflyIII\Models + */ class Note extends Model { protected $dates = ['created_at', 'updated_at', 'deleted_at']; diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index d0c001fb23..2e3f1f01b0 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -20,6 +20,11 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Steam; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +/** + * Class PiggyBank + * + * @package FireflyIII\Models + */ class PiggyBank extends Model { use SoftDeletes; diff --git a/app/Models/PiggyBankEvent.php b/app/Models/PiggyBankEvent.php index b8f589b55f..ac11ec0d70 100644 --- a/app/Models/PiggyBankEvent.php +++ b/app/Models/PiggyBankEvent.php @@ -15,6 +15,11 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; +/** + * Class PiggyBankEvent + * + * @package FireflyIII\Models + */ class PiggyBankEvent extends Model { diff --git a/app/Models/PiggyBankRepetition.php b/app/Models/PiggyBankRepetition.php index a6a7ed41b5..cc1432e31b 100644 --- a/app/Models/PiggyBankRepetition.php +++ b/app/Models/PiggyBankRepetition.php @@ -17,6 +17,11 @@ use Carbon\Carbon; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Model; +/** + * Class PiggyBankRepetition + * + * @package FireflyIII\Models + */ class PiggyBankRepetition extends Model { diff --git a/app/Models/Preference.php b/app/Models/Preference.php index 5909ef55aa..768c662f2d 100644 --- a/app/Models/Preference.php +++ b/app/Models/Preference.php @@ -19,6 +19,11 @@ use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Database\Eloquent\Model; use Log; +/** + * Class Preference + * + * @package FireflyIII\Models + */ class Preference extends Model { diff --git a/app/Models/Role.php b/app/Models/Role.php index b400b69394..23584fd274 100644 --- a/app/Models/Role.php +++ b/app/Models/Role.php @@ -16,6 +16,11 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; +/** + * Class Role + * + * @package FireflyIII\Models + */ class Role extends Model { diff --git a/app/Models/Rule.php b/app/Models/Rule.php index 3e965f1f40..09f8897326 100644 --- a/app/Models/Rule.php +++ b/app/Models/Rule.php @@ -17,6 +17,11 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +/** + * Class Rule + * + * @package FireflyIII\Models + */ class Rule extends Model { use SoftDeletes; diff --git a/app/Models/RuleAction.php b/app/Models/RuleAction.php index 271aaf1164..11a40ef6a6 100644 --- a/app/Models/RuleAction.php +++ b/app/Models/RuleAction.php @@ -22,6 +22,11 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; +/** + * Class RuleAction + * + * @package FireflyIII\Models + */ class RuleAction extends Model { /** diff --git a/app/Models/RuleGroup.php b/app/Models/RuleGroup.php index e89a88ee8d..f3dc6abd20 100644 --- a/app/Models/RuleGroup.php +++ b/app/Models/RuleGroup.php @@ -17,6 +17,11 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +/** + * Class RuleGroup + * + * @package FireflyIII\Models + */ class RuleGroup extends Model { use SoftDeletes; diff --git a/app/Models/RuleTrigger.php b/app/Models/RuleTrigger.php index 399b262c12..72fe0f76a4 100644 --- a/app/Models/RuleTrigger.php +++ b/app/Models/RuleTrigger.php @@ -15,6 +15,11 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; +/** + * Class RuleTrigger + * + * @package FireflyIII\Models + */ class RuleTrigger extends Model { /** diff --git a/app/Models/Tag.php b/app/Models/Tag.php index d69c13eb68..8106423a33 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -18,6 +18,11 @@ use FireflyIII\Support\Models\TagSupport; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Watson\Validating\ValidatingTrait; +/** + * Class Tag + * + * @package FireflyIII\Models + */ class Tag extends TagSupport { protected $dates = ['created_at', 'updated_at', 'date']; diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index 1033611b21..f54d613a29 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -19,6 +19,11 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Watson\Validating\ValidatingTrait; +/** + * Class Transaction + * + * @package FireflyIII\Models + */ class Transaction extends Model { diff --git a/app/Models/TransactionCurrency.php b/app/Models/TransactionCurrency.php index da1c887cad..3e48b6c1d4 100644 --- a/app/Models/TransactionCurrency.php +++ b/app/Models/TransactionCurrency.php @@ -18,6 +18,11 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Watson\Validating\ValidatingTrait; +/** + * Class TransactionCurrency + * + * @package FireflyIII\Models + */ class TransactionCurrency extends Model { use SoftDeletes, ValidatingTrait; diff --git a/app/Models/TransactionGroup.php b/app/Models/TransactionGroup.php index bcef0817e5..46ff2d6e2c 100644 --- a/app/Models/TransactionGroup.php +++ b/app/Models/TransactionGroup.php @@ -16,6 +16,11 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; +/** + * Class TransactionGroup + * + * @package FireflyIII\Models + */ class TransactionGroup extends Model { use SoftDeletes; diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index e698c0ab15..0441e6e8a0 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -25,6 +25,11 @@ use Preferences; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Watson\Validating\ValidatingTrait; +/** + * Class TransactionJournal + * + * @package FireflyIII\Models + */ class TransactionJournal extends TransactionJournalSupport { use SoftDeletes, ValidatingTrait; diff --git a/app/Models/TransactionJournalMeta.php b/app/Models/TransactionJournalMeta.php index 86653f07fc..9e6ff0ceec 100644 --- a/app/Models/TransactionJournalMeta.php +++ b/app/Models/TransactionJournalMeta.php @@ -16,6 +16,11 @@ namespace FireflyIII\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +/** + * Class TransactionJournalMeta + * + * @package FireflyIII\Models + */ class TransactionJournalMeta extends Model { diff --git a/app/Models/TransactionType.php b/app/Models/TransactionType.php index b18b10a7b0..ff685856e7 100644 --- a/app/Models/TransactionType.php +++ b/app/Models/TransactionType.php @@ -17,6 +17,11 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +/** + * Class TransactionType + * + * @package FireflyIII\Models + */ class TransactionType extends Model { use SoftDeletes; diff --git a/app/Repositories/Journal/JournalTasker.php b/app/Repositories/Journal/JournalTasker.php index 862bafadb8..8cd0c74974 100644 --- a/app/Repositories/Journal/JournalTasker.php +++ b/app/Repositories/Journal/JournalTasker.php @@ -13,7 +13,6 @@ declare(strict_types = 1); namespace FireflyIII\Repositories\Journal; -use Carbon\Carbon; use Crypt; use DB; use FireflyIII\Models\PiggyBankEvent; @@ -22,7 +21,6 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\User; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Query\JoinClause; -use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; /** diff --git a/app/Repositories/Journal/JournalTaskerInterface.php b/app/Repositories/Journal/JournalTaskerInterface.php index 09ad5813a7..058bc63733 100644 --- a/app/Repositories/Journal/JournalTaskerInterface.php +++ b/app/Repositories/Journal/JournalTaskerInterface.php @@ -14,9 +14,7 @@ declare(strict_types = 1); namespace FireflyIII\Repositories\Journal; -use Carbon\Carbon; use FireflyIII\Models\TransactionJournal; -use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; /** From e15ea0418699291a0f4e4d1cafd6203d414527c1 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 19 Nov 2016 07:27:54 +0100 Subject: [PATCH 105/709] Join two charts, simpler code. --- app/Helpers/Report/BudgetReportHelper.php | 177 ++++++------------ .../Report/BudgetReportHelperInterface.php | 12 +- .../Controllers/Chart/ReportController.php | 4 +- .../Controllers/Report/BudgetController.php | 46 +---- .../Transaction/SplitController.php | 1 + public/js/ff/reports/default/multi-year.js | 2 +- public/js/ff/reports/default/year.js | 6 +- .../views/reports/default/multi-year.twig | 20 +- resources/views/reports/default/year.twig | 8 +- ...get-multi-year.twig => budget-period.twig} | 8 +- .../partials/budget-year-overview.twig | 25 --- routes/web.php | 15 +- 12 files changed, 104 insertions(+), 220 deletions(-) rename resources/views/reports/partials/{budget-multi-year.twig => budget-period.twig} (78%) delete mode 100644 resources/views/reports/partials/budget-year-overview.twig diff --git a/app/Helpers/Report/BudgetReportHelper.php b/app/Helpers/Report/BudgetReportHelper.php index 4f370f89bb..1aa1c45711 100644 --- a/app/Helpers/Report/BudgetReportHelper.php +++ b/app/Helpers/Report/BudgetReportHelper.php @@ -22,7 +22,6 @@ use FireflyIII\Models\Budget; use FireflyIII\Models\LimitRepetition; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; -use FireflyIII\Support\CacheProperties; use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; use stdClass; @@ -47,60 +46,6 @@ class BudgetReportHelper implements BudgetReportHelperInterface $this->repository = $repository; } - /** - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) // at 43, its ok. - * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly 5. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return Collection - */ - public function budgetYearOverview(Carbon $start, Carbon $end, Collection $accounts): Collection - { - // chart properties for cache: - $cache = new CacheProperties; - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty('budget-year'); - $cache->addProperty($accounts->pluck('id')->toArray()); - if ($cache->has()) { - return $cache->get(); - } - - $current = clone $start; - $return = new Collection; - $set = $this->repository->getBudgets(); - $budgets = []; - $spent = []; - $headers = $this->createYearHeaders($current, $end); - - /** @var Budget $budget */ - foreach ($set as $budget) { - $id = $budget->id; - $budgets[$id] = $budget->name; - $current = clone $start; - $budgetData = $this->getBudgetSpentData($current, $end, $budget, $accounts); - $sum = $budgetData['sum']; - $spent[$id] = $budgetData['spent']; - - if (bccomp('0', $sum) === 0) { - // not spent anything. - unset($spent[$id]); - unset($budgets[$id]); - } - } - - $return->put('headers', $headers); - $return->put('budgets', $budgets); - $return->put('spent', $spent); - - $cache->store($return); - - return $return; - } - /** * @param Carbon $start * @param Carbon $end @@ -108,10 +53,25 @@ class BudgetReportHelper implements BudgetReportHelperInterface * * @return array */ - public function getBudgetMultiYear(Carbon $start, Carbon $end, Collection $accounts): array + public function getBudgetPeriodReport(Carbon $start, Carbon $end, Collection $accounts): array { + // get account ID's. $accountIds = $accounts->pluck('id')->toArray(); - $query = TransactionJournal + + // define period to group on: + $sqlDateFormat = '%Y-%m-%d'; + // monthly report (for year) + if ($start->diffInMonths($end) > 1) { + $sqlDateFormat = '%Y-%m'; + } + + // yearly report (for multi year) + if ($start->diffInMonths($end) > 12) { + $sqlDateFormat = '%Y'; + } + + // build query. + $query = TransactionJournal ::leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') ->leftJoin( @@ -127,18 +87,18 @@ class BudgetReportHelper implements BudgetReportHelperInterface if (count($accountIds) > 0) { $query->whereIn('transactions.account_id', $accountIds); } - $query->groupBy(['budget_transaction_journal.budget_id', 'the_year']); + $query->groupBy(['budget_transaction_journal.budget_id', 'period_marker']); $queryResult = $query->get( [ 'budget_transaction_journal.budget_id', - DB::raw('DATE_FORMAT(transaction_journals.date,"%Y") AS the_year'), + DB::raw('DATE_FORMAT(transaction_journals.date,"' . $sqlDateFormat . '") AS period_marker'), DB::raw('SUM(transactions.amount) as sum_of_period'), ] ); $data = []; $budgets = $this->repository->getBudgets(); - $years = $this->listOfYears($start, $end); + $periods = $this->listOfPeriods($start, $end); // do budget "zero" $emptyBudget = new Budget; @@ -151,12 +111,12 @@ class BudgetReportHelper implements BudgetReportHelperInterface foreach ($budgets as $budget) { $data[$budget->id] = [ 'name' => $budget->name, - 'entries' => $this->filterAmounts($queryResult, $budget->id, $years), + 'entries' => $this->filterAllAmounts($queryResult, $budget->id, $periods), 'sum' => '0', ]; } // filter out empty ones and fill sum: - $data = $this->getBudgetMultiYearMeta($data); + $data = $this->filterBudgetPeriodReport($data); return $data; } @@ -252,16 +212,34 @@ class BudgetReportHelper implements BudgetReportHelperInterface * * @return array */ - public function listOfYears(Carbon $start, Carbon $end): array + public function listOfPeriods(Carbon $start, Carbon $end): array { - $begin = clone $start; - $years = []; - while ($begin < $end) { - $years[] = $begin->year; - $begin->addYear(); + // define period to increment + $increment = 'addDay'; + $format = 'Y-m-d'; + // increment by month (for year) + if ($start->diffInMonths($end) > 1) { + $increment = 'addMonth'; + $format = 'Y-m'; } - return $years; + // increment by year (for multi year) + if ($start->diffInMonths($end) > 12) { + $increment = 'addYear'; + $format = 'Y'; + } + + $begin = clone $start; + $entries = []; + while ($begin < $end) { + $formatted = $begin->format($format); + $entries[$formatted] = $formatted; + + $begin->$increment(); + } + + return $entries; + } /** @@ -285,38 +263,22 @@ class BudgetReportHelper implements BudgetReportHelperInterface } /** - * @param Carbon $current - * @param Carbon $end + * Filters entries from the result set generated by getBudgetPeriodReport * - * @return array - */ - private function createYearHeaders(Carbon $current, Carbon $end): array - { - $headers = []; - while ($current < $end) { - $short = $current->format('m-Y'); - $headers[$short] = $current->formatLocalized((string)trans('config.month')); - $current->addMonth(); - } - - return $headers; - } - - /** * @param Collection $set * @param int $budgetId - * @param array $years + * @param array $periods * * @return array */ - private function filterAmounts(Collection $set, int $budgetId, array $years):array + private function filterAllAmounts(Collection $set, int $budgetId, array $periods):array { $arr = []; - foreach ($years as $year) { + foreach ($periods as $period) { /** @var stdClass $object */ $result = $set->filter( - function (TransactionJournal $object) use ($budgetId, $year) { - return intval($object->the_year) === $year && $budgetId === intval($object->budget_id); + function (TransactionJournal $object) use ($budgetId, $period) { + return strval($object->period_marker) === $period && $budgetId === intval($object->budget_id); } ); $amount = '0'; @@ -324,18 +286,20 @@ class BudgetReportHelper implements BudgetReportHelperInterface $amount = $result->first()->sum_of_period; } - $arr[$year] = $amount; + $arr[$period] = $amount; } return $arr; } /** + * Filters empty results from getBudgetPeriodReport + * * @param array $data * * @return array */ - private function getBudgetMultiYearMeta(array $data): array + private function filterBudgetPeriodReport(array $data): array { /** * @var int $budgetId @@ -355,31 +319,4 @@ class BudgetReportHelper implements BudgetReportHelperInterface return $data; } - /** - * @param Carbon $current - * @param Carbon $end - * @param Budget $budget - * @param Collection $accounts - * - * @return array - */ - private function getBudgetSpentData(Carbon $current, Carbon $end, Budget $budget, Collection $accounts): array - { - $sum = '0'; - $spent = []; - while ($current < $end) { - $currentEnd = clone $current; - $currentEnd->endOfMonth(); - $format = $current->format('m-Y'); - $budgetSpent = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $current, $currentEnd); - $spent[$format] = $budgetSpent; - $sum = bcadd($sum, $budgetSpent); - $current->addMonth(); - } - - return [ - 'spent' => $spent, - 'sum' => $sum, - ]; - } } diff --git a/app/Helpers/Report/BudgetReportHelperInterface.php b/app/Helpers/Report/BudgetReportHelperInterface.php index 89c785b0a8..ee1bb2a1b6 100644 --- a/app/Helpers/Report/BudgetReportHelperInterface.php +++ b/app/Helpers/Report/BudgetReportHelperInterface.php @@ -25,14 +25,6 @@ use Illuminate\Support\Collection; */ interface BudgetReportHelperInterface { - /** - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return Collection - */ - public function budgetYearOverview(Carbon $start, Carbon $end, Collection $accounts): Collection; /** * @param Carbon $start @@ -41,7 +33,7 @@ interface BudgetReportHelperInterface * * @return array */ - public function getBudgetMultiYear(Carbon $start, Carbon $end, Collection $accounts): array; + public function getBudgetPeriodReport(Carbon $start, Carbon $end, Collection $accounts): array; /** * @param Carbon $start @@ -67,6 +59,6 @@ interface BudgetReportHelperInterface * * @return array */ - public function listOfYears(Carbon $start, Carbon $end): array; + public function listOfPeriods(Carbon $start, Carbon $end): array; } diff --git a/app/Http/Controllers/Chart/ReportController.php b/app/Http/Controllers/Chart/ReportController.php index c8fecbdad2..ca02e01e37 100644 --- a/app/Http/Controllers/Chart/ReportController.php +++ b/app/Http/Controllers/Chart/ReportController.php @@ -49,20 +49,18 @@ class ReportController extends Controller * This chart, by default, is shown on the multi-year and year report pages, * which means that giving it a 2 week "period" should be enough granularity. * - * @param string $reportType * @param Carbon $start * @param Carbon $end * @param Collection $accounts * * @return \Illuminate\Http\JsonResponse */ - public function netWorth(string $reportType, Carbon $start, Carbon $end, Collection $accounts) + public function netWorth(Carbon $start, Carbon $end, Collection $accounts) { // chart properties for cache: $cache = new CacheProperties; $cache->addProperty('netWorth'); $cache->addProperty($start); - $cache->addProperty($reportType); $cache->addProperty($accounts); $cache->addProperty($end); if ($cache->has()) { diff --git a/app/Http/Controllers/Report/BudgetController.php b/app/Http/Controllers/Report/BudgetController.php index c000f86a24..aa1f844f14 100644 --- a/app/Http/Controllers/Report/BudgetController.php +++ b/app/Http/Controllers/Report/BudgetController.php @@ -29,6 +29,7 @@ class BudgetController extends Controller { /** + * * @param BudgetReportHelperInterface $helper * @param Carbon $start * @param Carbon $end @@ -36,22 +37,20 @@ class BudgetController extends Controller * * @return mixed|string */ - public function budgetMultiYear(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts) + public function budgetPeriodReport(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts) { $cache = new CacheProperties; $cache->addProperty($start); $cache->addProperty($end); - $cache->addProperty('budget-mult-year-report'); + $cache->addProperty('budget-period-report'); $cache->addProperty($accounts->pluck('id')->toArray()); if ($cache->has()) { - return $cache->get(); + //return $cache->get(); } - - $years = $helper->listOfYears($start, $end); - $budgetMultiYear = $helper->getBudgetMultiYear($start, $end, $accounts); - - $result = view('reports.partials.budget-multi-year', compact('budgetMultiYear', 'years'))->render(); + $periods = $helper->listOfPeriods($start, $end); + $budgets = $helper->getBudgetPeriodReport($start, $end, $accounts); + $result = view('reports.partials.budget-period', compact('budgets', 'periods'))->render(); $cache->store($result); return $result; @@ -86,35 +85,4 @@ class BudgetController extends Controller return $result; } - - /** - * @param BudgetReportHelperInterface $helper - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return string - */ - public function budgetYearOverview(BudgetReportHelperInterface $helper, Carbon $start, Carbon $end, Collection $accounts) - { - - // chart properties for cache: - $cache = new CacheProperties; - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty('budget-year-overview'); - $cache->addProperty($accounts->pluck('id')->toArray()); - if ($cache->has()) { - return $cache->get(); - } - - $budgets = $helper->budgetYearOverview($start, $end, $accounts); - - $result = view('reports.partials.budget-year-overview', compact('budgets'))->render(); - $cache->store($result); - - return $result; - - } - } \ No newline at end of file diff --git a/app/Http/Controllers/Transaction/SplitController.php b/app/Http/Controllers/Transaction/SplitController.php index d1f3504670..7c80d3f392 100644 --- a/app/Http/Controllers/Transaction/SplitController.php +++ b/app/Http/Controllers/Transaction/SplitController.php @@ -187,6 +187,7 @@ class SplitController extends Controller 'transactions' => $this->getTransactionDataFromRequest($request), ]; + return $array; } diff --git a/public/js/ff/reports/default/multi-year.js b/public/js/ff/reports/default/multi-year.js index 0c8213785b..1bc23389ae 100644 --- a/public/js/ff/reports/default/multi-year.js +++ b/public/js/ff/reports/default/multi-year.js @@ -5,7 +5,7 @@ $(function () { "use strict"; drawChart(); - loadAjaxPartial('budgetMultiYear', budgetMultiUri); + loadAjaxPartial('budgetPeriodReport', budgetPeriodReportUri); }); function drawChart() { diff --git a/public/js/ff/reports/default/year.js b/public/js/ff/reports/default/year.js index a28ac3f8c4..a7c5121abe 100644 --- a/public/js/ff/reports/default/year.js +++ b/public/js/ff/reports/default/year.js @@ -1,16 +1,16 @@ -/* globals google, accountIds, budgetYearOverviewUri */ +/* globals google, accountIds, budgetPeriodReportUri */ $(function () { "use strict"; drawChart(); - loadAjaxPartial('budgetOverview',budgetYearOverviewUri); + loadAjaxPartial('budgetPeriodReport', budgetPeriodReportUri); }); function drawChart() { "use strict"; - lineChart('chart/report/net-worth/' + reportType + '/' + startDate + '/' + endDate + '/' + accountIds, 'net-worth'); + lineChart('chart/report/net-worth/' + startDate + '/' + endDate + '/' + accountIds, 'net-worth'); columnChart('chart/report/in-out/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-chart'); columnChart('chart/report/in-out-sum/' + startDate + '/' + endDate + '/' + accountIds, 'income-expenses-sum-chart'); diff --git a/resources/views/reports/default/multi-year.twig b/resources/views/reports/default/multi-year.twig index 0470f1a6dc..95f2932824 100644 --- a/resources/views/reports/default/multi-year.twig +++ b/resources/views/reports/default/multi-year.twig @@ -85,13 +85,29 @@
+ {# This is the budget overview generated by budget period report #}

{{ 'budgets'|_ }}

-
+
+
+
+
+
+ + + {# This is the chart that belongs to the above overview. #} +
+
+
+
+

{{ 'chart'|_ }}

+
+
+
@@ -124,7 +140,7 @@ var expenseReportUri = '{{ route('reports.data.expenseReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; var incExpReportUri = '{{ route('reports.data.incExpReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; - var budgetMultiUri = '{{ route('reports.data.budgetMultiYear', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var budgetPeriodReportUri = '{{ route('reports.data.budgetPeriodReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; diff --git a/resources/views/reports/default/year.twig b/resources/views/reports/default/year.twig index 0f99348969..de0a4df7bb 100644 --- a/resources/views/reports/default/year.twig +++ b/resources/views/reports/default/year.twig @@ -84,17 +84,21 @@
+ {# This is the budget overview generated by budget period report #}

{{ 'budgets'|_ }}

-
+
+ + + {# This is the chart that belongs to the above overview. #}
@@ -129,7 +133,7 @@ var expenseReportUri = '{{ route('reports.data.expenseReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; var incExpReportUri = '{{ route('reports.data.incExpReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; - var budgetYearOverviewUri = '{{ route('reports.data.budgetYearOverview', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; + var budgetPeriodReportUri = '{{ route('reports.data.budgetPeriodReport', [start.format('Ymd'), end.format('Ymd'), accountIds]) }}'; diff --git a/resources/views/reports/partials/budget-multi-year.twig b/resources/views/reports/partials/budget-period.twig similarity index 78% rename from resources/views/reports/partials/budget-multi-year.twig rename to resources/views/reports/partials/budget-period.twig index 7fc30ea9f3..7c4b16cee4 100644 --- a/resources/views/reports/partials/budget-multi-year.twig +++ b/resources/views/reports/partials/budget-period.twig @@ -2,19 +2,19 @@ {{ 'budget'|_ }} - {% for year in years %} - {{ year }} + {% for period in periods %} + {{ period }} {% endfor %} {{ 'sum'|_ }} - {% for id, info in budgetMultiYear %} + {% for id, info in budgets %} + ({{ info.name }}) {% if id == 0 %} {{ info.name }} - {% else %} {{ info.name }} {% endif %} diff --git a/resources/views/reports/partials/budget-year-overview.twig b/resources/views/reports/partials/budget-year-overview.twig deleted file mode 100644 index 6836d0d25b..0000000000 --- a/resources/views/reports/partials/budget-year-overview.twig +++ /dev/null @@ -1,25 +0,0 @@ - - - - - {% for date, header in budgets.get('headers') %} - - {% endfor %} - - - - {% set spentData = budgets.get('spent') %} - {% for budgetId, budgetName in budgets.get('budgets') %} - - - {% for date, header in budgets.get('headers') %} - - {% endfor %} - - - {% endfor %} - - -
 {{ header }}
- {{ budgetName }} - {{ spentData[budgetId][date]|formatAmount }}
\ No newline at end of file diff --git a/routes/web.php b/routes/web.php index bff941d52f..37bae1859b 100755 --- a/routes/web.php +++ b/routes/web.php @@ -237,7 +237,7 @@ Route::group( // reports: Route::get('/chart/report/in-out/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@yearInOut']); Route::get('/chart/report/in-out-sum/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@yearInOutSummarized']); - Route::get('/chart/report/net-worth/{reportType}/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@netWorth']); + Route::get('/chart/report/net-worth/{start_date}/{end_date}/{accountList}', ['uses' => 'Chart\ReportController@netWorth']); /** @@ -388,19 +388,12 @@ Route::group( '/reports/data/budget-report/{start_date}/{end_date}/{accountList}', ['uses' => 'Report\BudgetController@budgetReport', 'as' => 'reports.data.budgetReport'] ); - // budget year overview + // budget period overview Route::get( - '/reports/data/budget-year-overview/{start_date}/{end_date}/{accountList}', - ['uses' => 'Report\BudgetController@budgetYearOverview', 'as' => 'reports.data.budgetYearOverview'] + '/reports/data/budget-period/{start_date}/{end_date}/{accountList}', + ['uses' => 'Report\BudgetController@budgetPeriodReport', 'as' => 'reports.data.budgetPeriodReport'] ); - // budget multi year overview - Route::get( - '/reports/data/budget-multi-year/{start_date}/{end_date}/{accountList}', - ['uses' => 'Report\BudgetController@budgetMultiYear', 'as' => 'reports.data.budgetMultiYear'] - ); - - /** * Rules Controller */ From 781621960da929c3120d436d04c8fa23b22387da Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 19 Nov 2016 09:26:32 +0100 Subject: [PATCH 106/709] Make sure chart is displayed. --- app/Helpers/Report/BudgetReportHelper.php | 23 ++++++++++++------- .../Controllers/Chart/BudgetController.php | 6 +++++ .../views/reports/partials/budget-period.twig | 7 +----- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/app/Helpers/Report/BudgetReportHelper.php b/app/Helpers/Report/BudgetReportHelper.php index 1aa1c45711..9bfa5a46d9 100644 --- a/app/Helpers/Report/BudgetReportHelper.php +++ b/app/Helpers/Report/BudgetReportHelper.php @@ -47,6 +47,8 @@ class BudgetReportHelper implements BudgetReportHelperInterface } /** + * TODO the query called here must be moved to a repository. + * * @param Carbon $start * @param Carbon $end * @param Collection $accounts @@ -215,25 +217,29 @@ class BudgetReportHelper implements BudgetReportHelperInterface public function listOfPeriods(Carbon $start, Carbon $end): array { // define period to increment - $increment = 'addDay'; - $format = 'Y-m-d'; + $increment = 'addDay'; + $format = 'Y-m-d'; + $displayFormat = strval(trans('config.month_and_day')); // increment by month (for year) if ($start->diffInMonths($end) > 1) { - $increment = 'addMonth'; - $format = 'Y-m'; + $increment = 'addMonth'; + $format = 'Y-m'; + $displayFormat = strval(trans('config.month')); } // increment by year (for multi year) if ($start->diffInMonths($end) > 12) { - $increment = 'addYear'; - $format = 'Y'; + $increment = 'addYear'; + $format = 'Y'; + $displayFormat = strval(trans('config.year')); } $begin = clone $start; $entries = []; while ($begin < $end) { $formatted = $begin->format($format); - $entries[$formatted] = $formatted; + $displayed = $begin->formatLocalized($displayFormat); + $entries[$formatted] = $displayed; $begin->$increment(); } @@ -274,7 +280,8 @@ class BudgetReportHelper implements BudgetReportHelperInterface private function filterAllAmounts(Collection $set, int $budgetId, array $periods):array { $arr = []; - foreach ($periods as $period) { + $keys = array_keys($periods); + foreach ($keys as $period) { /** @var stdClass $object */ $result = $set->filter( function (TransactionJournal $object) use ($budgetId, $period) { diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index 43536e67cc..fbcc415869 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -186,6 +186,9 @@ class BudgetController extends Controller } /** + * + * TODO use the NEW query that will be in the repository. Because that query will be shared between the budget period report (table for all budgets) + * TODO and this chart (a single budget) * @param BudgetRepositoryInterface $repository * @param Budget $budget * @param Carbon $start @@ -196,6 +199,9 @@ class BudgetController extends Controller */ public function period(BudgetRepositoryInterface $repository, Budget $budget, Carbon $start, Carbon $end, Collection $accounts) { + + + // chart properties for cache: $cache = new CacheProperties(); $cache->addProperty($start); diff --git a/resources/views/reports/partials/budget-period.twig b/resources/views/reports/partials/budget-period.twig index 7c4b16cee4..e801a6e1b6 100644 --- a/resources/views/reports/partials/budget-period.twig +++ b/resources/views/reports/partials/budget-period.twig @@ -12,12 +12,7 @@ {% for id, info in budgets %} - ({{ info.name }}) - {% if id == 0 %} - {{ info.name }} - {% else %} - {{ info.name }} - {% endif %} + {{ info.name }} {% for amount in info.entries %} From ee6b72afa5cc8c0751352dff332cc7c2107b1d67 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 19 Nov 2016 12:57:35 +0100 Subject: [PATCH 107/709] Fix some bugs related to cash accounts. --- app/Helpers/Collector/JournalCollector.php | 13 +++++-- .../Transaction/SingleController.php | 4 +-- .../Controllers/TransactionController.php | 10 ++++-- app/Models/Account.php | 28 +++++++-------- app/Repositories/Journal/JournalTasker.php | 19 ++++++++-- resources/views/transactions/show.twig | 35 +++++++------------ 6 files changed, 62 insertions(+), 47 deletions(-) diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index a4237d311d..3894133bdd 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -134,7 +134,9 @@ class JournalCollector implements JournalCollectorInterface { $this->run = true; $set = $this->query->get(array_values($this->fields)); - $set = $this->filterTransfers($set); + Log::debug(sprintf('Count of set is %d', $set->count())); + $set = $this->filterTransfers($set); + Log::debug(sprintf('Count of set after filterTransfers() is %d', $set->count())); // loop for decryption. $set->each( @@ -374,6 +376,7 @@ class JournalCollector implements JournalCollectorInterface public function setTypes(array $types): JournalCollectorInterface { if (count($types) > 0) { + Log::debug('Set query collector types', $types); $this->query->whereIn('transaction_types.type', $types); } @@ -390,7 +393,9 @@ class JournalCollector implements JournalCollectorInterface $accountIds = $this->accountIds; $this->query->where( function (EloquentBuilder $q1) use ($accountIds) { - // set 1 + // set 1: + // where source is in the set of $accounts + // but destination is not. $q1->where( function (EloquentBuilder $q2) use ($accountIds) { // transactions.account_id in set @@ -400,7 +405,9 @@ class JournalCollector implements JournalCollectorInterface } ); - // or set 2 + // set 1: + // where source is not in the set of $accounts + // but destination is. $q1->orWhere( function (EloquentBuilder $q3) use ($accountIds) { // transactions.account_id not in set diff --git a/app/Http/Controllers/Transaction/SingleController.php b/app/Http/Controllers/Transaction/SingleController.php index 18f4d672eb..d917fac89b 100644 --- a/app/Http/Controllers/Transaction/SingleController.php +++ b/app/Http/Controllers/Transaction/SingleController.php @@ -189,9 +189,9 @@ class SingleController extends Controller 'budget_id' => TransactionJournal::budgetId($journal), 'tags' => join(',', $journal->tags->pluck('tag')->toArray()), 'source_account_id' => $sourceAccounts->first()->id, - 'source_account_name' => $sourceAccounts->first()->name, + 'source_account_name' => $sourceAccounts->first()->edit_name, 'destination_account_id' => $destinationAccounts->first()->id, - 'destination_account_name' => $destinationAccounts->first()->name, + 'destination_account_name' => $destinationAccounts->first()->edit_name, 'amount' => TransactionJournal::amountPositive($journal), // new custom fields: diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 5b6caeb74d..594f5f103e 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -19,6 +19,7 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalTaskerInterface; use Illuminate\Http\Request; +use Log; use Preferences; use Response; use View; @@ -63,8 +64,13 @@ class TransactionController extends Controller $subTitle = trans('firefly.title_' . $what); $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); $collector = new JournalCollector(auth()->user()); - $collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts() - ->withOpposingAccount(); + $collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts(); + + // do not filter transfers if $what = transfer. + if (!in_array($what, ['transfer', 'transfers'])) { + Log::debug('Also get opposing account info.'); + $collector->withOpposingAccount(); + } $journals = $collector->getPaginatedJournals(); $journals->setPath('transactions/' . $what); diff --git a/app/Models/Account.php b/app/Models/Account.php index 316da495b8..2b13ed17a5 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -119,6 +119,20 @@ class Account extends Model return $this->belongsTo('FireflyIII\Models\AccountType'); } + /** + * @return string + */ + public function getEditNameAttribute(): string + { + $name = $this->name; + + if ($this->accountType->type === AccountType::CASH) { + return ''; + } + + return $name; + } + /** * FIxxME can return null * @@ -229,20 +243,6 @@ class Account extends Model return $journal->date; } - /** - * @return string - */ - public function nameForEdit(): string - { - $name = $this->name; - - if ($this->accountType->type === AccountType::CASH) { - return ''; - } - - return $name; - } - /** * @return HasMany */ diff --git a/app/Repositories/Journal/JournalTasker.php b/app/Repositories/Journal/JournalTasker.php index 8cd0c74974..f4337eb8d6 100644 --- a/app/Repositories/Journal/JournalTasker.php +++ b/app/Repositories/Journal/JournalTasker.php @@ -15,6 +15,7 @@ namespace FireflyIII\Repositories\Journal; use Crypt; use DB; +use FireflyIII\Models\AccountType; use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; @@ -114,7 +115,9 @@ class JournalTasker implements JournalTaskerInterface ) ->with(['budgets', 'categories']) ->leftJoin('accounts as source_accounts', 'transactions.account_id', '=', 'source_accounts.id') + ->leftJoin('account_types as source_account_types', 'source_accounts.account_type_id', '=', 'source_account_types.id') ->leftJoin('accounts as destination_accounts', 'destination.account_id', '=', 'destination_accounts.id') + ->leftJoin('account_types as destination_account_types', 'destination_accounts.account_type_id', '=', 'destination_account_types.id') ->where('transactions.amount', '<', 0) ->whereNull('transactions.deleted_at') ->get( @@ -123,12 +126,14 @@ class JournalTasker implements JournalTaskerInterface 'transactions.account_id', 'source_accounts.name as account_name', 'source_accounts.encrypted as account_encrypted', + 'source_account_types.type as account_type', 'transactions.amount', 'transactions.description', 'destination.id as destination_id', 'destination.account_id as destination_account_id', 'destination_accounts.name as destination_account_name', 'destination_accounts.encrypted as destination_account_encrypted', + 'destination_account_types.type as destination_account_type', ] ); @@ -141,17 +146,18 @@ class JournalTasker implements JournalTaskerInterface $budget = $entry->budgets->first(); $category = $entry->categories->first(); $transaction = [ - 'source_id' => $entry->id, - 'source_amount' => $entry->amount, - + 'source_id' => $entry->id, + 'source_amount' => $entry->amount, 'description' => $entry->description, 'source_account_id' => $entry->account_id, 'source_account_name' => intval($entry->account_encrypted) === 1 ? Crypt::decrypt($entry->account_name) : $entry->account_name, + 'source_account_type' => $entry->account_type, 'source_account_before' => $sourceBalance, 'source_account_after' => bcadd($sourceBalance, $entry->amount), 'destination_id' => $entry->destination_id, 'destination_amount' => bcmul($entry->amount, '-1'), 'destination_account_id' => $entry->destination_account_id, + 'destination_account_type' => $entry->destination_account_type, 'destination_account_name' => intval($entry->destination_account_encrypted) === 1 ? Crypt::decrypt($entry->destination_account_name) : $entry->destination_account_name, 'destination_account_before' => $destinationBalance, @@ -159,6 +165,13 @@ class JournalTasker implements JournalTaskerInterface 'budget_id' => is_null($budget) ? 0 : $budget->id, 'category' => is_null($category) ? '' : $category->name, ]; + if ($entry->destination_account_type === AccountType::CASH) { + $transaction['destination_account_name'] = ''; + } + + if ($entry->account_type === AccountType::CASH) { + $transaction['source_account_name'] = ''; + } $transactions[] = $transaction; diff --git a/resources/views/transactions/show.twig b/resources/views/transactions/show.twig index 2ade2b2752..82bba01b45 100644 --- a/resources/views/transactions/show.twig +++ b/resources/views/transactions/show.twig @@ -277,14 +277,24 @@ {% endif %} - {{ transaction.source_account_name }} + {% if transaction.source_account_type == 'Cash account' %} + (cash) + {% else %} + {{ transaction.source_account_name }} + {% endif %} + {{ formatAmountWithCode(transaction.source_account_before,journal.transactionCurrency.code) }} ⟶ {{ formatAmountWithCode(transaction.source_account_after,journal.transactionCurrency.code) }} - {{ transaction.destination_account_name }} + {% if transaction.destination_account_type == 'Cash account' %} + (cash) + {% else %} + {{ transaction.destination_account_name }} + {% endif %} + {{ formatAmountWithCode(transaction.destination_account_before,journal.transactionCurrency.code) }} @@ -313,27 +323,6 @@ {{ transactionIdCategories(transaction.source_id) }} - {# - - - {% if (index+1) != transactions|length or what == 'transfer' %} - {{ t.description }} - {% endif %} - - {{ t.account.name }} ({{ t.account.accounttype.type|_ }}) - {{ t.sum|formatAmount }} - {{ t.before|formatAmount }} → {{ (t.sum+t.before)|formatAmount }} - - {% if (index+1) != transactions|length or what == 'transfer' %} - {{ transactionBudgets(t)|raw }} - {% endif %} - - - {% if (index+1) != transactions|length or what == 'transfer' %} - {{ transactionCategories(t)|raw }} - {% endif %} - - #} {% endfor %} From 50b72cf229ad2f679a9b78aa22bb3cecc61457ee Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 19 Nov 2016 13:37:44 +0100 Subject: [PATCH 108/709] More chart optimisations. --- .../Budget/BudgetChartGeneratorInterface.php | 5 +- .../Budget/ChartJsBudgetChartGenerator.php | 13 +- app/Helpers/Report/BudgetReportHelper.php | 127 +----------------- .../Report/BudgetReportHelperInterface.php | 8 -- .../Controllers/Chart/BudgetController.php | 72 ++++++---- .../Controllers/Report/BudgetController.php | 5 +- app/Repositories/Budget/BudgetRepository.php | 102 ++++++++++++++ .../Budget/BudgetRepositoryInterface.php | 26 ++++ app/Support/Navigation.php | 41 +++++- 9 files changed, 229 insertions(+), 170 deletions(-) diff --git a/app/Generator/Chart/Budget/BudgetChartGeneratorInterface.php b/app/Generator/Chart/Budget/BudgetChartGeneratorInterface.php index 4983d9635a..9d17fcb204 100644 --- a/app/Generator/Chart/Budget/BudgetChartGeneratorInterface.php +++ b/app/Generator/Chart/Budget/BudgetChartGeneratorInterface.php @@ -39,12 +39,11 @@ interface BudgetChartGeneratorInterface public function frontpage(Collection $entries): array; /** - * @param Collection $entries - * @param string $viewRange + * @param array $entries * * @return array */ - public function period(Collection $entries, string $viewRange) : array; + public function period(array $entries) : array; /** * @param Collection $budgets diff --git a/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php b/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php index cf78936c4d..6af797b8a3 100644 --- a/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php +++ b/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php @@ -101,15 +101,15 @@ class ChartJsBudgetChartGenerator implements BudgetChartGeneratorInterface } /** - * @param Collection $entries - * @param string $viewRange + * @param array $entries * * @return array */ - public function period(Collection $entries, string $viewRange) : array + public function period(array $entries) : array { + $data = [ - 'labels' => [], + 'labels' => array_keys($entries), 'datasets' => [ 0 => [ 'label' => trans('firefly.budgeted'), @@ -122,9 +122,8 @@ class ChartJsBudgetChartGenerator implements BudgetChartGeneratorInterface ], 'count' => 2, ]; - foreach ($entries as $entry) { - $label = Navigation::periodShow($entry['date'], $viewRange); - $data['labels'][] = $label; + + foreach ($entries as $label => $entry) { // data set 0 is budgeted // data set 1 is spent: $data['datasets'][0]['data'][] = $entry['budgeted']; diff --git a/app/Helpers/Report/BudgetReportHelper.php b/app/Helpers/Report/BudgetReportHelper.php index 9bfa5a46d9..675c4e2046 100644 --- a/app/Helpers/Report/BudgetReportHelper.php +++ b/app/Helpers/Report/BudgetReportHelper.php @@ -15,15 +15,14 @@ namespace FireflyIII\Helpers\Report; use Carbon\Carbon; -use DB; use FireflyIII\Helpers\Collection\Budget as BudgetCollection; use FireflyIII\Helpers\Collection\BudgetLine; use FireflyIII\Models\Budget; use FireflyIII\Models\LimitRepetition; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; -use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; +use Navigation; use stdClass; /** @@ -47,8 +46,6 @@ class BudgetReportHelper implements BudgetReportHelperInterface } /** - * TODO the query called here must be moved to a repository. - * * @param Carbon $start * @param Carbon $end * @param Collection $accounts @@ -57,50 +54,10 @@ class BudgetReportHelper implements BudgetReportHelperInterface */ public function getBudgetPeriodReport(Carbon $start, Carbon $end, Collection $accounts): array { - // get account ID's. - $accountIds = $accounts->pluck('id')->toArray(); - - // define period to group on: - $sqlDateFormat = '%Y-%m-%d'; - // monthly report (for year) - if ($start->diffInMonths($end) > 1) { - $sqlDateFormat = '%Y-%m'; - } - - // yearly report (for multi year) - if ($start->diffInMonths($end) > 12) { - $sqlDateFormat = '%Y'; - } - - // build query. - $query = TransactionJournal - ::leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') - ->leftJoin( - 'transactions', function (JoinClause $join) { - $join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0); - } - ) - ->whereNull('transaction_journals.deleted_at') - ->whereNull('transactions.deleted_at') - ->where('transaction_types.type', 'Withdrawal') - ->where('transaction_journals.user_id', auth()->user()->id); - - if (count($accountIds) > 0) { - $query->whereIn('transactions.account_id', $accountIds); - } - $query->groupBy(['budget_transaction_journal.budget_id', 'period_marker']); - $queryResult = $query->get( - [ - 'budget_transaction_journal.budget_id', - DB::raw('DATE_FORMAT(transaction_journals.date,"' . $sqlDateFormat . '") AS period_marker'), - DB::raw('SUM(transactions.amount) as sum_of_period'), - ] - ); - - $data = []; - $budgets = $this->repository->getBudgets(); - $periods = $this->listOfPeriods($start, $end); + $budgets = $this->repository->getBudgets(); + $queryResult = $this->repository->getBudgetPeriodReport($budgets, $accounts, $start, $end); + $data = []; + $periods = Navigation::listOfPeriods($start, $end); // do budget "zero" $emptyBudget = new Budget; @@ -108,12 +65,11 @@ class BudgetReportHelper implements BudgetReportHelperInterface $emptyBudget->name = strval(trans('firefly.no_budget')); $budgets->push($emptyBudget); - // get all budgets and years. foreach ($budgets as $budget) { $data[$budget->id] = [ 'name' => $budget->name, - 'entries' => $this->filterAllAmounts($queryResult, $budget->id, $periods), + 'entries' => $this->repository->filterAmounts($queryResult, $budget->id, $periods), 'sum' => '0', ]; } @@ -208,46 +164,6 @@ class BudgetReportHelper implements BudgetReportHelperInterface return $set; } - /** - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function listOfPeriods(Carbon $start, Carbon $end): array - { - // define period to increment - $increment = 'addDay'; - $format = 'Y-m-d'; - $displayFormat = strval(trans('config.month_and_day')); - // increment by month (for year) - if ($start->diffInMonths($end) > 1) { - $increment = 'addMonth'; - $format = 'Y-m'; - $displayFormat = strval(trans('config.month')); - } - - // increment by year (for multi year) - if ($start->diffInMonths($end) > 12) { - $increment = 'addYear'; - $format = 'Y'; - $displayFormat = strval(trans('config.year')); - } - - $begin = clone $start; - $entries = []; - while ($begin < $end) { - $formatted = $begin->format($format); - $displayed = $begin->formatLocalized($displayFormat); - $entries[$formatted] = $displayed; - - $begin->$increment(); - } - - return $entries; - - } - /** * @param Budget $budget * @param LimitRepetition $repetition @@ -268,37 +184,6 @@ class BudgetReportHelper implements BudgetReportHelperInterface } - /** - * Filters entries from the result set generated by getBudgetPeriodReport - * - * @param Collection $set - * @param int $budgetId - * @param array $periods - * - * @return array - */ - private function filterAllAmounts(Collection $set, int $budgetId, array $periods):array - { - $arr = []; - $keys = array_keys($periods); - foreach ($keys as $period) { - /** @var stdClass $object */ - $result = $set->filter( - function (TransactionJournal $object) use ($budgetId, $period) { - return strval($object->period_marker) === $period && $budgetId === intval($object->budget_id); - } - ); - $amount = '0'; - if (!is_null($result->first())) { - $amount = $result->first()->sum_of_period; - } - - $arr[$period] = $amount; - } - - return $arr; - } - /** * Filters empty results from getBudgetPeriodReport * diff --git a/app/Helpers/Report/BudgetReportHelperInterface.php b/app/Helpers/Report/BudgetReportHelperInterface.php index ee1bb2a1b6..ad8540a22a 100644 --- a/app/Helpers/Report/BudgetReportHelperInterface.php +++ b/app/Helpers/Report/BudgetReportHelperInterface.php @@ -53,12 +53,4 @@ interface BudgetReportHelperInterface */ public function getBudgetsWithExpenses(Carbon $start, Carbon $end, Collection $accounts): Collection; - /** - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function listOfPeriods(Carbon $start, Carbon $end): array; - } diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index fbcc415869..e1cd94e3dc 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -24,7 +24,6 @@ use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; -use Log; use Navigation; use Preferences; use Response; @@ -147,7 +146,6 @@ class BudgetController extends Controller */ public function frontpage(BudgetRepositoryInterface $repository) { - Log::debug('Hello'); $start = session('start', Carbon::now()->startOfMonth()); $end = session('end', Carbon::now()->endOfMonth()); // chart properties for cache: @@ -189,6 +187,7 @@ class BudgetController extends Controller * * TODO use the NEW query that will be in the repository. Because that query will be shared between the budget period report (table for all budgets) * TODO and this chart (a single budget) + * * @param BudgetRepositoryInterface $repository * @param Budget $budget * @param Carbon $start @@ -199,9 +198,6 @@ class BudgetController extends Controller */ public function period(BudgetRepositoryInterface $repository, Budget $budget, Carbon $start, Carbon $end, Collection $accounts) { - - - // chart properties for cache: $cache = new CacheProperties(); $cache->addProperty($start); @@ -211,40 +207,60 @@ class BudgetController extends Controller $cache->addProperty('budget'); $cache->addProperty('period'); if ($cache->has()) { - return Response::json($cache->get()); + //return Response::json($cache->get()); } - // loop over period, add by users range: - $current = clone $start; - $viewRange = Preferences::get('viewRange', '1M')->data; - $set = new Collection; + + // the expenses: + $periods = Navigation::listOfPeriods($start, $end); + $result = $repository->getBudgetPeriodReport(new Collection([$budget]), $accounts, $start, $end); + $entries = $repository->filterAmounts($result, $budget->id, $periods); + $budgeted = []; + + // the budget limits: + $range = '1D'; + $key = 'Y-m-d'; + if ($start->diffInMonths($end) > 1) { + $range = '1M'; + $key = 'Y-m'; + } + + if ($start->diffInMonths($end) > 12) { + $range = '1Y'; + $key = 'Y'; + } + $repetitions = $repository->getAllBudgetLimitRepetitions($start, $end); - - + $current = clone $start; while ($current < $end) { - $currentStart = clone $current; - $currentEnd = Navigation::endOfPeriod($currentStart, $viewRange); - $reps = $repetitions->filter( - function (LimitRepetition $repetition) use ($budget, $currentStart) { - if ($repetition->budget_id === $budget->id && $repetition->startdate == $currentStart) { + $currentStart = Navigation::startOfPeriod($current, $range); + $currentEnd = Navigation::endOfPeriod($current, $range); + $reps = $repetitions->filter( + function (LimitRepetition $repetition) use ($budget, $currentStart, $currentEnd) { + if ($repetition->budget_id === $budget->id && $repetition->startdate >= $currentStart && $repetition->enddate <= $currentEnd) { return true; } return false; } ); - $budgeted = $reps->sum('amount'); - $spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $currentStart, $currentEnd); - $entry = [ - 'date' => clone $currentStart, - 'budgeted' => $budgeted, - 'spent' => $spent, - ]; - $set->push($entry); + $index = $currentStart->format($key); + $budgeted[$index] = $reps->sum('amount'); $currentEnd->addDay(); $current = clone $currentEnd; - } - $data = $this->generator->period($set, $viewRange); + + // join them: + $result = []; + foreach (array_keys($periods) as $period) { + $nice = $periods[$period]; + $result[$nice] = [ + 'spent' => isset($entries[$period]) ? $entries[$period] : '0', + 'budgeted' => isset($entries[$period]) ? $budgeted[$period] : 0, + ]; + } + + $data = $this->generator->period($result); + $cache->store($data); return Response::json($data); @@ -336,7 +352,7 @@ class BudgetController extends Controller { // collector $collector = new JournalCollector(auth()->user()); - $types = [TransactionType::WITHDRAWAL]; + $types = [TransactionType::WITHDRAWAL]; $collector->setAllAssetAccounts()->setTypes($types)->setRange($start, $end)->withoutBudget(); $journals = $collector->getJournals(); $sum = '0'; diff --git a/app/Http/Controllers/Report/BudgetController.php b/app/Http/Controllers/Report/BudgetController.php index aa1f844f14..09aa4a0475 100644 --- a/app/Http/Controllers/Report/BudgetController.php +++ b/app/Http/Controllers/Report/BudgetController.php @@ -19,6 +19,7 @@ use FireflyIII\Helpers\Report\BudgetReportHelperInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; +use Navigation; /** * Class BudgetController @@ -45,10 +46,10 @@ class BudgetController extends Controller $cache->addProperty('budget-period-report'); $cache->addProperty($accounts->pluck('id')->toArray()); if ($cache->has()) { - //return $cache->get(); + return $cache->get(); } - $periods = $helper->listOfPeriods($start, $end); + $periods = Navigation::listOfPeriods($start, $end); $budgets = $helper->getBudgetPeriodReport($start, $end, $accounts); $result = view('reports.partials.budget-period', compact('budgets', 'periods'))->render(); $cache->store($result); diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 6726287704..e1cc7fa0f9 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -14,6 +14,7 @@ declare(strict_types = 1); namespace FireflyIII\Repositories\Budget; use Carbon\Carbon; +use DB; use FireflyIII\Events\StoredBudgetLimit; use FireflyIII\Events\UpdatedBudgetLimit; use FireflyIII\Models\Budget; @@ -27,6 +28,7 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; use Log; +use stdClass; /** * Class BudgetRepository @@ -72,6 +74,39 @@ class BudgetRepository implements BudgetRepositoryInterface return true; } + /** + * Filters entries from the result set generated by getBudgetPeriodReport + * + * @param Collection $set + * @param int $budgetId + * @param array $periods + * + * @return array + */ + public function filterAmounts(Collection $set, int $budgetId, array $periods): array + { + $arr = []; + $keys = array_keys($periods); + foreach ($keys as $period) { + /** @var stdClass $object */ + $result = $set->filter( + function (TransactionJournal $object) use ($budgetId, $period) { + $result = strval($object->period_marker) === strval($period) && $budgetId === intval($object->budget_id); + + return $result; + } + ); + $amount = '0'; + if (!is_null($result->first())) { + $amount = $result->first()->sum_of_period; + } + + $arr[$period] = $amount; + } + + return $arr; + } + /** * Find a budget. * @@ -175,6 +210,73 @@ class BudgetRepository implements BudgetRepositoryInterface return $set; } + /** + * This method is being used to generate the budget overview in the year/multi-year report. More specifically, this + * method runs the query and returns the result that is used for this report. + * + * The query is used in both the year/multi-year budget overview AND in the accompanying chart. + * + * @param Collection $budgets + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + public function getBudgetPeriodReport(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): Collection + { + // get account ID's. + $accountIds = $accounts->pluck('id')->toArray(); + + // get budget ID's. + $budgetIds = $budgets->pluck('id')->toArray(); + + // define period to group on: + $sqlDateFormat = '%Y-%m-%d'; + // monthly report (for year) + if ($start->diffInMonths($end) > 1) { + $sqlDateFormat = '%Y-%m'; + } + + // yearly report (for multi year) + if ($start->diffInMonths($end) > 12) { + $sqlDateFormat = '%Y'; + } + + // build query. + $query = TransactionJournal + ::leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') + ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') + ->leftJoin( + 'transactions', function (JoinClause $join) { + $join->on('transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transactions.amount', '<', 0); + } + ) + ->whereNull('transaction_journals.deleted_at') + ->whereNull('transactions.deleted_at') + ->where('transaction_types.type', 'Withdrawal') + ->where('transaction_journals.user_id', auth()->user()->id); + + if (count($accountIds) > 0) { + $query->whereIn('transactions.account_id', $accountIds); + } + + if (count($budgetIds) > 0) { + $query->whereIn('budget_transaction_journal.budget_id', $budgetIds); + } + + $query->groupBy(['budget_transaction_journal.budget_id', 'period_marker']); + + return $query->get( + [ + 'budget_transaction_journal.budget_id', + DB::raw(sprintf('DATE_FORMAT(transaction_journals.date,"%s") AS period_marker', $sqlDateFormat)), + DB::raw('SUM(transactions.amount) as sum_of_period'), + ] + ); + + } + /** * @return Collection */ diff --git a/app/Repositories/Budget/BudgetRepositoryInterface.php b/app/Repositories/Budget/BudgetRepositoryInterface.php index b7116dcbd2..fcc74eac31 100644 --- a/app/Repositories/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetRepositoryInterface.php @@ -38,6 +38,17 @@ interface BudgetRepositoryInterface */ public function destroy(Budget $budget): bool; + /** + * Filters entries from the result set generated by getBudgetPeriodReport + * + * @param Collection $set + * @param int $budgetId + * @param array $periods + * + * @return array + */ + public function filterAmounts(Collection $set, int $budgetId, array $periods): array; + /** * Find a budget. * @@ -79,6 +90,21 @@ interface BudgetRepositoryInterface */ public function getAllBudgetLimitRepetitions(Carbon $start, Carbon $end): Collection; + /** + * This method is being used to generate the budget overview in the year/multi-year report. More specifically, this + * method runs the query and returns the result that is used for this report. + * + * The query is used in both the year/multi-year budget overview AND in the accompanying chart. + * + * @param Collection $budgets + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + public function getBudgetPeriodReport(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): Collection; + /** * @return Collection */ diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php index 665873596e..449ac4e6ae 100644 --- a/app/Support/Navigation.php +++ b/app/Support/Navigation.php @@ -24,7 +24,6 @@ use FireflyIII\Exceptions\FireflyException; class Navigation { - /** * @param \Carbon\Carbon $theDate * @param $repeatFreq @@ -170,6 +169,46 @@ class Navigation return $currentEnd; } + /** + * @param \Carbon\Carbon $start + * @param \Carbon\Carbon $end + * + * @return array + */ + public function listOfPeriods(Carbon $start, Carbon $end): array + { + // define period to increment + $increment = 'addDay'; + $format = 'Y-m-d'; + $displayFormat = strval(trans('config.month_and_day')); + // increment by month (for year) + if ($start->diffInMonths($end) > 1) { + $increment = 'addMonth'; + $format = 'Y-m'; + $displayFormat = strval(trans('config.month')); + } + + // increment by year (for multi year) + if ($start->diffInMonths($end) > 12) { + $increment = 'addYear'; + $format = 'Y'; + $displayFormat = strval(trans('config.year')); + } + + $begin = clone $start; + $entries = []; + while ($begin < $end) { + $formatted = $begin->format($format); + $displayed = $begin->formatLocalized($displayFormat); + $entries[$formatted] = $displayed; + + $begin->$increment(); + } + + return $entries; + + } + /** * @param \Carbon\Carbon $date * @param $repeatFrequency From 23925a007605e27ed45b7e1015333ed11ab807f6 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 19 Nov 2016 15:17:00 +0100 Subject: [PATCH 109/709] Enabled cache [skip ci] --- app/Http/Controllers/Chart/BudgetController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index e1cd94e3dc..e337126f71 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -207,7 +207,7 @@ class BudgetController extends Controller $cache->addProperty('budget'); $cache->addProperty('period'); if ($cache->has()) { - //return Response::json($cache->get()); + return Response::json($cache->get()); } // the expenses: From 240f3c126bb9b8b2028918b40b32af92a0a01161 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 19 Nov 2016 15:55:49 +0100 Subject: [PATCH 110/709] Restored some tests. --- .env.testing | 45 +++++++ config/database.php | 121 ++++-------------- .../2016_08_25_091522_changes_for_3101.php | 2 +- test.sh | 41 ++++++ tests/ExampleTest.php | 6 +- tests/TestCase.php | 87 ++++++++++++- .../Controllers/HomeControllerTest.php | 63 +++++++++ tests/unit/Models/TransactionTypeTest.php | 43 +++++++ 8 files changed, 306 insertions(+), 102 deletions(-) create mode 100755 .env.testing create mode 100755 test.sh create mode 100755 tests/acceptance/Controllers/HomeControllerTest.php create mode 100755 tests/unit/Models/TransactionTypeTest.php diff --git a/.env.testing b/.env.testing new file mode 100755 index 0000000000..3c9802e829 --- /dev/null +++ b/.env.testing @@ -0,0 +1,45 @@ +APP_ENV=testing +APP_DEBUG=true +APP_FORCE_SSL=false +APP_FORCE_ROOT= +APP_KEY=TestTestTestTestTestTestTestTest +APP_LOG_LEVEL=debug +APP_URL=http://localhost + +DB_CONNECTION=sqlite +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_USERNAME=homestead +DB_PASSWORD=secret + +BROADCAST_DRIVER=log +CACHE_DRIVER=file +SESSION_DRIVER=file +QUEUE_DRIVER=sync + +COOKIE_PATH="/" +COOKIE_DOMAIN= +COOKIE_SECURE=false + +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_DRIVER=smtp +MAIL_HOST=mailtrap.io +MAIL_PORT=2525 +MAIL_FROM=changeme@example.com +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null + +SEND_REGISTRATION_MAIL=true +SEND_ERROR_MESSAGE=true +SHOW_INCOMPLETE_TRANSLATIONS=false + +ANALYTICS_ID= +SITE_OWNER=mail@example.com + +PUSHER_KEY= +PUSHER_SECRET= +PUSHER_APP_ID= diff --git a/config/database.php b/config/database.php index 81a863d474..451ffd9741 100755 --- a/config/database.php +++ b/config/database.php @@ -11,120 +11,51 @@ return [ - /* - |-------------------------------------------------------------------------- - | PDO Fetch Style - |-------------------------------------------------------------------------- - | - | By default, database results will be returned as instances of the PHP - | stdClass object; however, you may desire to retrieve records in an - | array format for simplicity. Here you can tweak the fetch style. - | - */ - - 'fetch' => PDO::FETCH_OBJ, - - /* - |-------------------------------------------------------------------------- - | Default Database Connection Name - |-------------------------------------------------------------------------- - | - | Here you may specify which of the database connections below you wish - | to use as your default connection for all database work. Of course - | you may use many connections at once using the Database library. - | - */ - - 'default' => env('DB_CONNECTION', 'mysql'), - - /* - |-------------------------------------------------------------------------- - | Database Connections - |-------------------------------------------------------------------------- - | - | Here are each of the database connections setup for your application. - | Of course, examples of configuring each database platform that is - | supported by Laravel is shown below to make development simple. - | - | - | All database work in Laravel is done through the PHP PDO facilities - | so make sure you have the driver for your particular database of - | choice installed on your machine before you begin development. - | - */ - + 'fetch' => PDO::FETCH_OBJ, + 'default' => env('DB_CONNECTION', 'mysql'), 'connections' => [ - 'sqlite' => [ - 'driver' => 'sqlite', - 'database' => env('DB_DATABASE', database_path('database.sqlite')), - 'prefix' => '', + 'driver' => 'sqlite', + 'database' => env('DB_DATABASE', storage_path('database/database.sqlite')), + 'prefix' => '', ], 'mysql' => [ - 'driver' => 'mysql', - 'host' => env('DB_HOST', 'localhost'), - 'port' => env('DB_PORT', '3306'), - 'database' => env('DB_DATABASE', 'forge'), - 'username' => env('DB_USERNAME', 'forge'), - 'password' => env('DB_PASSWORD', ''), - 'charset' => 'utf8', + 'driver' => 'mysql', + 'host' => env('DB_HOST', 'localhost'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci', - 'prefix' => '', - 'strict' => true, - 'engine' => null, + 'prefix' => '', + 'strict' => true, + 'engine' => null, ], 'pgsql' => [ - 'driver' => 'pgsql', - 'host' => env('DB_HOST', 'localhost'), - 'port' => env('DB_PORT', '5432'), + 'driver' => 'pgsql', + 'host' => env('DB_HOST', 'localhost'), + 'port' => env('DB_PORT', '5432'), 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), - 'charset' => 'utf8', - 'prefix' => '', - 'schema' => 'public', - 'sslmode' => 'prefer', + 'charset' => 'utf8', + 'prefix' => '', + 'schema' => 'public', + 'sslmode' => 'prefer', ], ], - - /* - |-------------------------------------------------------------------------- - | Migration Repository Table - |-------------------------------------------------------------------------- - | - | This table keeps track of all the migrations that have already run for - | your application. Using this information, we can determine which of - | the migrations on disk haven't actually been run in the database. - | - */ - - 'migrations' => 'migrations', - - /* - |-------------------------------------------------------------------------- - | Redis Databases - |-------------------------------------------------------------------------- - | - | Redis is an open source, fast, and advanced key-value store that also - | provides a richer set of commands than a typical key-value systems - | such as APC or Memcached. Laravel makes it easy to dig right in. - | - */ - - 'redis' => [ - + 'migrations' => 'migrations', + 'redis' => [ 'cluster' => false, - 'default' => [ - 'host' => env('REDIS_HOST', 'localhost'), + 'host' => env('REDIS_HOST', 'localhost'), 'password' => env('REDIS_PASSWORD', null), - 'port' => env('REDIS_PORT', 6379), + 'port' => env('REDIS_PORT', 6379), 'database' => 0, ], - ], - ]; diff --git a/database/migrations/2016_08_25_091522_changes_for_3101.php b/database/migrations/2016_08_25_091522_changes_for_3101.php index 24883b7933..15a0c54afd 100644 --- a/database/migrations/2016_08_25_091522_changes_for_3101.php +++ b/database/migrations/2016_08_25_091522_changes_for_3101.php @@ -37,7 +37,7 @@ class ChangesFor3101 extends Migration { Schema::table( 'import_jobs', function (Blueprint $table) { - $table->text('extended_status'); + $table->text('extended_status')->nullable(); } ); } diff --git a/test.sh b/test.sh new file mode 100755 index 0000000000..6c2e7847fd --- /dev/null +++ b/test.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +DATABASE=storage/database/database.sqlite +DATABASECOPY=storage/database/databasecopy.sqlite + + +# backup current config: +mv .env .env.current + +# enable testing config +cp .env.testing .env + +# clear cache: +php artisan cache:clear + +if [ "$1" == "--reset" ]; then + echo "Must reset database" + + # touch files to make sure they exist. + touch $DATABASE + touch $DATABASECOPY + + # truncate original database file + truncate $DATABASE --size 0 + + # run migration + php artisan migrate:refresh --seed + + # copy new database over backup (resets backup) + cp $DATABASE $DATABASECOPY +fi + +# take database from copy: +cp $DATABASECOPY $DATABASE + +# run PHPUnit + +phpunit + +# restore current config: +mv .env.current .env \ No newline at end of file diff --git a/tests/ExampleTest.php b/tests/ExampleTest.php index 451bd48bed..5f8b620794 100755 --- a/tests/ExampleTest.php +++ b/tests/ExampleTest.php @@ -1,9 +1,5 @@ visit('/') - ->see('Laravel'); + ->see('Firefly'); } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 2781681df5..79e8cbf40b 100755 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1,5 +1,9 @@ id)->where('name', 'viewRange')->delete(); + Preference::create( + [ + 'user_id' => $user->id, + 'name' => 'viewRange', + 'data' => $range, + ] + ); + // set period to match? + + } + if ($range === 'custom') { + $this->session( + [ + 'start' => Carbon::now()->subDays(20), + 'end' => Carbon::now(), + ] + ); + } + } + /** * Creates the application. * @@ -19,10 +52,62 @@ abstract class TestCase extends Illuminate\Foundation\Testing\TestCase */ public function createApplication() { - $app = require __DIR__.'/../bootstrap/app.php'; + $app = require __DIR__ . '/../bootstrap/app.php'; $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap(); return $app; } + + /** + * @return array + */ + public function dateRangeProvider() + { + return [ + 'one day' => ['1D'], + 'one week' => ['1W'], + 'one month' => ['1M'], + 'three months' => ['3M'], + 'six months' => ['6M'], + 'one year' => ['1Y'], + 'custom range' => ['custom'], + ]; + } + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + public function setUp() + { + parent::setUp(); + + } + + + /** + * @return User + */ + public function user() + { + $user = User::find(1); + + return $user; + } + + /** + * @param string $class + * + * @return \Mockery\MockInterface + */ + protected function mock($class) + { + $object = Mockery::mock($class); + + + $this->app->instance($class, $object); + + return $object; + } } diff --git a/tests/acceptance/Controllers/HomeControllerTest.php b/tests/acceptance/Controllers/HomeControllerTest.php new file mode 100755 index 0000000000..7d4abdcdc8 --- /dev/null +++ b/tests/acceptance/Controllers/HomeControllerTest.php @@ -0,0 +1,63 @@ +assertTrue(true); + } + + /** + * @covers FireflyIII\Http\Controllers\HomeController::dateRange + * @covers FireflyIII\Http\Controllers\HomeController::__construct + */ + public function testDateRange() + { + + $this->be($this->user()); + + $args = [ + 'start' => '2012-01-01', + 'end' => '2012-04-01', + ]; + + // if date range is > 50, should have flash. + $this->call('POST', '/daterange', $args); + $this->assertResponseStatus(200); + $this->assertSessionHas('warning', '91 days of data may take a while to load.'); + } + + /** + * @covers FireflyIII\Http\Controllers\HomeController::flush + */ + public function testFlush() + { + $this->be($this->user()); + $this->call('GET', '/flush'); + $this->assertResponseStatus(302); + } + + /** + * @covers FireflyIII\Http\Controllers\HomeController::index + * @covers FireflyIII\Http\Controllers\Controller::__construct + * @dataProvider dateRangeProvider + * + * @param $range + */ + public function testIndex($range) + { + $this->be($this->user()); + $this->changeDateRange($this->user(), $range); + $this->call('GET', '/'); + $this->assertResponseStatus(200); + } +} diff --git a/tests/unit/Models/TransactionTypeTest.php b/tests/unit/Models/TransactionTypeTest.php new file mode 100755 index 0000000000..e0c3f2aa86 --- /dev/null +++ b/tests/unit/Models/TransactionTypeTest.php @@ -0,0 +1,43 @@ +first(); + $this->assertTrue($transactionType->isDeposit()); + } + + public function testIsOpeningBalance() + { + $transactionType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first(); + $this->assertTrue($transactionType->isOpeningBalance()); + } + + public function testIsTransfer() + { + $transactionType = TransactionType::whereType(TransactionType::TRANSFER)->first(); + $this->assertTrue($transactionType->isTransfer()); + } + + public function testIsWithdrawal() + { + $transactionType = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); + $this->assertTrue($transactionType->isWithdrawal()); + } +} From 1ba35f73e167348714b4c146c031a9ba5d134f00 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 19 Nov 2016 16:00:20 +0100 Subject: [PATCH 111/709] Fixed a test --- app/Http/Middleware/Range.php | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/app/Http/Middleware/Range.php b/app/Http/Middleware/Range.php index 149e6d8eb3..cedce6ab74 100644 --- a/app/Http/Middleware/Range.php +++ b/app/Http/Middleware/Range.php @@ -130,13 +130,29 @@ class Range */ private function datePicker() { - $viewRange = Preferences::get('viewRange', '1M')->data; - $start = Session::get('start'); - $end = Session::get('end'); - $prevStart = Navigation::subtractPeriod($start, $viewRange);// subtract for previous period - $prevEnd = Navigation::endOfPeriod($prevStart, $viewRange); - $nextStart = Navigation::addPeriod($start, $viewRange, 0); // add for previous period - $nextEnd = Navigation::endOfPeriod($nextStart, $viewRange); + $viewRange = Preferences::get('viewRange', '1M')->data; + /** @var Carbon $start */ + $start = Session::get('start'); + /** @var Carbon $end */ + $end = Session::get('end'); + if ($viewRange === 'custom') { + $days = $start->diffInDays($end); + $prevStart = clone $start; + $prevStart->subDays($days); + $prevEnd = clone $start; + $nextStart = clone $end; + $nextEnd = clone $end; + $nextEnd->addDays($days); + unset($days); + } + + if ($viewRange !== 'custom') { + $prevStart = Navigation::subtractPeriod($start, $viewRange);// subtract for previous period + $prevEnd = Navigation::endOfPeriod($prevStart, $viewRange); + $nextStart = Navigation::addPeriod($start, $viewRange, 0); // add for previous period + $nextEnd = Navigation::endOfPeriod($nextStart, $viewRange); + } + $ranges = []; $ranges['current'] = [$start->format('Y-m-d'), $end->format('Y-m-d')]; $ranges['previous'] = [$prevStart->format('Y-m-d'), $prevEnd->format('Y-m-d')]; @@ -146,6 +162,7 @@ class Range default: throw new FireflyException('The date picker does not yet support "' . $viewRange . '".'); case '1D': + case 'custom': $format = (string)trans('config.month_and_day'); break; case '3M': From e4ecd0b7ff3aeaa54947bafecd7da94a42591fa4 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 19 Nov 2016 16:01:19 +0100 Subject: [PATCH 112/709] Empty travis CI file. --- .travis.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..e69de29bb2 From ff4e1838bceaeccd8c330e51a66d0f67175b21b7 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 19 Nov 2016 16:03:59 +0100 Subject: [PATCH 113/709] Update travis configuration. --- .travis.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.travis.yml b/.travis.yml index e69de29bb2..0863a11f23 100644 --- a/.travis.yml +++ b/.travis.yml @@ -0,0 +1,19 @@ +language: php +sudo: false +php: + - 7 + +install: + - phpenv config-rm xdebug.ini + - composer selfupdate + - rm composer.lock + - composer update --no-scripts + - php artisan clear-compiled + - php artisan optimize + - php artisan env + - mv -v .env.testing .env + - php artisan env + - test.sh --notest + +script: + - phpunit \ No newline at end of file From b057d69f8ee04bc2a5fe692f5a2fafbf169e879b Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 19 Nov 2016 16:07:02 +0100 Subject: [PATCH 114/709] Correct travis configuration. --- .travis.yml | 5 +++-- test.sh | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0863a11f23..0d768b2bcd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,8 @@ language: php sudo: false php: - - 7 + - '7.0' + - hhvm install: - phpenv config-rm xdebug.ini @@ -13,7 +14,7 @@ install: - php artisan env - mv -v .env.testing .env - php artisan env - - test.sh --notest + - ./test.sh --notest script: - phpunit \ No newline at end of file diff --git a/test.sh b/test.sh index 6c2e7847fd..0ece8440dc 100755 --- a/test.sh +++ b/test.sh @@ -34,8 +34,11 @@ fi cp $DATABASECOPY $DATABASE # run PHPUnit - -phpunit +if [ "$1" == "--notest" ]; then + echo "Must not run PHPUnit" +else + phpunit +fi # restore current config: mv .env.current .env \ No newline at end of file From 7ec9c090cc28849aaf1f88b0578bf01ed769bd6e Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 19 Nov 2016 16:13:57 +0100 Subject: [PATCH 115/709] Fix test script. --- .travis.yml | 2 +- test.sh | 26 +++++++++++++++++--------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0d768b2bcd..efb24c6544 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ install: - php artisan env - mv -v .env.testing .env - php artisan env - - ./test.sh --notest + - ./test.sh --notest --reset script: - phpunit \ No newline at end of file diff --git a/test.sh b/test.sh index 0ece8440dc..ba4bde7d36 100755 --- a/test.sh +++ b/test.sh @@ -1,19 +1,24 @@ #!/bin/bash -DATABASE=storage/database/database.sqlite -DATABASECOPY=storage/database/databasecopy.sqlite +DATABASE=./storage/database/database.sqlite +DATABASECOPY=./storage/database/databasecopy.sqlite +ORIGINALENV=./.env +BACKUPENV=./.env.current +TESTINGENV=./.env.testing - -# backup current config: -mv .env .env.current +# backup current config (if it exists): +if [ -f $ORIGINALENV ]; then + mv $ORIGINALENV $BACKUPENV +fi # enable testing config -cp .env.testing .env +cp $TESTINGENV $ORIGINALENV # clear cache: php artisan cache:clear -if [ "$1" == "--reset" ]; then +if [[ "$@" == "--reset" ]] +then echo "Must reset database" # touch files to make sure they exist. @@ -34,11 +39,14 @@ fi cp $DATABASECOPY $DATABASE # run PHPUnit -if [ "$1" == "--notest" ]; then +if [[ "$@" == "--notest" ]] +then echo "Must not run PHPUnit" else phpunit fi # restore current config: -mv .env.current .env \ No newline at end of file +if [ -f $BACKUPENV ]; then + mv $BACKUPENV $ORIGINALENV +fi \ No newline at end of file From 01e3f91ece5c9955695e62a83b983e3a09b4e567 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 19 Nov 2016 16:17:04 +0100 Subject: [PATCH 116/709] Do not test hhvm. Fix script. --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index efb24c6544..55481d4251 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ language: php sudo: false php: - '7.0' - - hhvm install: - phpenv config-rm xdebug.ini @@ -12,9 +11,8 @@ install: - php artisan clear-compiled - php artisan optimize - php artisan env - - mv -v .env.testing .env - - php artisan env - ./test.sh --notest --reset + - php artisan env script: - phpunit \ No newline at end of file From 5a2ef36f2ad8b8d0deca808fd4646af587a601e8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 19 Nov 2016 16:28:04 +0100 Subject: [PATCH 117/709] Fix travis script. --- .travis.yml | 2 +- test.sh | 26 ++++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 55481d4251..a8f360bfff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ install: - php artisan clear-compiled - php artisan optimize - php artisan env - - ./test.sh --notest --reset + - ./test.sh -r - php artisan env script: diff --git a/test.sh b/test.sh index ba4bde7d36..6190e186af 100755 --- a/test.sh +++ b/test.sh @@ -6,6 +6,20 @@ ORIGINALENV=./.env BACKUPENV=./.env.current TESTINGENV=./.env.testing +# do something with flags: +rflag='' +tflag='' + +while getopts 'rt' flag; do + case "${flag}" in + r) rflag='true' ;; + t) tflag='true' ;; + *) error "Unexpected option ${flag}" ;; + esac +done + + + # backup current config (if it exists): if [ -f $ORIGINALENV ]; then mv $ORIGINALENV $BACKUPENV @@ -17,7 +31,8 @@ cp $TESTINGENV $ORIGINALENV # clear cache: php artisan cache:clear -if [[ "$@" == "--reset" ]] +# reset database (optional) +if [[ $rflag == "true" ]] then echo "Must reset database" @@ -35,14 +50,21 @@ then cp $DATABASE $DATABASECOPY fi +# do not reset database (optional) +if [[ $rflag == "" ]] +then + echo "Will not reset database" +fi + # take database from copy: cp $DATABASECOPY $DATABASE # run PHPUnit -if [[ "$@" == "--notest" ]] +if [[ $tflag == "" ]] then echo "Must not run PHPUnit" else + echo "Must run PHPUnit" phpunit fi From 8554aae21e0bf92ceb231e147bc024d6391894c6 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 19 Nov 2016 16:31:39 +0100 Subject: [PATCH 118/709] Update read me file [skip ci] --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index faf06b33f4..1043c10aa8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Firefly III [![Requires PHP7](https://img.shields.io/badge/php-7.0-red.svg)](https://secure.php.net/downloads.php#v7.0.4) [![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable)](https://packagist.org/packages/grumpydictator/firefly-iii) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/JC5/firefly-iii/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/JC5/firefly-iii/?branch=master) +[![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=master)](https://travis-ci.org/JC5/firefly-iii) + ## A personal finances manager [![Screenshot](https://i.nder.be/hhfv03hp/400)](https://i.nder.be/hhfv03hp) [![Screenshot](https://i.nder.be/hhmwmqw9/400)](https://i.nder.be/hhmwmqw9) From a9795fb0958039ed269e2fc867b6be4bd6ff1ffb Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 19 Nov 2016 16:34:52 +0100 Subject: [PATCH 119/709] Update ignore file [skip ci] --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0e45076d94..cf0adc5454 100755 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ test-import.sh test-import-report.txt public/google*.html _ide_helper_models.php +.env.backup From b6473865413da49a07d899a4a4bb972bdf984ff3 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 19 Nov 2016 16:44:17 +0100 Subject: [PATCH 120/709] New release imminent. [skip ci] --- CHANGELOG.md | 23 +++++++++++++++++++++++ config/firefly.php | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8582dae225..d98f996949 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,29 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [4.1.7] - 2015-05-25 +### Added +- Check for database table presence in console commands. +- Category report +- Reinstated old test routines. + + +### Changed +- Confirm account setting is no longer in `.env` file. +- Titles are now in reverse (current page > parent > firefly iii) +- Easier update of language files thanks to Github implementation. +- Uniform colours for charts. + +### Fixed +- Made all pages more mobile friendly. +- Fixed #395 found by @marcoveeneman. +- Fixed #398 found by @marcoveeneman. +- Fixed #401 found by @marcoveeneman. +- Many optimizations. +- Updated many libraries. +- Various bugs found by myself. + + ## [4.1.6] - 2016-11-06 ### Added - New budget table for multi year report. diff --git a/config/firefly.php b/config/firefly.php index a8b27f2513..565cc37e9c 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -24,7 +24,7 @@ return [ 'must_confirm_account' => false, ], 'chart' => 'chartjs', - 'version' => '4.1.6', + 'version' => '4.1.7', 'csv_import_enabled' => true, 'maxUploadSize' => 5242880, 'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'], From cd6e37b9cb8b378cc659876f71b090ee1d84fdbb Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 19 Nov 2016 16:45:33 +0100 Subject: [PATCH 121/709] Update composer.lock file. [skip ci] --- composer.lock | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/composer.lock b/composer.lock index 675c12bb64..f627dd9f55 100644 --- a/composer.lock +++ b/composer.lock @@ -2720,16 +2720,16 @@ }, { "name": "twig/twig", - "version": "v1.28.0", + "version": "v1.28.1", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "60ae30368f7ac50a95de032f16c1e882b0f69813" + "reference": "fff80c4a7ae1d47a81dfec10c76cbcb939170b45" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/60ae30368f7ac50a95de032f16c1e882b0f69813", - "reference": "60ae30368f7ac50a95de032f16c1e882b0f69813", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/fff80c4a7ae1d47a81dfec10c76cbcb939170b45", + "reference": "fff80c4a7ae1d47a81dfec10c76cbcb939170b45", "shasum": "" }, "require": { @@ -2777,7 +2777,7 @@ "keywords": [ "templating" ], - "time": "2016-11-17 17:46:53" + "time": "2016-11-19 05:52:49" }, { "name": "vlucas/phpdotenv", @@ -3903,23 +3903,23 @@ }, { "name": "phpunit/phpunit-mock-objects", - "version": "3.4.0", + "version": "3.4.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "238d7a2723bce689c79eeac9c7d5e1d623bb9dc2" + "reference": "45026c8383187ad1dcb14fbfec77dced265b9cfc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/238d7a2723bce689c79eeac9c7d5e1d623bb9dc2", - "reference": "238d7a2723bce689c79eeac9c7d5e1d623bb9dc2", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/45026c8383187ad1dcb14fbfec77dced265b9cfc", + "reference": "45026c8383187ad1dcb14fbfec77dced265b9cfc", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.6 || ^7.0", "phpunit/php-text-template": "^1.2", - "sebastian/exporter": "^1.2" + "sebastian/exporter": "^1.2 || ^2.0" }, "conflict": { "phpunit/phpunit": "<5.4.0" @@ -3958,7 +3958,7 @@ "mock", "xunit" ], - "time": "2016-10-09 07:01:45" + "time": "2016-11-19 09:07:46" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -4007,22 +4007,22 @@ }, { "name": "sebastian/comparator", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "ce2bda23a56456f19e35d98241446b581f648c14" + "reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/ce2bda23a56456f19e35d98241446b581f648c14", - "reference": "ce2bda23a56456f19e35d98241446b581f648c14", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6a1ed12e8b2409076ab22e3897126211ff8b1f7f", + "reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f", "shasum": "" }, "require": { "php": ">=5.3.3", "sebastian/diff": "~1.2", - "sebastian/exporter": "~1.2" + "sebastian/exporter": "~1.2 || ~2.0" }, "require-dev": { "phpunit/phpunit": "~4.4" @@ -4067,7 +4067,7 @@ "compare", "equality" ], - "time": "2016-11-17 14:39:37" + "time": "2016-11-19 09:18:40" }, { "name": "sebastian/diff", From 5d901a7ecbb5bd1f31d26892c8aa7420b83f2109 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 19 Nov 2016 18:21:48 +0100 Subject: [PATCH 122/709] Remove local development file. [skip ci] --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index cf0adc5454..6b366227eb 100755 --- a/.gitignore +++ b/.gitignore @@ -11,5 +11,4 @@ result.html test-import.sh test-import-report.txt public/google*.html -_ide_helper_models.php .env.backup From 26190524f4a99722b548ec661bd285f1fd57051f Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 19 Nov 2016 20:30:30 +0100 Subject: [PATCH 123/709] Skeletons for test --- test.sh | 28 ++- .../Controllers/AccountControllerTest.php | 138 ++++++++++++ .../Admin/ConfigurationControllerTest.php | 55 +++++ .../Admin/DomainControllerTest.php | 67 ++++++ .../Controllers/Admin/HomeControllerTest.php | 43 ++++ .../Controllers/Admin/UserControllerTest.php | 67 ++++++ .../Controllers/AttachmentControllerTest.php | 102 +++++++++ .../Auth/ConfirmationControllerTest.php | 67 ++++++ .../Auth/ForgotPasswordControllerTest.php | 67 ++++++ .../Controllers/Auth/LoginControllerTest.php | 91 ++++++++ .../Auth/PasswordControllerTest.php | 91 ++++++++ .../Auth/RegisterControllerTest.php | 67 ++++++ .../Auth/ResetPasswordControllerTest.php | 79 +++++++ .../Auth/TwoFactorControllerTest.php | 67 ++++++ .../Controllers/BillControllerTest.php | 138 ++++++++++++ .../Controllers/BudgetControllerTest.php | 186 ++++++++++++++++ .../Controllers/CategoryControllerTest.php | 150 +++++++++++++ .../Chart/AccountControllerTest.php | 103 +++++++++ .../Controllers/Chart/BillControllerTest.php | 55 +++++ .../Chart/BudgetControllerTest.php | 79 +++++++ .../Chart/CategoryControllerTest.php | 79 +++++++ .../Chart/CategoryReportControllerTest.php | 91 ++++++++ .../Chart/PiggyBankControllerTest.php | 43 ++++ .../Chart/ReportControllerTest.php | 67 ++++++ .../acceptance/Controllers/ControllerTest.php | 114 ++++++++++ .../Controllers/CurrencyControllerTest.php | 126 +++++++++++ .../Controllers/ExportControllerTest.php | 78 +++++++ .../Controllers/HelpControllerTest.php | 42 ++++ .../Controllers/ImportControllerTest.php | 174 +++++++++++++++ .../Controllers/JsonControllerTest.php | 186 ++++++++++++++++ .../Controllers/NewUserControllerTest.php | 54 +++++ .../Controllers/PiggyBankControllerTest.php | 210 ++++++++++++++++++ .../Popup/ReportControllerTest.php | 43 ++++ .../Controllers/PreferencesControllerTest.php | 90 ++++++++ .../Controllers/ProfileControllerTest.php | 90 ++++++++ .../Report/AccountControllerTest.php | 43 ++++ .../Report/BalanceControllerTest.php | 43 ++++ .../Report/BudgetControllerTest.php | 55 +++++ .../Report/CategoryControllerTest.php | 43 ++++ .../Report/InOutControllerTest.php | 67 ++++++ .../Controllers/ReportControllerTest.php | 102 +++++++++ .../Controllers/RuleControllerTest.php | 174 +++++++++++++++ .../Controllers/RuleGroupControllerTest.php | 150 +++++++++++++ .../Controllers/SearchControllerTest.php | 42 ++++ .../Controllers/TagControllerTest.php | 138 ++++++++++++ .../Transaction/ConvertControllerTest.php | 55 +++++ .../Transaction/MassControllerTest.php | 79 +++++++ .../Transaction/SingleControllerTest.php | 103 +++++++++ .../Transaction/SplitControllerTest.php | 55 +++++ .../Controllers/TransactionControllerTest.php | 66 ++++++ 50 files changed, 4433 insertions(+), 9 deletions(-) create mode 100644 tests/acceptance/Controllers/AccountControllerTest.php create mode 100644 tests/acceptance/Controllers/Admin/ConfigurationControllerTest.php create mode 100644 tests/acceptance/Controllers/Admin/DomainControllerTest.php create mode 100644 tests/acceptance/Controllers/Admin/HomeControllerTest.php create mode 100644 tests/acceptance/Controllers/Admin/UserControllerTest.php create mode 100644 tests/acceptance/Controllers/AttachmentControllerTest.php create mode 100644 tests/acceptance/Controllers/Auth/ConfirmationControllerTest.php create mode 100644 tests/acceptance/Controllers/Auth/ForgotPasswordControllerTest.php create mode 100644 tests/acceptance/Controllers/Auth/LoginControllerTest.php create mode 100644 tests/acceptance/Controllers/Auth/PasswordControllerTest.php create mode 100644 tests/acceptance/Controllers/Auth/RegisterControllerTest.php create mode 100644 tests/acceptance/Controllers/Auth/ResetPasswordControllerTest.php create mode 100644 tests/acceptance/Controllers/Auth/TwoFactorControllerTest.php create mode 100644 tests/acceptance/Controllers/BillControllerTest.php create mode 100644 tests/acceptance/Controllers/BudgetControllerTest.php create mode 100644 tests/acceptance/Controllers/CategoryControllerTest.php create mode 100644 tests/acceptance/Controllers/Chart/AccountControllerTest.php create mode 100644 tests/acceptance/Controllers/Chart/BillControllerTest.php create mode 100644 tests/acceptance/Controllers/Chart/BudgetControllerTest.php create mode 100644 tests/acceptance/Controllers/Chart/CategoryControllerTest.php create mode 100644 tests/acceptance/Controllers/Chart/CategoryReportControllerTest.php create mode 100644 tests/acceptance/Controllers/Chart/PiggyBankControllerTest.php create mode 100644 tests/acceptance/Controllers/Chart/ReportControllerTest.php create mode 100644 tests/acceptance/Controllers/ControllerTest.php create mode 100644 tests/acceptance/Controllers/CurrencyControllerTest.php create mode 100644 tests/acceptance/Controllers/ExportControllerTest.php create mode 100644 tests/acceptance/Controllers/HelpControllerTest.php create mode 100644 tests/acceptance/Controllers/ImportControllerTest.php create mode 100644 tests/acceptance/Controllers/JsonControllerTest.php create mode 100644 tests/acceptance/Controllers/NewUserControllerTest.php create mode 100644 tests/acceptance/Controllers/PiggyBankControllerTest.php create mode 100644 tests/acceptance/Controllers/Popup/ReportControllerTest.php create mode 100644 tests/acceptance/Controllers/PreferencesControllerTest.php create mode 100644 tests/acceptance/Controllers/ProfileControllerTest.php create mode 100644 tests/acceptance/Controllers/Report/AccountControllerTest.php create mode 100644 tests/acceptance/Controllers/Report/BalanceControllerTest.php create mode 100644 tests/acceptance/Controllers/Report/BudgetControllerTest.php create mode 100644 tests/acceptance/Controllers/Report/CategoryControllerTest.php create mode 100644 tests/acceptance/Controllers/Report/InOutControllerTest.php create mode 100644 tests/acceptance/Controllers/ReportControllerTest.php create mode 100644 tests/acceptance/Controllers/RuleControllerTest.php create mode 100644 tests/acceptance/Controllers/RuleGroupControllerTest.php create mode 100644 tests/acceptance/Controllers/SearchControllerTest.php create mode 100644 tests/acceptance/Controllers/TagControllerTest.php create mode 100644 tests/acceptance/Controllers/Transaction/ConvertControllerTest.php create mode 100644 tests/acceptance/Controllers/Transaction/MassControllerTest.php create mode 100644 tests/acceptance/Controllers/Transaction/SingleControllerTest.php create mode 100644 tests/acceptance/Controllers/Transaction/SplitControllerTest.php create mode 100644 tests/acceptance/Controllers/TransactionControllerTest.php diff --git a/test.sh b/test.sh index 6190e186af..e9cb412c91 100755 --- a/test.sh +++ b/test.sh @@ -7,13 +7,15 @@ BACKUPENV=./.env.current TESTINGENV=./.env.testing # do something with flags: -rflag='' -tflag='' +resetestflag='' +testflag='' +coverageflag='' -while getopts 'rt' flag; do +while getopts 'crt' flag; do case "${flag}" in - r) rflag='true' ;; - t) tflag='true' ;; + r) resetestflag='true' ;; + t) testflag='true' ;; + c) coverageflag='true' ;; *) error "Unexpected option ${flag}" ;; esac done @@ -32,7 +34,7 @@ cp $TESTINGENV $ORIGINALENV php artisan cache:clear # reset database (optional) -if [[ $rflag == "true" ]] +if [[ $resetestflag == "true" ]] then echo "Must reset database" @@ -51,7 +53,7 @@ then fi # do not reset database (optional) -if [[ $rflag == "" ]] +if [[ $resetestflag == "" ]] then echo "Will not reset database" fi @@ -60,12 +62,20 @@ fi cp $DATABASECOPY $DATABASE # run PHPUnit -if [[ $tflag == "" ]] +if [[ $testflag == "" ]] then echo "Must not run PHPUnit" else echo "Must run PHPUnit" - phpunit + + if [[ $coverageflag == "" ]] + then + echo "Must run PHPUnit without coverage" + phpunit + else + echo "Must run PHPUnit with coverage" + phpunit --configuration phpunit.coverage.xml + fi fi # restore current config: diff --git a/tests/acceptance/Controllers/AccountControllerTest.php b/tests/acceptance/Controllers/AccountControllerTest.php new file mode 100644 index 0000000000..8dfbe9c1f8 --- /dev/null +++ b/tests/acceptance/Controllers/AccountControllerTest.php @@ -0,0 +1,138 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\AccountController::delete + * @todo Implement testDelete(). + */ + public function testDelete() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\AccountController::destroy + * @todo Implement testDestroy(). + */ + public function testDestroy() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\AccountController::edit + * @todo Implement testEdit(). + */ + public function testEdit() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\AccountController::index + * @todo Implement testIndex(). + */ + public function testIndex() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\AccountController::show + * @todo Implement testShow(). + */ + public function testShow() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\AccountController::showWithDate + * @todo Implement testShowWithDate(). + */ + public function testShowWithDate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\AccountController::store + * @todo Implement testStore(). + */ + public function testStore() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\AccountController::update + * @todo Implement testUpdate(). + */ + public function testUpdate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Admin/ConfigurationControllerTest.php b/tests/acceptance/Controllers/Admin/ConfigurationControllerTest.php new file mode 100644 index 0000000000..969d6a1f9f --- /dev/null +++ b/tests/acceptance/Controllers/Admin/ConfigurationControllerTest.php @@ -0,0 +1,55 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Admin\ConfigurationController::store + * @todo Implement testStore(). + */ + public function testStore() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Admin/DomainControllerTest.php b/tests/acceptance/Controllers/Admin/DomainControllerTest.php new file mode 100644 index 0000000000..4ab67c980d --- /dev/null +++ b/tests/acceptance/Controllers/Admin/DomainControllerTest.php @@ -0,0 +1,67 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Admin\DomainController::manual + * @todo Implement testManual(). + */ + public function testManual() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Admin\DomainController::toggleDomain + * @todo Implement testToggleDomain(). + */ + public function testToggleDomain() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Admin/HomeControllerTest.php b/tests/acceptance/Controllers/Admin/HomeControllerTest.php new file mode 100644 index 0000000000..d4adb3629d --- /dev/null +++ b/tests/acceptance/Controllers/Admin/HomeControllerTest.php @@ -0,0 +1,43 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Admin/UserControllerTest.php b/tests/acceptance/Controllers/Admin/UserControllerTest.php new file mode 100644 index 0000000000..1c5c8aa66b --- /dev/null +++ b/tests/acceptance/Controllers/Admin/UserControllerTest.php @@ -0,0 +1,67 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Admin\UserController::index + * @todo Implement testIndex(). + */ + public function testIndex() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Admin\UserController::show + * @todo Implement testShow(). + */ + public function testShow() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/AttachmentControllerTest.php b/tests/acceptance/Controllers/AttachmentControllerTest.php new file mode 100644 index 0000000000..a3aa8ba5c3 --- /dev/null +++ b/tests/acceptance/Controllers/AttachmentControllerTest.php @@ -0,0 +1,102 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\AttachmentController::destroy + * @todo Implement testDestroy(). + */ + public function testDestroy() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\AttachmentController::download + * @todo Implement testDownload(). + */ + public function testDownload() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\AttachmentController::edit + * @todo Implement testEdit(). + */ + public function testEdit() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\AttachmentController::preview + * @todo Implement testPreview(). + */ + public function testPreview() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\AttachmentController::update + * @todo Implement testUpdate(). + */ + public function testUpdate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Auth/ConfirmationControllerTest.php b/tests/acceptance/Controllers/Auth/ConfirmationControllerTest.php new file mode 100644 index 0000000000..bcbf659c50 --- /dev/null +++ b/tests/acceptance/Controllers/Auth/ConfirmationControllerTest.php @@ -0,0 +1,67 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\ConfirmationController::doConfirmation + * @todo Implement testDoConfirmation(). + */ + public function testDoConfirmation() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\ConfirmationController::resendConfirmation + * @todo Implement testResendConfirmation(). + */ + public function testResendConfirmation() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Auth/ForgotPasswordControllerTest.php b/tests/acceptance/Controllers/Auth/ForgotPasswordControllerTest.php new file mode 100644 index 0000000000..9229664a80 --- /dev/null +++ b/tests/acceptance/Controllers/Auth/ForgotPasswordControllerTest.php @@ -0,0 +1,67 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\ForgotPasswordController::sendResetLinkEmail + * @todo Implement testSendResetLinkEmail(). + */ + public function testSendResetLinkEmail() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\ForgotPasswordController::broker + * @todo Implement testBroker(). + */ + public function testBroker() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Auth/LoginControllerTest.php b/tests/acceptance/Controllers/Auth/LoginControllerTest.php new file mode 100644 index 0000000000..21a46f6015 --- /dev/null +++ b/tests/acceptance/Controllers/Auth/LoginControllerTest.php @@ -0,0 +1,91 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\LoginController::showLoginForm + * @todo Implement testShowLoginForm(). + */ + public function testShowLoginForm() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\LoginController::username + * @todo Implement testUsername(). + */ + public function testUsername() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\LoginController::logout + * @todo Implement testLogout(). + */ + public function testLogout() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\LoginController::redirectPath + * @todo Implement testRedirectPath(). + */ + public function testRedirectPath() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Auth/PasswordControllerTest.php b/tests/acceptance/Controllers/Auth/PasswordControllerTest.php new file mode 100644 index 0000000000..d2714b9fed --- /dev/null +++ b/tests/acceptance/Controllers/Auth/PasswordControllerTest.php @@ -0,0 +1,91 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\PasswordController::showResetForm + * @todo Implement testShowResetForm(). + */ + public function testShowResetForm() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\PasswordController::reset + * @todo Implement testReset(). + */ + public function testReset() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\PasswordController::broker + * @todo Implement testBroker(). + */ + public function testBroker() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\PasswordController::redirectPath + * @todo Implement testRedirectPath(). + */ + public function testRedirectPath() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Auth/RegisterControllerTest.php b/tests/acceptance/Controllers/Auth/RegisterControllerTest.php new file mode 100644 index 0000000000..47bd7b7b0a --- /dev/null +++ b/tests/acceptance/Controllers/Auth/RegisterControllerTest.php @@ -0,0 +1,67 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\RegisterController::showRegistrationForm + * @todo Implement testShowRegistrationForm(). + */ + public function testShowRegistrationForm() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\RegisterController::redirectPath + * @todo Implement testRedirectPath(). + */ + public function testRedirectPath() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Auth/ResetPasswordControllerTest.php b/tests/acceptance/Controllers/Auth/ResetPasswordControllerTest.php new file mode 100644 index 0000000000..33f20a0dde --- /dev/null +++ b/tests/acceptance/Controllers/Auth/ResetPasswordControllerTest.php @@ -0,0 +1,79 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\ResetPasswordController::reset + * @todo Implement testReset(). + */ + public function testReset() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\ResetPasswordController::broker + * @todo Implement testBroker(). + */ + public function testBroker() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\ResetPasswordController::redirectPath + * @todo Implement testRedirectPath(). + */ + public function testRedirectPath() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Auth/TwoFactorControllerTest.php b/tests/acceptance/Controllers/Auth/TwoFactorControllerTest.php new file mode 100644 index 0000000000..a26f0b5bf9 --- /dev/null +++ b/tests/acceptance/Controllers/Auth/TwoFactorControllerTest.php @@ -0,0 +1,67 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\TwoFactorController::lostTwoFactor + * @todo Implement testLostTwoFactor(). + */ + public function testLostTwoFactor() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\TwoFactorController::postIndex + * @todo Implement testPostIndex(). + */ + public function testPostIndex() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/BillControllerTest.php b/tests/acceptance/Controllers/BillControllerTest.php new file mode 100644 index 0000000000..4d45549939 --- /dev/null +++ b/tests/acceptance/Controllers/BillControllerTest.php @@ -0,0 +1,138 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\BillController::delete + * @todo Implement testDelete(). + */ + public function testDelete() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\BillController::destroy + * @todo Implement testDestroy(). + */ + public function testDestroy() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\BillController::edit + * @todo Implement testEdit(). + */ + public function testEdit() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\BillController::index + * @todo Implement testIndex(). + */ + public function testIndex() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\BillController::rescan + * @todo Implement testRescan(). + */ + public function testRescan() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\BillController::show + * @todo Implement testShow(). + */ + public function testShow() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\BillController::store + * @todo Implement testStore(). + */ + public function testStore() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\BillController::update + * @todo Implement testUpdate(). + */ + public function testUpdate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/BudgetControllerTest.php b/tests/acceptance/Controllers/BudgetControllerTest.php new file mode 100644 index 0000000000..9bcd562dcd --- /dev/null +++ b/tests/acceptance/Controllers/BudgetControllerTest.php @@ -0,0 +1,186 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\BudgetController::create + * @todo Implement testCreate(). + */ + public function testCreate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\BudgetController::delete + * @todo Implement testDelete(). + */ + public function testDelete() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\BudgetController::destroy + * @todo Implement testDestroy(). + */ + public function testDestroy() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\BudgetController::edit + * @todo Implement testEdit(). + */ + public function testEdit() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\BudgetController::index + * @todo Implement testIndex(). + */ + public function testIndex() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\BudgetController::noBudget + * @todo Implement testNoBudget(). + */ + public function testNoBudget() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\BudgetController::postUpdateIncome + * @todo Implement testPostUpdateIncome(). + */ + public function testPostUpdateIncome() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\BudgetController::show + * @todo Implement testShow(). + */ + public function testShow() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\BudgetController::showWithRepetition + * @todo Implement testShowWithRepetition(). + */ + public function testShowWithRepetition() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\BudgetController::store + * @todo Implement testStore(). + */ + public function testStore() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\BudgetController::update + * @todo Implement testUpdate(). + */ + public function testUpdate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\BudgetController::updateIncome + * @todo Implement testUpdateIncome(). + */ + public function testUpdateIncome() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/CategoryControllerTest.php b/tests/acceptance/Controllers/CategoryControllerTest.php new file mode 100644 index 0000000000..3720334941 --- /dev/null +++ b/tests/acceptance/Controllers/CategoryControllerTest.php @@ -0,0 +1,150 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\CategoryController::delete + * @todo Implement testDelete(). + */ + public function testDelete() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\CategoryController::destroy + * @todo Implement testDestroy(). + */ + public function testDestroy() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\CategoryController::edit + * @todo Implement testEdit(). + */ + public function testEdit() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\CategoryController::index + * @todo Implement testIndex(). + */ + public function testIndex() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\CategoryController::noCategory + * @todo Implement testNoCategory(). + */ + public function testNoCategory() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\CategoryController::show + * @todo Implement testShow(). + */ + public function testShow() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\CategoryController::showWithDate + * @todo Implement testShowWithDate(). + */ + public function testShowWithDate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\CategoryController::store + * @todo Implement testStore(). + */ + public function testStore() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\CategoryController::update + * @todo Implement testUpdate(). + */ + public function testUpdate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Chart/AccountControllerTest.php b/tests/acceptance/Controllers/Chart/AccountControllerTest.php new file mode 100644 index 0000000000..94b516b1c9 --- /dev/null +++ b/tests/acceptance/Controllers/Chart/AccountControllerTest.php @@ -0,0 +1,103 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Chart\AccountController::frontpage + * @todo Implement testFrontpage(). + */ + public function testFrontpage() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Chart\AccountController::report + * @todo Implement testReport(). + */ + public function testReport() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Chart\AccountController::revenueAccounts + * @todo Implement testRevenueAccounts(). + */ + public function testRevenueAccounts() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Chart\AccountController::single + * @todo Implement testSingle(). + */ + public function testSingle() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Chart\AccountController::specificPeriod + * @todo Implement testSpecificPeriod(). + */ + public function testSpecificPeriod() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Chart/BillControllerTest.php b/tests/acceptance/Controllers/Chart/BillControllerTest.php new file mode 100644 index 0000000000..c9ceba540e --- /dev/null +++ b/tests/acceptance/Controllers/Chart/BillControllerTest.php @@ -0,0 +1,55 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Chart\BillController::single + * @todo Implement testSingle(). + */ + public function testSingle() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Chart/BudgetControllerTest.php b/tests/acceptance/Controllers/Chart/BudgetControllerTest.php new file mode 100644 index 0000000000..a58fc719eb --- /dev/null +++ b/tests/acceptance/Controllers/Chart/BudgetControllerTest.php @@ -0,0 +1,79 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Chart\BudgetController::budgetLimit + * @todo Implement testBudgetLimit(). + */ + public function testBudgetLimit() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Chart\BudgetController::frontpage + * @todo Implement testFrontpage(). + */ + public function testFrontpage() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Chart\BudgetController::period + * @todo Implement testPeriod(). + */ + public function testPeriod() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Chart/CategoryControllerTest.php b/tests/acceptance/Controllers/Chart/CategoryControllerTest.php new file mode 100644 index 0000000000..a9f5708675 --- /dev/null +++ b/tests/acceptance/Controllers/Chart/CategoryControllerTest.php @@ -0,0 +1,79 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Chart\CategoryController::currentPeriod + * @todo Implement testCurrentPeriod(). + */ + public function testCurrentPeriod() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Chart\CategoryController::frontpage + * @todo Implement testFrontpage(). + */ + public function testFrontpage() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Chart\CategoryController::specificPeriod + * @todo Implement testSpecificPeriod(). + */ + public function testSpecificPeriod() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Chart/CategoryReportControllerTest.php b/tests/acceptance/Controllers/Chart/CategoryReportControllerTest.php new file mode 100644 index 0000000000..bb45625b17 --- /dev/null +++ b/tests/acceptance/Controllers/Chart/CategoryReportControllerTest.php @@ -0,0 +1,91 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Chart\CategoryReportController::accountIncome + * @todo Implement testAccountIncome(). + */ + public function testAccountIncome() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Chart\CategoryReportController::categoryExpense + * @todo Implement testCategoryExpense(). + */ + public function testCategoryExpense() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Chart\CategoryReportController::categoryIncome + * @todo Implement testCategoryIncome(). + */ + public function testCategoryIncome() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Chart\CategoryReportController::mainChart + * @todo Implement testMainChart(). + */ + public function testMainChart() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Chart/PiggyBankControllerTest.php b/tests/acceptance/Controllers/Chart/PiggyBankControllerTest.php new file mode 100644 index 0000000000..38d4a4472f --- /dev/null +++ b/tests/acceptance/Controllers/Chart/PiggyBankControllerTest.php @@ -0,0 +1,43 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Chart/ReportControllerTest.php b/tests/acceptance/Controllers/Chart/ReportControllerTest.php new file mode 100644 index 0000000000..93402f3d9d --- /dev/null +++ b/tests/acceptance/Controllers/Chart/ReportControllerTest.php @@ -0,0 +1,67 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Chart\ReportController::yearInOut + * @todo Implement testYearInOut(). + */ + public function testYearInOut() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Chart\ReportController::yearInOutSummarized + * @todo Implement testYearInOutSummarized(). + */ + public function testYearInOutSummarized() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/ControllerTest.php b/tests/acceptance/Controllers/ControllerTest.php new file mode 100644 index 0000000000..c323b6736d --- /dev/null +++ b/tests/acceptance/Controllers/ControllerTest.php @@ -0,0 +1,114 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Controller::authorizeForUser + * @todo Implement testAuthorizeForUser(). + */ + public function testAuthorizeForUser() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Controller::authorizeResource + * @todo Implement testAuthorizeResource(). + */ + public function testAuthorizeResource() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Controller::dispatchNow + * @todo Implement testDispatchNow(). + */ + public function testDispatchNow() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Controller::validateWith + * @todo Implement testValidateWith(). + */ + public function testValidateWith() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Controller::validate + * @todo Implement testValidate(). + */ + public function testValidate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Controller::validateWithBag + * @todo Implement testValidateWithBag(). + */ + public function testValidateWithBag() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/CurrencyControllerTest.php b/tests/acceptance/Controllers/CurrencyControllerTest.php new file mode 100644 index 0000000000..69a9e19ea0 --- /dev/null +++ b/tests/acceptance/Controllers/CurrencyControllerTest.php @@ -0,0 +1,126 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\CurrencyController::defaultCurrency + * @todo Implement testDefaultCurrency(). + */ + public function testDefaultCurrency() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\CurrencyController::delete + * @todo Implement testDelete(). + */ + public function testDelete() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\CurrencyController::destroy + * @todo Implement testDestroy(). + */ + public function testDestroy() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\CurrencyController::edit + * @todo Implement testEdit(). + */ + public function testEdit() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\CurrencyController::index + * @todo Implement testIndex(). + */ + public function testIndex() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\CurrencyController::store + * @todo Implement testStore(). + */ + public function testStore() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\CurrencyController::update + * @todo Implement testUpdate(). + */ + public function testUpdate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/ExportControllerTest.php b/tests/acceptance/Controllers/ExportControllerTest.php new file mode 100644 index 0000000000..12bb96391f --- /dev/null +++ b/tests/acceptance/Controllers/ExportControllerTest.php @@ -0,0 +1,78 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ExportController::getStatus + * @todo Implement testGetStatus(). + */ + public function testGetStatus() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ExportController::index + * @todo Implement testIndex(). + */ + public function testIndex() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ExportController::postIndex + * @todo Implement testPostIndex(). + */ + public function testPostIndex() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/HelpControllerTest.php b/tests/acceptance/Controllers/HelpControllerTest.php new file mode 100644 index 0000000000..b850723126 --- /dev/null +++ b/tests/acceptance/Controllers/HelpControllerTest.php @@ -0,0 +1,42 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/ImportControllerTest.php b/tests/acceptance/Controllers/ImportControllerTest.php new file mode 100644 index 0000000000..2c19894ec8 --- /dev/null +++ b/tests/acceptance/Controllers/ImportControllerTest.php @@ -0,0 +1,174 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ImportController::configure + * @todo Implement testConfigure(). + */ + public function testConfigure() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ImportController::download + * @todo Implement testDownload(). + */ + public function testDownload() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ImportController::finished + * @todo Implement testFinished(). + */ + public function testFinished() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ImportController::index + * @todo Implement testIndex(). + */ + public function testIndex() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ImportController::json + * @todo Implement testJson(). + */ + public function testJson() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ImportController::postConfigure + * @todo Implement testPostConfigure(). + */ + public function testPostConfigure() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ImportController::postSettings + * @todo Implement testPostSettings(). + */ + public function testPostSettings() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ImportController::settings + * @todo Implement testSettings(). + */ + public function testSettings() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ImportController::start + * @todo Implement testStart(). + */ + public function testStart() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ImportController::status + * @todo Implement testStatus(). + */ + public function testStatus() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ImportController::upload + * @todo Implement testUpload(). + */ + public function testUpload() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/JsonControllerTest.php b/tests/acceptance/Controllers/JsonControllerTest.php new file mode 100644 index 0000000000..dc628321d1 --- /dev/null +++ b/tests/acceptance/Controllers/JsonControllerTest.php @@ -0,0 +1,186 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\JsonController::boxBillsPaid + * @todo Implement testBoxBillsPaid(). + */ + public function testBoxBillsPaid() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\JsonController::boxBillsUnpaid + * @todo Implement testBoxBillsUnpaid(). + */ + public function testBoxBillsUnpaid() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\JsonController::boxIn + * @todo Implement testBoxIn(). + */ + public function testBoxIn() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\JsonController::boxOut + * @todo Implement testBoxOut(). + */ + public function testBoxOut() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\JsonController::categories + * @todo Implement testCategories(). + */ + public function testCategories() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\JsonController::endTour + * @todo Implement testEndTour(). + */ + public function testEndTour() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\JsonController::expenseAccounts + * @todo Implement testExpenseAccounts(). + */ + public function testExpenseAccounts() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\JsonController::revenueAccounts + * @todo Implement testRevenueAccounts(). + */ + public function testRevenueAccounts() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\JsonController::tags + * @todo Implement testTags(). + */ + public function testTags() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\JsonController::tour + * @todo Implement testTour(). + */ + public function testTour() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\JsonController::transactionJournals + * @todo Implement testTransactionJournals(). + */ + public function testTransactionJournals() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\JsonController::trigger + * @todo Implement testTrigger(). + */ + public function testTrigger() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/NewUserControllerTest.php b/tests/acceptance/Controllers/NewUserControllerTest.php new file mode 100644 index 0000000000..051ee41cbb --- /dev/null +++ b/tests/acceptance/Controllers/NewUserControllerTest.php @@ -0,0 +1,54 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\NewUserController::submit + * @todo Implement testSubmit(). + */ + public function testSubmit() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/PiggyBankControllerTest.php b/tests/acceptance/Controllers/PiggyBankControllerTest.php new file mode 100644 index 0000000000..8255486f97 --- /dev/null +++ b/tests/acceptance/Controllers/PiggyBankControllerTest.php @@ -0,0 +1,210 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\PiggyBankController::addMobile + * @todo Implement testAddMobile(). + */ + public function testAddMobile() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\PiggyBankController::create + * @todo Implement testCreate(). + */ + public function testCreate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\PiggyBankController::delete + * @todo Implement testDelete(). + */ + public function testDelete() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\PiggyBankController::destroy + * @todo Implement testDestroy(). + */ + public function testDestroy() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\PiggyBankController::edit + * @todo Implement testEdit(). + */ + public function testEdit() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\PiggyBankController::index + * @todo Implement testIndex(). + */ + public function testIndex() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\PiggyBankController::order + * @todo Implement testOrder(). + */ + public function testOrder() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\PiggyBankController::postAdd + * @todo Implement testPostAdd(). + */ + public function testPostAdd() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\PiggyBankController::postRemove + * @todo Implement testPostRemove(). + */ + public function testPostRemove() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\PiggyBankController::remove + * @todo Implement testRemove(). + */ + public function testRemove() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\PiggyBankController::removeMobile + * @todo Implement testRemoveMobile(). + */ + public function testRemoveMobile() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\PiggyBankController::show + * @todo Implement testShow(). + */ + public function testShow() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\PiggyBankController::store + * @todo Implement testStore(). + */ + public function testStore() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\PiggyBankController::update + * @todo Implement testUpdate(). + */ + public function testUpdate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Popup/ReportControllerTest.php b/tests/acceptance/Controllers/Popup/ReportControllerTest.php new file mode 100644 index 0000000000..35c673e0a3 --- /dev/null +++ b/tests/acceptance/Controllers/Popup/ReportControllerTest.php @@ -0,0 +1,43 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/PreferencesControllerTest.php b/tests/acceptance/Controllers/PreferencesControllerTest.php new file mode 100644 index 0000000000..25ea2d911c --- /dev/null +++ b/tests/acceptance/Controllers/PreferencesControllerTest.php @@ -0,0 +1,90 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\PreferencesController::deleteCode + * @todo Implement testDeleteCode(). + */ + public function testDeleteCode() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\PreferencesController::index + * @todo Implement testIndex(). + */ + public function testIndex() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\PreferencesController::postCode + * @todo Implement testPostCode(). + */ + public function testPostCode() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\PreferencesController::postIndex + * @todo Implement testPostIndex(). + */ + public function testPostIndex() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/ProfileControllerTest.php b/tests/acceptance/Controllers/ProfileControllerTest.php new file mode 100644 index 0000000000..d238bf741b --- /dev/null +++ b/tests/acceptance/Controllers/ProfileControllerTest.php @@ -0,0 +1,90 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ProfileController::deleteAccount + * @todo Implement testDeleteAccount(). + */ + public function testDeleteAccount() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ProfileController::index + * @todo Implement testIndex(). + */ + public function testIndex() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ProfileController::postChangePassword + * @todo Implement testPostChangePassword(). + */ + public function testPostChangePassword() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ProfileController::postDeleteAccount + * @todo Implement testPostDeleteAccount(). + */ + public function testPostDeleteAccount() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Report/AccountControllerTest.php b/tests/acceptance/Controllers/Report/AccountControllerTest.php new file mode 100644 index 0000000000..5d8f09e4af --- /dev/null +++ b/tests/acceptance/Controllers/Report/AccountControllerTest.php @@ -0,0 +1,43 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Report/BalanceControllerTest.php b/tests/acceptance/Controllers/Report/BalanceControllerTest.php new file mode 100644 index 0000000000..bff80f9e00 --- /dev/null +++ b/tests/acceptance/Controllers/Report/BalanceControllerTest.php @@ -0,0 +1,43 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Report/BudgetControllerTest.php b/tests/acceptance/Controllers/Report/BudgetControllerTest.php new file mode 100644 index 0000000000..6d596982a8 --- /dev/null +++ b/tests/acceptance/Controllers/Report/BudgetControllerTest.php @@ -0,0 +1,55 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Report\BudgetController::budgetReport + * @todo Implement testBudgetReport(). + */ + public function testBudgetReport() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Report/CategoryControllerTest.php b/tests/acceptance/Controllers/Report/CategoryControllerTest.php new file mode 100644 index 0000000000..4bb0759a95 --- /dev/null +++ b/tests/acceptance/Controllers/Report/CategoryControllerTest.php @@ -0,0 +1,43 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Report/InOutControllerTest.php b/tests/acceptance/Controllers/Report/InOutControllerTest.php new file mode 100644 index 0000000000..862d7f307a --- /dev/null +++ b/tests/acceptance/Controllers/Report/InOutControllerTest.php @@ -0,0 +1,67 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Report\InOutController::incExpReport + * @todo Implement testIncExpReport(). + */ + public function testIncExpReport() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Report\InOutController::incomeReport + * @todo Implement testIncomeReport(). + */ + public function testIncomeReport() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/ReportControllerTest.php b/tests/acceptance/Controllers/ReportControllerTest.php new file mode 100644 index 0000000000..ebd94ed67e --- /dev/null +++ b/tests/acceptance/Controllers/ReportControllerTest.php @@ -0,0 +1,102 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ReportController::categoryReport + * @todo Implement testCategoryReport(). + */ + public function testCategoryReport() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ReportController::defaultReport + * @todo Implement testDefaultReport(). + */ + public function testDefaultReport() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ReportController::index + * @todo Implement testIndex(). + */ + public function testIndex() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ReportController::options + * @todo Implement testOptions(). + */ + public function testOptions() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\ReportController::postIndex + * @todo Implement testPostIndex(). + */ + public function testPostIndex() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/RuleControllerTest.php b/tests/acceptance/Controllers/RuleControllerTest.php new file mode 100644 index 0000000000..990fb6d8ca --- /dev/null +++ b/tests/acceptance/Controllers/RuleControllerTest.php @@ -0,0 +1,174 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\RuleController::delete + * @todo Implement testDelete(). + */ + public function testDelete() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\RuleController::destroy + * @todo Implement testDestroy(). + */ + public function testDestroy() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\RuleController::down + * @todo Implement testDown(). + */ + public function testDown() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\RuleController::edit + * @todo Implement testEdit(). + */ + public function testEdit() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\RuleController::index + * @todo Implement testIndex(). + */ + public function testIndex() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\RuleController::reorderRuleActions + * @todo Implement testReorderRuleActions(). + */ + public function testReorderRuleActions() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\RuleController::reorderRuleTriggers + * @todo Implement testReorderRuleTriggers(). + */ + public function testReorderRuleTriggers() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\RuleController::store + * @todo Implement testStore(). + */ + public function testStore() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\RuleController::testTriggers + * @todo Implement testTestTriggers(). + */ + public function testTestTriggers() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\RuleController::up + * @todo Implement testUp(). + */ + public function testUp() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\RuleController::update + * @todo Implement testUpdate(). + */ + public function testUpdate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/RuleGroupControllerTest.php b/tests/acceptance/Controllers/RuleGroupControllerTest.php new file mode 100644 index 0000000000..a8d5063a26 --- /dev/null +++ b/tests/acceptance/Controllers/RuleGroupControllerTest.php @@ -0,0 +1,150 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\RuleGroupController::delete + * @todo Implement testDelete(). + */ + public function testDelete() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\RuleGroupController::destroy + * @todo Implement testDestroy(). + */ + public function testDestroy() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\RuleGroupController::down + * @todo Implement testDown(). + */ + public function testDown() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\RuleGroupController::edit + * @todo Implement testEdit(). + */ + public function testEdit() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\RuleGroupController::execute + * @todo Implement testExecute(). + */ + public function testExecute() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\RuleGroupController::selectTransactions + * @todo Implement testSelectTransactions(). + */ + public function testSelectTransactions() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\RuleGroupController::store + * @todo Implement testStore(). + */ + public function testStore() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\RuleGroupController::up + * @todo Implement testUp(). + */ + public function testUp() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\RuleGroupController::update + * @todo Implement testUpdate(). + */ + public function testUpdate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/SearchControllerTest.php b/tests/acceptance/Controllers/SearchControllerTest.php new file mode 100644 index 0000000000..d0a4948e8a --- /dev/null +++ b/tests/acceptance/Controllers/SearchControllerTest.php @@ -0,0 +1,42 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/TagControllerTest.php b/tests/acceptance/Controllers/TagControllerTest.php new file mode 100644 index 0000000000..91b4472267 --- /dev/null +++ b/tests/acceptance/Controllers/TagControllerTest.php @@ -0,0 +1,138 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\TagController::delete + * @todo Implement testDelete(). + */ + public function testDelete() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\TagController::destroy + * @todo Implement testDestroy(). + */ + public function testDestroy() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\TagController::edit + * @todo Implement testEdit(). + */ + public function testEdit() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\TagController::hideTagHelp + * @todo Implement testHideTagHelp(). + */ + public function testHideTagHelp() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\TagController::index + * @todo Implement testIndex(). + */ + public function testIndex() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\TagController::show + * @todo Implement testShow(). + */ + public function testShow() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\TagController::store + * @todo Implement testStore(). + */ + public function testStore() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\TagController::update + * @todo Implement testUpdate(). + */ + public function testUpdate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Transaction/ConvertControllerTest.php b/tests/acceptance/Controllers/Transaction/ConvertControllerTest.php new file mode 100644 index 0000000000..1ab037b16b --- /dev/null +++ b/tests/acceptance/Controllers/Transaction/ConvertControllerTest.php @@ -0,0 +1,55 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Transaction\ConvertController::submit + * @todo Implement testSubmit(). + */ + public function testSubmit() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Transaction/MassControllerTest.php b/tests/acceptance/Controllers/Transaction/MassControllerTest.php new file mode 100644 index 0000000000..aef64ec4d8 --- /dev/null +++ b/tests/acceptance/Controllers/Transaction/MassControllerTest.php @@ -0,0 +1,79 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Transaction\MassController::massDestroy + * @todo Implement testMassDestroy(). + */ + public function testMassDestroy() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Transaction\MassController::massEdit + * @todo Implement testMassEdit(). + */ + public function testMassEdit() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Transaction\MassController::massUpdate + * @todo Implement testMassUpdate(). + */ + public function testMassUpdate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Transaction/SingleControllerTest.php b/tests/acceptance/Controllers/Transaction/SingleControllerTest.php new file mode 100644 index 0000000000..d5bbe3831e --- /dev/null +++ b/tests/acceptance/Controllers/Transaction/SingleControllerTest.php @@ -0,0 +1,103 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Transaction\SingleController::delete + * @todo Implement testDelete(). + */ + public function testDelete() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Transaction\SingleController::destroy + * @todo Implement testDestroy(). + */ + public function testDestroy() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Transaction\SingleController::edit + * @todo Implement testEdit(). + */ + public function testEdit() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Transaction\SingleController::store + * @todo Implement testStore(). + */ + public function testStore() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Transaction\SingleController::update + * @todo Implement testUpdate(). + */ + public function testUpdate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/Transaction/SplitControllerTest.php b/tests/acceptance/Controllers/Transaction/SplitControllerTest.php new file mode 100644 index 0000000000..9045f772af --- /dev/null +++ b/tests/acceptance/Controllers/Transaction/SplitControllerTest.php @@ -0,0 +1,55 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Transaction\SplitController::update + * @todo Implement testUpdate(). + */ + public function testUpdate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} diff --git a/tests/acceptance/Controllers/TransactionControllerTest.php b/tests/acceptance/Controllers/TransactionControllerTest.php new file mode 100644 index 0000000000..20c347a934 --- /dev/null +++ b/tests/acceptance/Controllers/TransactionControllerTest.php @@ -0,0 +1,66 @@ +markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\TransactionController::reorder + * @todo Implement testReorder(). + */ + public function testReorder() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\TransactionController::show + * @todo Implement testShow(). + */ + public function testShow() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } +} From f8c5c1565564dac9a0d4864efdb6381326e70074 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 20 Nov 2016 07:24:18 +0100 Subject: [PATCH 124/709] Updated some tests. --- app/Http/Controllers/HomeController.php | 11 ++--- app/Support/Navigation.php | 22 ++++----- routes/web.php | 6 +-- .../Controllers/AccountControllerTest.php | 8 ++-- .../Admin/ConfigurationControllerTest.php | 8 ++-- .../Admin/DomainControllerTest.php | 8 ++-- .../Controllers/Admin/HomeControllerTest.php | 8 ++-- .../Controllers/Admin/UserControllerTest.php | 8 ++-- .../Controllers/AttachmentControllerTest.php | 8 ++-- .../Auth/ConfirmationControllerTest.php | 8 ++-- .../Auth/ForgotPasswordControllerTest.php | 8 ++-- .../Controllers/Auth/LoginControllerTest.php | 8 ++-- .../Auth/PasswordControllerTest.php | 8 ++-- .../Auth/RegisterControllerTest.php | 8 ++-- .../Auth/ResetPasswordControllerTest.php | 8 ++-- .../Auth/TwoFactorControllerTest.php | 8 ++-- .../Controllers/BillControllerTest.php | 8 ++-- .../Controllers/BudgetControllerTest.php | 8 ++-- .../Controllers/CategoryControllerTest.php | 8 ++-- .../Chart/AccountControllerTest.php | 8 ++-- .../Controllers/Chart/BillControllerTest.php | 8 ++-- .../Chart/BudgetControllerTest.php | 8 ++-- .../Chart/CategoryControllerTest.php | 8 ++-- .../Chart/CategoryReportControllerTest.php | 8 ++-- .../Chart/PiggyBankControllerTest.php | 8 ++-- .../Chart/ReportControllerTest.php | 8 ++-- .../acceptance/Controllers/ControllerTest.php | 8 ++-- .../Controllers/CurrencyControllerTest.php | 8 ++-- .../Controllers/ExportControllerTest.php | 8 ++-- .../Controllers/HelpControllerTest.php | 8 ++-- .../Controllers/HomeControllerTest.php | 45 +++++++++++++++++-- .../Controllers/ImportControllerTest.php | 8 ++-- .../Controllers/JsonControllerTest.php | 8 ++-- .../Controllers/NewUserControllerTest.php | 8 ++-- .../Controllers/PiggyBankControllerTest.php | 8 ++-- .../Popup/ReportControllerTest.php | 8 ++-- .../Controllers/PreferencesControllerTest.php | 8 ++-- .../Controllers/ProfileControllerTest.php | 8 ++-- .../Report/AccountControllerTest.php | 8 ++-- .../Report/BalanceControllerTest.php | 8 ++-- .../Report/BudgetControllerTest.php | 8 ++-- .../Report/CategoryControllerTest.php | 8 ++-- .../Report/InOutControllerTest.php | 8 ++-- .../Controllers/ReportControllerTest.php | 8 ++-- .../Controllers/RuleControllerTest.php | 8 ++-- .../Controllers/RuleGroupControllerTest.php | 8 ++-- .../Controllers/SearchControllerTest.php | 8 ++-- .../Controllers/TagControllerTest.php | 8 ++-- .../Transaction/ConvertControllerTest.php | 8 ++-- .../Transaction/MassControllerTest.php | 8 ++-- .../Transaction/SingleControllerTest.php | 8 ++-- .../Transaction/SplitControllerTest.php | 8 ++-- .../Controllers/TransactionControllerTest.php | 8 ++-- 53 files changed, 207 insertions(+), 269 deletions(-) diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 8bf0a6c6e6..a8109734e9 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -169,8 +169,7 @@ class HomeController extends Controller 'admin.users.domains.block-', 'import.json', 'help.', ]; $routes = Route::getRoutes(); - - echo '
';
+        $return = '
';
 
         /** @var \Illuminate\Routing\Route $route */
         foreach ($routes as $route) {
@@ -178,15 +177,13 @@ class HomeController extends Controller
             $methods = $route->getMethods();
 
             if (!is_null($name) && strlen($name) > 0 && in_array('GET', $methods) && !$this->startsWithAny($ignore, $name)) {
-                echo sprintf('touch %s.md', $name) . "\n";
+                $return .= sprintf('touch %s.md', $name) . "\n";
 
             }
         }
-        echo '
'; + $return .= '

'; - echo '
'; - - return ' '; + return $return; } /** diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php index 449ac4e6ae..ed24425fbf 100644 --- a/app/Support/Navigation.php +++ b/app/Support/Navigation.php @@ -365,11 +365,12 @@ class Navigation public function updateEndDate(string $range, Carbon $start): Carbon { $functionMap = [ - '1D' => 'endOfDay', - '1W' => 'endOfWeek', - '1M' => 'endOfMonth', - '3M' => 'lastOfQuarter', - '1Y' => 'endOfYear', + '1D' => 'endOfDay', + '1W' => 'endOfWeek', + '1M' => 'endOfMonth', + '3M' => 'lastOfQuarter', + '1Y' => 'endOfYear', + 'custom' => 'startOfMonth', // this only happens in test situations. ]; $end = clone $start; @@ -402,11 +403,12 @@ class Navigation public function updateStartDate(string $range, Carbon $start): Carbon { $functionMap = [ - '1D' => 'startOfDay', - '1W' => 'startOfWeek', - '1M' => 'startOfMonth', - '3M' => 'firstOfQuarter', - '1Y' => 'startOfYear', + '1D' => 'startOfDay', + '1W' => 'startOfWeek', + '1M' => 'startOfMonth', + '3M' => 'firstOfQuarter', + '1Y' => 'startOfYear', + 'custom' => 'startOfMonth', // this only happens in test situations. ]; if (isset($functionMap[$range])) { $function = $functionMap[$range]; diff --git a/routes/web.php b/routes/web.php index 37bae1859b..5fa77bcc3b 100755 --- a/routes/web.php +++ b/routes/web.php @@ -39,9 +39,9 @@ Route::group( */ Route::group( ['middleware' => 'user-simple-auth'], function () { - Route::get('/error', 'HomeController@displayError'); + Route::get('/error', ['uses' => 'HomeController@displayError', 'as' => 'displayError']); Route::any('logout', ['uses' => 'Auth\LoginController@logout', 'as' => 'logout']); - Route::get('/flush', ['uses' => 'HomeController@flush']); + Route::get('/flush', ['uses' => 'HomeController@flush', 'as' => 'flush']); } ); @@ -83,7 +83,7 @@ Route::group( Route::get('/flash', ['uses' => 'HomeController@testFlash', 'as' => 'testFlash']); Route::get('/home', ['uses' => 'HomeController@index', 'as' => 'home']); Route::post('/daterange', ['uses' => 'HomeController@dateRange', 'as' => 'daterange']); - Route::get('/routes', ['uses' => 'HomeController@routes']); + Route::get('/routes', ['uses' => 'HomeController@routes', 'as' => 'allRoutes']); /** * Account Controller */ diff --git a/tests/acceptance/Controllers/AccountControllerTest.php b/tests/acceptance/Controllers/AccountControllerTest.php index 8dfbe9c1f8..3a00136ada 100644 --- a/tests/acceptance/Controllers/AccountControllerTest.php +++ b/tests/acceptance/Controllers/AccountControllerTest.php @@ -2,14 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:38. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:26. */ class AccountControllerTest extends TestCase { - /** - * @var AccountController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Admin/ConfigurationControllerTest.php b/tests/acceptance/Controllers/Admin/ConfigurationControllerTest.php index 969d6a1f9f..a00b30beae 100644 --- a/tests/acceptance/Controllers/Admin/ConfigurationControllerTest.php +++ b/tests/acceptance/Controllers/Admin/ConfigurationControllerTest.php @@ -3,14 +3,12 @@ namespace Admin; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:38. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:26. */ class ConfigurationControllerTest extends TestCase { - /** - * @var ConfigurationController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Admin/DomainControllerTest.php b/tests/acceptance/Controllers/Admin/DomainControllerTest.php index 4ab67c980d..faee8052ae 100644 --- a/tests/acceptance/Controllers/Admin/DomainControllerTest.php +++ b/tests/acceptance/Controllers/Admin/DomainControllerTest.php @@ -3,14 +3,12 @@ namespace Admin; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:38. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:26. */ class DomainControllerTest extends TestCase { - /** - * @var DomainController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Admin/HomeControllerTest.php b/tests/acceptance/Controllers/Admin/HomeControllerTest.php index d4adb3629d..8582f47b9c 100644 --- a/tests/acceptance/Controllers/Admin/HomeControllerTest.php +++ b/tests/acceptance/Controllers/Admin/HomeControllerTest.php @@ -3,14 +3,12 @@ namespace Admin; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:38. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:26. */ class HomeControllerTest extends TestCase { - /** - * @var HomeController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Admin/UserControllerTest.php b/tests/acceptance/Controllers/Admin/UserControllerTest.php index 1c5c8aa66b..1bda62a3bc 100644 --- a/tests/acceptance/Controllers/Admin/UserControllerTest.php +++ b/tests/acceptance/Controllers/Admin/UserControllerTest.php @@ -3,14 +3,12 @@ namespace Admin; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:38. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:26. */ class UserControllerTest extends TestCase { - /** - * @var UserController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/AttachmentControllerTest.php b/tests/acceptance/Controllers/AttachmentControllerTest.php index a3aa8ba5c3..21e23768f4 100644 --- a/tests/acceptance/Controllers/AttachmentControllerTest.php +++ b/tests/acceptance/Controllers/AttachmentControllerTest.php @@ -2,14 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:38. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:26. */ class AttachmentControllerTest extends TestCase { - /** - * @var AttachmentController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Auth/ConfirmationControllerTest.php b/tests/acceptance/Controllers/Auth/ConfirmationControllerTest.php index bcbf659c50..ba14dab239 100644 --- a/tests/acceptance/Controllers/Auth/ConfirmationControllerTest.php +++ b/tests/acceptance/Controllers/Auth/ConfirmationControllerTest.php @@ -3,14 +3,12 @@ namespace Auth; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:38. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:26. */ class ConfirmationControllerTest extends TestCase { - /** - * @var ConfirmationController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Auth/ForgotPasswordControllerTest.php b/tests/acceptance/Controllers/Auth/ForgotPasswordControllerTest.php index 9229664a80..1081139c32 100644 --- a/tests/acceptance/Controllers/Auth/ForgotPasswordControllerTest.php +++ b/tests/acceptance/Controllers/Auth/ForgotPasswordControllerTest.php @@ -3,14 +3,12 @@ namespace Auth; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:38. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:26. */ class ForgotPasswordControllerTest extends TestCase { - /** - * @var ForgotPasswordController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Auth/LoginControllerTest.php b/tests/acceptance/Controllers/Auth/LoginControllerTest.php index 21a46f6015..9f536f4041 100644 --- a/tests/acceptance/Controllers/Auth/LoginControllerTest.php +++ b/tests/acceptance/Controllers/Auth/LoginControllerTest.php @@ -3,14 +3,12 @@ namespace Auth; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:39. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:27. */ class LoginControllerTest extends TestCase { - /** - * @var LoginController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Auth/PasswordControllerTest.php b/tests/acceptance/Controllers/Auth/PasswordControllerTest.php index d2714b9fed..09b82673f1 100644 --- a/tests/acceptance/Controllers/Auth/PasswordControllerTest.php +++ b/tests/acceptance/Controllers/Auth/PasswordControllerTest.php @@ -3,14 +3,12 @@ namespace Auth; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:39. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:27. */ class PasswordControllerTest extends TestCase { - /** - * @var PasswordController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Auth/RegisterControllerTest.php b/tests/acceptance/Controllers/Auth/RegisterControllerTest.php index 47bd7b7b0a..68b0b50054 100644 --- a/tests/acceptance/Controllers/Auth/RegisterControllerTest.php +++ b/tests/acceptance/Controllers/Auth/RegisterControllerTest.php @@ -3,14 +3,12 @@ namespace Auth; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:39. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:27. */ class RegisterControllerTest extends TestCase { - /** - * @var RegisterController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Auth/ResetPasswordControllerTest.php b/tests/acceptance/Controllers/Auth/ResetPasswordControllerTest.php index 33f20a0dde..003025cadc 100644 --- a/tests/acceptance/Controllers/Auth/ResetPasswordControllerTest.php +++ b/tests/acceptance/Controllers/Auth/ResetPasswordControllerTest.php @@ -3,14 +3,12 @@ namespace Auth; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:39. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:27. */ class ResetPasswordControllerTest extends TestCase { - /** - * @var ResetPasswordController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Auth/TwoFactorControllerTest.php b/tests/acceptance/Controllers/Auth/TwoFactorControllerTest.php index a26f0b5bf9..4f89d40917 100644 --- a/tests/acceptance/Controllers/Auth/TwoFactorControllerTest.php +++ b/tests/acceptance/Controllers/Auth/TwoFactorControllerTest.php @@ -3,14 +3,12 @@ namespace Auth; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:39. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:27. */ class TwoFactorControllerTest extends TestCase { - /** - * @var TwoFactorController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/BillControllerTest.php b/tests/acceptance/Controllers/BillControllerTest.php index 4d45549939..efe042f57d 100644 --- a/tests/acceptance/Controllers/BillControllerTest.php +++ b/tests/acceptance/Controllers/BillControllerTest.php @@ -2,14 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:39. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:27. */ class BillControllerTest extends TestCase { - /** - * @var BillController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/BudgetControllerTest.php b/tests/acceptance/Controllers/BudgetControllerTest.php index 9bcd562dcd..8323bf9aac 100644 --- a/tests/acceptance/Controllers/BudgetControllerTest.php +++ b/tests/acceptance/Controllers/BudgetControllerTest.php @@ -2,14 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:39. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:28. */ class BudgetControllerTest extends TestCase { - /** - * @var BudgetController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/CategoryControllerTest.php b/tests/acceptance/Controllers/CategoryControllerTest.php index 3720334941..2ed594653d 100644 --- a/tests/acceptance/Controllers/CategoryControllerTest.php +++ b/tests/acceptance/Controllers/CategoryControllerTest.php @@ -2,14 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:40. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:28. */ class CategoryControllerTest extends TestCase { - /** - * @var CategoryController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Chart/AccountControllerTest.php b/tests/acceptance/Controllers/Chart/AccountControllerTest.php index 94b516b1c9..c7d662f1f2 100644 --- a/tests/acceptance/Controllers/Chart/AccountControllerTest.php +++ b/tests/acceptance/Controllers/Chart/AccountControllerTest.php @@ -3,14 +3,12 @@ namespace Chart; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:40. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:28. */ class AccountControllerTest extends TestCase { - /** - * @var AccountController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Chart/BillControllerTest.php b/tests/acceptance/Controllers/Chart/BillControllerTest.php index c9ceba540e..a6545676c7 100644 --- a/tests/acceptance/Controllers/Chart/BillControllerTest.php +++ b/tests/acceptance/Controllers/Chart/BillControllerTest.php @@ -3,14 +3,12 @@ namespace Chart; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:40. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:28. */ class BillControllerTest extends TestCase { - /** - * @var BillController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Chart/BudgetControllerTest.php b/tests/acceptance/Controllers/Chart/BudgetControllerTest.php index a58fc719eb..36d3c25e55 100644 --- a/tests/acceptance/Controllers/Chart/BudgetControllerTest.php +++ b/tests/acceptance/Controllers/Chart/BudgetControllerTest.php @@ -3,14 +3,12 @@ namespace Chart; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:40. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:28. */ class BudgetControllerTest extends TestCase { - /** - * @var BudgetController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Chart/CategoryControllerTest.php b/tests/acceptance/Controllers/Chart/CategoryControllerTest.php index a9f5708675..769a528888 100644 --- a/tests/acceptance/Controllers/Chart/CategoryControllerTest.php +++ b/tests/acceptance/Controllers/Chart/CategoryControllerTest.php @@ -3,14 +3,12 @@ namespace Chart; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:40. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:28. */ class CategoryControllerTest extends TestCase { - /** - * @var CategoryController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Chart/CategoryReportControllerTest.php b/tests/acceptance/Controllers/Chart/CategoryReportControllerTest.php index bb45625b17..754c0442fb 100644 --- a/tests/acceptance/Controllers/Chart/CategoryReportControllerTest.php +++ b/tests/acceptance/Controllers/Chart/CategoryReportControllerTest.php @@ -3,14 +3,12 @@ namespace Chart; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:40. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:28. */ class CategoryReportControllerTest extends TestCase { - /** - * @var CategoryReportController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Chart/PiggyBankControllerTest.php b/tests/acceptance/Controllers/Chart/PiggyBankControllerTest.php index 38d4a4472f..aa33dbb5df 100644 --- a/tests/acceptance/Controllers/Chart/PiggyBankControllerTest.php +++ b/tests/acceptance/Controllers/Chart/PiggyBankControllerTest.php @@ -3,14 +3,12 @@ namespace Chart; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:41. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:28. */ class PiggyBankControllerTest extends TestCase { - /** - * @var PiggyBankController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Chart/ReportControllerTest.php b/tests/acceptance/Controllers/Chart/ReportControllerTest.php index 93402f3d9d..c354a6f40f 100644 --- a/tests/acceptance/Controllers/Chart/ReportControllerTest.php +++ b/tests/acceptance/Controllers/Chart/ReportControllerTest.php @@ -3,14 +3,12 @@ namespace Chart; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:41. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:29. */ class ReportControllerTest extends TestCase { - /** - * @var ReportController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/ControllerTest.php b/tests/acceptance/Controllers/ControllerTest.php index c323b6736d..53c3aebbc0 100644 --- a/tests/acceptance/Controllers/ControllerTest.php +++ b/tests/acceptance/Controllers/ControllerTest.php @@ -2,14 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:41. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:29. */ class ControllerTest extends TestCase { - /** - * @var Controller - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/CurrencyControllerTest.php b/tests/acceptance/Controllers/CurrencyControllerTest.php index 69a9e19ea0..bfdcad48ba 100644 --- a/tests/acceptance/Controllers/CurrencyControllerTest.php +++ b/tests/acceptance/Controllers/CurrencyControllerTest.php @@ -2,14 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:41. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:29. */ class CurrencyControllerTest extends TestCase { - /** - * @var CurrencyController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/ExportControllerTest.php b/tests/acceptance/Controllers/ExportControllerTest.php index 12bb96391f..3d0b152a95 100644 --- a/tests/acceptance/Controllers/ExportControllerTest.php +++ b/tests/acceptance/Controllers/ExportControllerTest.php @@ -2,14 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:41. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:29. */ class ExportControllerTest extends TestCase { - /** - * @var ExportController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/HelpControllerTest.php b/tests/acceptance/Controllers/HelpControllerTest.php index b850723126..e43f03b38c 100644 --- a/tests/acceptance/Controllers/HelpControllerTest.php +++ b/tests/acceptance/Controllers/HelpControllerTest.php @@ -2,14 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:41. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:29. */ class HelpControllerTest extends TestCase { - /** - * @var HelpController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/HomeControllerTest.php b/tests/acceptance/Controllers/HomeControllerTest.php index 7d4abdcdc8..75e109e2a2 100755 --- a/tests/acceptance/Controllers/HomeControllerTest.php +++ b/tests/acceptance/Controllers/HomeControllerTest.php @@ -30,19 +30,28 @@ class HomeControllerTest extends TestCase 'end' => '2012-04-01', ]; - // if date range is > 50, should have flash. - $this->call('POST', '/daterange', $args); + $this->call('POST', route('daterange'), $args); $this->assertResponseStatus(200); $this->assertSessionHas('warning', '91 days of data may take a while to load.'); } + /** + * @covers FireflyIII\Http\Controllers\HomeController::displayError + */ + public function testDisplayError() + { + $this->be($this->user()); + $this->call('GET', route('displayError')); + $this->assertResponseStatus(500); + } + /** * @covers FireflyIII\Http\Controllers\HomeController::flush */ public function testFlush() { $this->be($this->user()); - $this->call('GET', '/flush'); + $this->call('GET', route('flush')); $this->assertResponseStatus(302); } @@ -57,7 +66,35 @@ class HomeControllerTest extends TestCase { $this->be($this->user()); $this->changeDateRange($this->user(), $range); - $this->call('GET', '/'); + $this->call('GET', route('index')); $this->assertResponseStatus(200); } + + /** + * @covers FireflyIII\Http\Controllers\HomeController::routes + * @dataProvider dateRangeProvider + * + * @param string $range + */ + public function testRoutes(string $range) + { + $this->be($this->user()); + $this->changeDateRange($this->user(), $range); + $this->call('GET', route('allRoutes')); + $this->assertResponseStatus(200); + } + + /** + * @covers FireflyIII\Http\Controllers\HomeController::testFlash + */ + public function testTestFlash() + { + $this->be($this->user()); + $this->call('GET', route('testFlash')); + $this->assertResponseStatus(302); + $this->assertSessionHas('success'); + $this->assertSessionHas('info'); + $this->assertSessionHas('warning'); + $this->assertSessionHas('error'); + } } diff --git a/tests/acceptance/Controllers/ImportControllerTest.php b/tests/acceptance/Controllers/ImportControllerTest.php index 2c19894ec8..4cb4357466 100644 --- a/tests/acceptance/Controllers/ImportControllerTest.php +++ b/tests/acceptance/Controllers/ImportControllerTest.php @@ -2,14 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:41. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:29. */ class ImportControllerTest extends TestCase { - /** - * @var ImportController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/JsonControllerTest.php b/tests/acceptance/Controllers/JsonControllerTest.php index dc628321d1..1807dbaafe 100644 --- a/tests/acceptance/Controllers/JsonControllerTest.php +++ b/tests/acceptance/Controllers/JsonControllerTest.php @@ -2,14 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:42. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:29. */ class JsonControllerTest extends TestCase { - /** - * @var JsonController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/NewUserControllerTest.php b/tests/acceptance/Controllers/NewUserControllerTest.php index 051ee41cbb..1d9438465d 100644 --- a/tests/acceptance/Controllers/NewUserControllerTest.php +++ b/tests/acceptance/Controllers/NewUserControllerTest.php @@ -2,14 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:42. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:29. */ class NewUserControllerTest extends TestCase { - /** - * @var NewUserController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/PiggyBankControllerTest.php b/tests/acceptance/Controllers/PiggyBankControllerTest.php index 8255486f97..5b7e5922ea 100644 --- a/tests/acceptance/Controllers/PiggyBankControllerTest.php +++ b/tests/acceptance/Controllers/PiggyBankControllerTest.php @@ -2,14 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:42. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:30. */ class PiggyBankControllerTest extends TestCase { - /** - * @var PiggyBankController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Popup/ReportControllerTest.php b/tests/acceptance/Controllers/Popup/ReportControllerTest.php index 35c673e0a3..897e6f3604 100644 --- a/tests/acceptance/Controllers/Popup/ReportControllerTest.php +++ b/tests/acceptance/Controllers/Popup/ReportControllerTest.php @@ -3,14 +3,12 @@ namespace Popup; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:42. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:30. */ class ReportControllerTest extends TestCase { - /** - * @var ReportController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/PreferencesControllerTest.php b/tests/acceptance/Controllers/PreferencesControllerTest.php index 25ea2d911c..b74fee6265 100644 --- a/tests/acceptance/Controllers/PreferencesControllerTest.php +++ b/tests/acceptance/Controllers/PreferencesControllerTest.php @@ -2,14 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:42. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:31. */ class PreferencesControllerTest extends TestCase { - /** - * @var PreferencesController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/ProfileControllerTest.php b/tests/acceptance/Controllers/ProfileControllerTest.php index d238bf741b..d374aa9449 100644 --- a/tests/acceptance/Controllers/ProfileControllerTest.php +++ b/tests/acceptance/Controllers/ProfileControllerTest.php @@ -2,14 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:42. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:31. */ class ProfileControllerTest extends TestCase { - /** - * @var ProfileController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Report/AccountControllerTest.php b/tests/acceptance/Controllers/Report/AccountControllerTest.php index 5d8f09e4af..59e20540eb 100644 --- a/tests/acceptance/Controllers/Report/AccountControllerTest.php +++ b/tests/acceptance/Controllers/Report/AccountControllerTest.php @@ -3,14 +3,12 @@ namespace Report; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:42. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:31. */ class AccountControllerTest extends TestCase { - /** - * @var AccountController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Report/BalanceControllerTest.php b/tests/acceptance/Controllers/Report/BalanceControllerTest.php index bff80f9e00..0d4b2e3b60 100644 --- a/tests/acceptance/Controllers/Report/BalanceControllerTest.php +++ b/tests/acceptance/Controllers/Report/BalanceControllerTest.php @@ -3,14 +3,12 @@ namespace Report; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:43. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:32. */ class BalanceControllerTest extends TestCase { - /** - * @var BalanceController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Report/BudgetControllerTest.php b/tests/acceptance/Controllers/Report/BudgetControllerTest.php index 6d596982a8..940c0a8379 100644 --- a/tests/acceptance/Controllers/Report/BudgetControllerTest.php +++ b/tests/acceptance/Controllers/Report/BudgetControllerTest.php @@ -3,14 +3,12 @@ namespace Report; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:43. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:32. */ class BudgetControllerTest extends TestCase { - /** - * @var BudgetController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Report/CategoryControllerTest.php b/tests/acceptance/Controllers/Report/CategoryControllerTest.php index 4bb0759a95..b56c517109 100644 --- a/tests/acceptance/Controllers/Report/CategoryControllerTest.php +++ b/tests/acceptance/Controllers/Report/CategoryControllerTest.php @@ -3,14 +3,12 @@ namespace Report; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:43. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:32. */ class CategoryControllerTest extends TestCase { - /** - * @var CategoryController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Report/InOutControllerTest.php b/tests/acceptance/Controllers/Report/InOutControllerTest.php index 862d7f307a..2998f583c2 100644 --- a/tests/acceptance/Controllers/Report/InOutControllerTest.php +++ b/tests/acceptance/Controllers/Report/InOutControllerTest.php @@ -3,14 +3,12 @@ namespace Report; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:43. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:32. */ class InOutControllerTest extends TestCase { - /** - * @var InOutController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/ReportControllerTest.php b/tests/acceptance/Controllers/ReportControllerTest.php index ebd94ed67e..b0a60cade1 100644 --- a/tests/acceptance/Controllers/ReportControllerTest.php +++ b/tests/acceptance/Controllers/ReportControllerTest.php @@ -2,14 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:43. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:33. */ class ReportControllerTest extends TestCase { - /** - * @var ReportController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/RuleControllerTest.php b/tests/acceptance/Controllers/RuleControllerTest.php index 990fb6d8ca..98e2adfb9a 100644 --- a/tests/acceptance/Controllers/RuleControllerTest.php +++ b/tests/acceptance/Controllers/RuleControllerTest.php @@ -2,14 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:43. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:33. */ class RuleControllerTest extends TestCase { - /** - * @var RuleController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/RuleGroupControllerTest.php b/tests/acceptance/Controllers/RuleGroupControllerTest.php index a8d5063a26..f45dc3e1e8 100644 --- a/tests/acceptance/Controllers/RuleGroupControllerTest.php +++ b/tests/acceptance/Controllers/RuleGroupControllerTest.php @@ -2,14 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:43. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:33. */ class RuleGroupControllerTest extends TestCase { - /** - * @var RuleGroupController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/SearchControllerTest.php b/tests/acceptance/Controllers/SearchControllerTest.php index d0a4948e8a..61219feabd 100644 --- a/tests/acceptance/Controllers/SearchControllerTest.php +++ b/tests/acceptance/Controllers/SearchControllerTest.php @@ -2,14 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:43. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:33. */ class SearchControllerTest extends TestCase { - /** - * @var SearchController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/TagControllerTest.php b/tests/acceptance/Controllers/TagControllerTest.php index 91b4472267..24bad8e4e9 100644 --- a/tests/acceptance/Controllers/TagControllerTest.php +++ b/tests/acceptance/Controllers/TagControllerTest.php @@ -2,14 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:44. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:33. */ class TagControllerTest extends TestCase { - /** - * @var TagController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Transaction/ConvertControllerTest.php b/tests/acceptance/Controllers/Transaction/ConvertControllerTest.php index 1ab037b16b..8c05269b10 100644 --- a/tests/acceptance/Controllers/Transaction/ConvertControllerTest.php +++ b/tests/acceptance/Controllers/Transaction/ConvertControllerTest.php @@ -3,14 +3,12 @@ namespace Transaction; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:44. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:33. */ class ConvertControllerTest extends TestCase { - /** - * @var ConvertController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Transaction/MassControllerTest.php b/tests/acceptance/Controllers/Transaction/MassControllerTest.php index aef64ec4d8..6d1e24a08c 100644 --- a/tests/acceptance/Controllers/Transaction/MassControllerTest.php +++ b/tests/acceptance/Controllers/Transaction/MassControllerTest.php @@ -3,14 +3,12 @@ namespace Transaction; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:44. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:34. */ class MassControllerTest extends TestCase { - /** - * @var MassController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Transaction/SingleControllerTest.php b/tests/acceptance/Controllers/Transaction/SingleControllerTest.php index d5bbe3831e..a0ae1a7faa 100644 --- a/tests/acceptance/Controllers/Transaction/SingleControllerTest.php +++ b/tests/acceptance/Controllers/Transaction/SingleControllerTest.php @@ -3,14 +3,12 @@ namespace Transaction; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:44. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:34. */ class SingleControllerTest extends TestCase { - /** - * @var SingleController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/Transaction/SplitControllerTest.php b/tests/acceptance/Controllers/Transaction/SplitControllerTest.php index 9045f772af..7700848e0f 100644 --- a/tests/acceptance/Controllers/Transaction/SplitControllerTest.php +++ b/tests/acceptance/Controllers/Transaction/SplitControllerTest.php @@ -3,14 +3,12 @@ namespace Transaction; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:44. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:34. */ class SplitControllerTest extends TestCase { - /** - * @var SplitController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. diff --git a/tests/acceptance/Controllers/TransactionControllerTest.php b/tests/acceptance/Controllers/TransactionControllerTest.php index 20c347a934..5930944614 100644 --- a/tests/acceptance/Controllers/TransactionControllerTest.php +++ b/tests/acceptance/Controllers/TransactionControllerTest.php @@ -2,14 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-19 at 19:28:44. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:34. */ class TransactionControllerTest extends TestCase { - /** - * @var TransactionController - */ - protected $object; + + /** * Sets up the fixture, for example, opens a network connection. From 45e7a4576a09ea32e973fd887db1491d22b54e89 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 20 Nov 2016 08:30:25 +0100 Subject: [PATCH 125/709] Extend some test stuff. --- test.sh | 23 ++++++++++++++----- .../Controllers/AccountControllerTest.php | 12 ++++------ .../Admin/ConfigurationControllerTest.php | 16 ++++++------- .../Admin/DomainControllerTest.php | 4 ++-- .../Controllers/Admin/HomeControllerTest.php | 4 ++-- .../Controllers/Admin/UserControllerTest.php | 4 ++-- .../Controllers/AttachmentControllerTest.php | 4 ++-- .../Auth/ConfirmationControllerTest.php | 4 ++-- .../Auth/ForgotPasswordControllerTest.php | 4 ++-- .../Controllers/Auth/LoginControllerTest.php | 4 ++-- .../Auth/PasswordControllerTest.php | 4 ++-- .../Auth/RegisterControllerTest.php | 4 ++-- .../Auth/ResetPasswordControllerTest.php | 4 ++-- .../Auth/TwoFactorControllerTest.php | 4 ++-- .../Controllers/BillControllerTest.php | 4 ++-- .../Controllers/BudgetControllerTest.php | 4 ++-- .../Controllers/CategoryControllerTest.php | 4 ++-- .../Chart/AccountControllerTest.php | 4 ++-- .../Controllers/Chart/BillControllerTest.php | 4 ++-- .../Chart/BudgetControllerTest.php | 4 ++-- .../Chart/CategoryControllerTest.php | 4 ++-- .../Chart/CategoryReportControllerTest.php | 4 ++-- .../Chart/PiggyBankControllerTest.php | 4 ++-- .../Chart/ReportControllerTest.php | 4 ++-- .../acceptance/Controllers/ControllerTest.php | 4 ++-- .../Controllers/CurrencyControllerTest.php | 4 ++-- .../Controllers/ExportControllerTest.php | 4 ++-- .../Controllers/HelpControllerTest.php | 4 ++-- .../Controllers/ImportControllerTest.php | 4 ++-- .../Controllers/JsonControllerTest.php | 4 ++-- .../Controllers/NewUserControllerTest.php | 4 ++-- .../Controllers/PiggyBankControllerTest.php | 4 ++-- .../Popup/ReportControllerTest.php | 4 ++-- .../Controllers/PreferencesControllerTest.php | 4 ++-- .../Controllers/ProfileControllerTest.php | 4 ++-- .../Report/AccountControllerTest.php | 4 ++-- .../Report/BalanceControllerTest.php | 4 ++-- .../Report/BudgetControllerTest.php | 4 ++-- .../Report/CategoryControllerTest.php | 4 ++-- .../Report/InOutControllerTest.php | 4 ++-- .../Controllers/ReportControllerTest.php | 4 ++-- .../Controllers/RuleControllerTest.php | 4 ++-- .../Controllers/RuleGroupControllerTest.php | 4 ++-- .../Controllers/SearchControllerTest.php | 4 ++-- .../Controllers/TagControllerTest.php | 4 ++-- .../Transaction/ConvertControllerTest.php | 4 ++-- .../Transaction/MassControllerTest.php | 4 ++-- .../Transaction/SingleControllerTest.php | 4 ++-- .../Transaction/SplitControllerTest.php | 4 ++-- .../Controllers/TransactionControllerTest.php | 4 ++-- 50 files changed, 123 insertions(+), 116 deletions(-) diff --git a/test.sh b/test.sh index e9cb412c91..53e5424121 100755 --- a/test.sh +++ b/test.sh @@ -10,12 +10,23 @@ TESTINGENV=./.env.testing resetestflag='' testflag='' coverageflag='' +acceptancetestclass='' -while getopts 'crt' flag; do +while getopts 'crta:' flag; do case "${flag}" in - r) resetestflag='true' ;; - t) testflag='true' ;; - c) coverageflag='true' ;; + r) + resetestflag='true' + ;; + t) + testflag='true' + ;; + c) + coverageflag='true' + ;; + a) + acceptancetestclass=./tests/acceptance/$OPTARG + echo "Will only run acceptance test $OPTARG" + ;; *) error "Unexpected option ${flag}" ;; esac done @@ -71,10 +82,10 @@ else if [[ $coverageflag == "" ]] then echo "Must run PHPUnit without coverage" - phpunit + phpunit $acceptancetestclass else echo "Must run PHPUnit with coverage" - phpunit --configuration phpunit.coverage.xml + phpunit --configuration phpunit.coverage.xml $acceptancetestclass fi fi diff --git a/tests/acceptance/Controllers/AccountControllerTest.php b/tests/acceptance/Controllers/AccountControllerTest.php index 3a00136ada..6e862ea3a3 100644 --- a/tests/acceptance/Controllers/AccountControllerTest.php +++ b/tests/acceptance/Controllers/AccountControllerTest.php @@ -2,7 +2,7 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:26. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:07. */ class AccountControllerTest extends TestCase { @@ -15,7 +15,7 @@ class AccountControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** @@ -28,14 +28,12 @@ class AccountControllerTest extends TestCase /** * @covers FireflyIII\Http\Controllers\AccountController::create - * @todo Implement testCreate(). */ public function testCreate() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $this->call('GET', route('accounts.create', ['asset'])); + $this->assertResponseStatus(200); } /** diff --git a/tests/acceptance/Controllers/Admin/ConfigurationControllerTest.php b/tests/acceptance/Controllers/Admin/ConfigurationControllerTest.php index a00b30beae..738a2037f9 100644 --- a/tests/acceptance/Controllers/Admin/ConfigurationControllerTest.php +++ b/tests/acceptance/Controllers/Admin/ConfigurationControllerTest.php @@ -3,7 +3,7 @@ namespace Admin; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:26. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:07. */ class ConfigurationControllerTest extends TestCase { @@ -16,7 +16,7 @@ class ConfigurationControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** @@ -28,19 +28,17 @@ class ConfigurationControllerTest extends TestCase } /** - * @covers FireflyIII\Http\Controllers\Admin\ConfigurationController::index - * @todo Implement testIndex(). + * @covers \FireflyIII\Http\Controllers\Admin\ConfigurationController::index */ public function testIndex() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $this->call('GET', route('admin.configuration.index')); + $this->assertResponseStatus(200); } /** - * @covers FireflyIII\Http\Controllers\Admin\ConfigurationController::store + * @covers \FireflyIII\Http\Controllers\Admin\ConfigurationController::store * @todo Implement testStore(). */ public function testStore() diff --git a/tests/acceptance/Controllers/Admin/DomainControllerTest.php b/tests/acceptance/Controllers/Admin/DomainControllerTest.php index faee8052ae..470506a3cb 100644 --- a/tests/acceptance/Controllers/Admin/DomainControllerTest.php +++ b/tests/acceptance/Controllers/Admin/DomainControllerTest.php @@ -3,7 +3,7 @@ namespace Admin; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:26. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:07. */ class DomainControllerTest extends TestCase { @@ -16,7 +16,7 @@ class DomainControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Admin/HomeControllerTest.php b/tests/acceptance/Controllers/Admin/HomeControllerTest.php index 8582f47b9c..482b029c2e 100644 --- a/tests/acceptance/Controllers/Admin/HomeControllerTest.php +++ b/tests/acceptance/Controllers/Admin/HomeControllerTest.php @@ -3,7 +3,7 @@ namespace Admin; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:26. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:07. */ class HomeControllerTest extends TestCase { @@ -16,7 +16,7 @@ class HomeControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Admin/UserControllerTest.php b/tests/acceptance/Controllers/Admin/UserControllerTest.php index 1bda62a3bc..0ed1978b90 100644 --- a/tests/acceptance/Controllers/Admin/UserControllerTest.php +++ b/tests/acceptance/Controllers/Admin/UserControllerTest.php @@ -3,7 +3,7 @@ namespace Admin; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:26. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:08. */ class UserControllerTest extends TestCase { @@ -16,7 +16,7 @@ class UserControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/AttachmentControllerTest.php b/tests/acceptance/Controllers/AttachmentControllerTest.php index 21e23768f4..310e7589fb 100644 --- a/tests/acceptance/Controllers/AttachmentControllerTest.php +++ b/tests/acceptance/Controllers/AttachmentControllerTest.php @@ -2,7 +2,7 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:26. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:08. */ class AttachmentControllerTest extends TestCase { @@ -15,7 +15,7 @@ class AttachmentControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Auth/ConfirmationControllerTest.php b/tests/acceptance/Controllers/Auth/ConfirmationControllerTest.php index ba14dab239..3099e7213c 100644 --- a/tests/acceptance/Controllers/Auth/ConfirmationControllerTest.php +++ b/tests/acceptance/Controllers/Auth/ConfirmationControllerTest.php @@ -3,7 +3,7 @@ namespace Auth; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:26. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:08. */ class ConfirmationControllerTest extends TestCase { @@ -16,7 +16,7 @@ class ConfirmationControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Auth/ForgotPasswordControllerTest.php b/tests/acceptance/Controllers/Auth/ForgotPasswordControllerTest.php index 1081139c32..d7c1cc8d7b 100644 --- a/tests/acceptance/Controllers/Auth/ForgotPasswordControllerTest.php +++ b/tests/acceptance/Controllers/Auth/ForgotPasswordControllerTest.php @@ -3,7 +3,7 @@ namespace Auth; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:26. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:08. */ class ForgotPasswordControllerTest extends TestCase { @@ -16,7 +16,7 @@ class ForgotPasswordControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Auth/LoginControllerTest.php b/tests/acceptance/Controllers/Auth/LoginControllerTest.php index 9f536f4041..de1d3569eb 100644 --- a/tests/acceptance/Controllers/Auth/LoginControllerTest.php +++ b/tests/acceptance/Controllers/Auth/LoginControllerTest.php @@ -3,7 +3,7 @@ namespace Auth; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:27. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:08. */ class LoginControllerTest extends TestCase { @@ -16,7 +16,7 @@ class LoginControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Auth/PasswordControllerTest.php b/tests/acceptance/Controllers/Auth/PasswordControllerTest.php index 09b82673f1..0b04b65b09 100644 --- a/tests/acceptance/Controllers/Auth/PasswordControllerTest.php +++ b/tests/acceptance/Controllers/Auth/PasswordControllerTest.php @@ -3,7 +3,7 @@ namespace Auth; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:27. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:08. */ class PasswordControllerTest extends TestCase { @@ -16,7 +16,7 @@ class PasswordControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Auth/RegisterControllerTest.php b/tests/acceptance/Controllers/Auth/RegisterControllerTest.php index 68b0b50054..35f9ce9fad 100644 --- a/tests/acceptance/Controllers/Auth/RegisterControllerTest.php +++ b/tests/acceptance/Controllers/Auth/RegisterControllerTest.php @@ -3,7 +3,7 @@ namespace Auth; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:27. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:09. */ class RegisterControllerTest extends TestCase { @@ -16,7 +16,7 @@ class RegisterControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Auth/ResetPasswordControllerTest.php b/tests/acceptance/Controllers/Auth/ResetPasswordControllerTest.php index 003025cadc..d527bb562f 100644 --- a/tests/acceptance/Controllers/Auth/ResetPasswordControllerTest.php +++ b/tests/acceptance/Controllers/Auth/ResetPasswordControllerTest.php @@ -3,7 +3,7 @@ namespace Auth; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:27. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:09. */ class ResetPasswordControllerTest extends TestCase { @@ -16,7 +16,7 @@ class ResetPasswordControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Auth/TwoFactorControllerTest.php b/tests/acceptance/Controllers/Auth/TwoFactorControllerTest.php index 4f89d40917..3e6b032ceb 100644 --- a/tests/acceptance/Controllers/Auth/TwoFactorControllerTest.php +++ b/tests/acceptance/Controllers/Auth/TwoFactorControllerTest.php @@ -3,7 +3,7 @@ namespace Auth; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:27. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:09. */ class TwoFactorControllerTest extends TestCase { @@ -16,7 +16,7 @@ class TwoFactorControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/BillControllerTest.php b/tests/acceptance/Controllers/BillControllerTest.php index efe042f57d..70e33dcd35 100644 --- a/tests/acceptance/Controllers/BillControllerTest.php +++ b/tests/acceptance/Controllers/BillControllerTest.php @@ -2,7 +2,7 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:27. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:09. */ class BillControllerTest extends TestCase { @@ -15,7 +15,7 @@ class BillControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/BudgetControllerTest.php b/tests/acceptance/Controllers/BudgetControllerTest.php index 8323bf9aac..fce236f666 100644 --- a/tests/acceptance/Controllers/BudgetControllerTest.php +++ b/tests/acceptance/Controllers/BudgetControllerTest.php @@ -2,7 +2,7 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:28. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:09. */ class BudgetControllerTest extends TestCase { @@ -15,7 +15,7 @@ class BudgetControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/CategoryControllerTest.php b/tests/acceptance/Controllers/CategoryControllerTest.php index 2ed594653d..381ec52424 100644 --- a/tests/acceptance/Controllers/CategoryControllerTest.php +++ b/tests/acceptance/Controllers/CategoryControllerTest.php @@ -2,7 +2,7 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:28. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:09. */ class CategoryControllerTest extends TestCase { @@ -15,7 +15,7 @@ class CategoryControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Chart/AccountControllerTest.php b/tests/acceptance/Controllers/Chart/AccountControllerTest.php index c7d662f1f2..64316461e0 100644 --- a/tests/acceptance/Controllers/Chart/AccountControllerTest.php +++ b/tests/acceptance/Controllers/Chart/AccountControllerTest.php @@ -3,7 +3,7 @@ namespace Chart; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:28. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:09. */ class AccountControllerTest extends TestCase { @@ -16,7 +16,7 @@ class AccountControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Chart/BillControllerTest.php b/tests/acceptance/Controllers/Chart/BillControllerTest.php index a6545676c7..58b507cee8 100644 --- a/tests/acceptance/Controllers/Chart/BillControllerTest.php +++ b/tests/acceptance/Controllers/Chart/BillControllerTest.php @@ -3,7 +3,7 @@ namespace Chart; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:28. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:10. */ class BillControllerTest extends TestCase { @@ -16,7 +16,7 @@ class BillControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Chart/BudgetControllerTest.php b/tests/acceptance/Controllers/Chart/BudgetControllerTest.php index 36d3c25e55..2d7ce2e35f 100644 --- a/tests/acceptance/Controllers/Chart/BudgetControllerTest.php +++ b/tests/acceptance/Controllers/Chart/BudgetControllerTest.php @@ -3,7 +3,7 @@ namespace Chart; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:28. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:10. */ class BudgetControllerTest extends TestCase { @@ -16,7 +16,7 @@ class BudgetControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Chart/CategoryControllerTest.php b/tests/acceptance/Controllers/Chart/CategoryControllerTest.php index 769a528888..2b9d62b301 100644 --- a/tests/acceptance/Controllers/Chart/CategoryControllerTest.php +++ b/tests/acceptance/Controllers/Chart/CategoryControllerTest.php @@ -3,7 +3,7 @@ namespace Chart; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:28. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:10. */ class CategoryControllerTest extends TestCase { @@ -16,7 +16,7 @@ class CategoryControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Chart/CategoryReportControllerTest.php b/tests/acceptance/Controllers/Chart/CategoryReportControllerTest.php index 754c0442fb..f42bfe27e7 100644 --- a/tests/acceptance/Controllers/Chart/CategoryReportControllerTest.php +++ b/tests/acceptance/Controllers/Chart/CategoryReportControllerTest.php @@ -3,7 +3,7 @@ namespace Chart; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:28. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:10. */ class CategoryReportControllerTest extends TestCase { @@ -16,7 +16,7 @@ class CategoryReportControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Chart/PiggyBankControllerTest.php b/tests/acceptance/Controllers/Chart/PiggyBankControllerTest.php index aa33dbb5df..3ddb6f452d 100644 --- a/tests/acceptance/Controllers/Chart/PiggyBankControllerTest.php +++ b/tests/acceptance/Controllers/Chart/PiggyBankControllerTest.php @@ -3,7 +3,7 @@ namespace Chart; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:28. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:10. */ class PiggyBankControllerTest extends TestCase { @@ -16,7 +16,7 @@ class PiggyBankControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Chart/ReportControllerTest.php b/tests/acceptance/Controllers/Chart/ReportControllerTest.php index c354a6f40f..1cd08fdcd3 100644 --- a/tests/acceptance/Controllers/Chart/ReportControllerTest.php +++ b/tests/acceptance/Controllers/Chart/ReportControllerTest.php @@ -3,7 +3,7 @@ namespace Chart; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:29. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:10. */ class ReportControllerTest extends TestCase { @@ -16,7 +16,7 @@ class ReportControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/ControllerTest.php b/tests/acceptance/Controllers/ControllerTest.php index 53c3aebbc0..d664eda3e3 100644 --- a/tests/acceptance/Controllers/ControllerTest.php +++ b/tests/acceptance/Controllers/ControllerTest.php @@ -2,7 +2,7 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:29. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:10. */ class ControllerTest extends TestCase { @@ -15,7 +15,7 @@ class ControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/CurrencyControllerTest.php b/tests/acceptance/Controllers/CurrencyControllerTest.php index bfdcad48ba..b283a23f13 100644 --- a/tests/acceptance/Controllers/CurrencyControllerTest.php +++ b/tests/acceptance/Controllers/CurrencyControllerTest.php @@ -2,7 +2,7 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:29. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:11. */ class CurrencyControllerTest extends TestCase { @@ -15,7 +15,7 @@ class CurrencyControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/ExportControllerTest.php b/tests/acceptance/Controllers/ExportControllerTest.php index 3d0b152a95..9850eb4392 100644 --- a/tests/acceptance/Controllers/ExportControllerTest.php +++ b/tests/acceptance/Controllers/ExportControllerTest.php @@ -2,7 +2,7 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:29. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:11. */ class ExportControllerTest extends TestCase { @@ -15,7 +15,7 @@ class ExportControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/HelpControllerTest.php b/tests/acceptance/Controllers/HelpControllerTest.php index e43f03b38c..9b45e5fb48 100644 --- a/tests/acceptance/Controllers/HelpControllerTest.php +++ b/tests/acceptance/Controllers/HelpControllerTest.php @@ -2,7 +2,7 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:29. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:11. */ class HelpControllerTest extends TestCase { @@ -15,7 +15,7 @@ class HelpControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/ImportControllerTest.php b/tests/acceptance/Controllers/ImportControllerTest.php index 4cb4357466..ca995c45f6 100644 --- a/tests/acceptance/Controllers/ImportControllerTest.php +++ b/tests/acceptance/Controllers/ImportControllerTest.php @@ -2,7 +2,7 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:29. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:11. */ class ImportControllerTest extends TestCase { @@ -15,7 +15,7 @@ class ImportControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/JsonControllerTest.php b/tests/acceptance/Controllers/JsonControllerTest.php index 1807dbaafe..5bb9a3dc32 100644 --- a/tests/acceptance/Controllers/JsonControllerTest.php +++ b/tests/acceptance/Controllers/JsonControllerTest.php @@ -2,7 +2,7 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:29. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:12. */ class JsonControllerTest extends TestCase { @@ -15,7 +15,7 @@ class JsonControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/NewUserControllerTest.php b/tests/acceptance/Controllers/NewUserControllerTest.php index 1d9438465d..1a90642f51 100644 --- a/tests/acceptance/Controllers/NewUserControllerTest.php +++ b/tests/acceptance/Controllers/NewUserControllerTest.php @@ -2,7 +2,7 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:29. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:12. */ class NewUserControllerTest extends TestCase { @@ -15,7 +15,7 @@ class NewUserControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/PiggyBankControllerTest.php b/tests/acceptance/Controllers/PiggyBankControllerTest.php index 5b7e5922ea..e3ad2bf35f 100644 --- a/tests/acceptance/Controllers/PiggyBankControllerTest.php +++ b/tests/acceptance/Controllers/PiggyBankControllerTest.php @@ -2,7 +2,7 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:30. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:12. */ class PiggyBankControllerTest extends TestCase { @@ -15,7 +15,7 @@ class PiggyBankControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Popup/ReportControllerTest.php b/tests/acceptance/Controllers/Popup/ReportControllerTest.php index 897e6f3604..a240c9c7d1 100644 --- a/tests/acceptance/Controllers/Popup/ReportControllerTest.php +++ b/tests/acceptance/Controllers/Popup/ReportControllerTest.php @@ -3,7 +3,7 @@ namespace Popup; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:30. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:12. */ class ReportControllerTest extends TestCase { @@ -16,7 +16,7 @@ class ReportControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/PreferencesControllerTest.php b/tests/acceptance/Controllers/PreferencesControllerTest.php index b74fee6265..34d2ebe312 100644 --- a/tests/acceptance/Controllers/PreferencesControllerTest.php +++ b/tests/acceptance/Controllers/PreferencesControllerTest.php @@ -2,7 +2,7 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:31. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:12. */ class PreferencesControllerTest extends TestCase { @@ -15,7 +15,7 @@ class PreferencesControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/ProfileControllerTest.php b/tests/acceptance/Controllers/ProfileControllerTest.php index d374aa9449..1fa9bb283a 100644 --- a/tests/acceptance/Controllers/ProfileControllerTest.php +++ b/tests/acceptance/Controllers/ProfileControllerTest.php @@ -2,7 +2,7 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:31. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:12. */ class ProfileControllerTest extends TestCase { @@ -15,7 +15,7 @@ class ProfileControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Report/AccountControllerTest.php b/tests/acceptance/Controllers/Report/AccountControllerTest.php index 59e20540eb..d26ae55059 100644 --- a/tests/acceptance/Controllers/Report/AccountControllerTest.php +++ b/tests/acceptance/Controllers/Report/AccountControllerTest.php @@ -3,7 +3,7 @@ namespace Report; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:31. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:12. */ class AccountControllerTest extends TestCase { @@ -16,7 +16,7 @@ class AccountControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Report/BalanceControllerTest.php b/tests/acceptance/Controllers/Report/BalanceControllerTest.php index 0d4b2e3b60..12bf3f858f 100644 --- a/tests/acceptance/Controllers/Report/BalanceControllerTest.php +++ b/tests/acceptance/Controllers/Report/BalanceControllerTest.php @@ -3,7 +3,7 @@ namespace Report; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:32. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:12. */ class BalanceControllerTest extends TestCase { @@ -16,7 +16,7 @@ class BalanceControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Report/BudgetControllerTest.php b/tests/acceptance/Controllers/Report/BudgetControllerTest.php index 940c0a8379..7557cfe60f 100644 --- a/tests/acceptance/Controllers/Report/BudgetControllerTest.php +++ b/tests/acceptance/Controllers/Report/BudgetControllerTest.php @@ -3,7 +3,7 @@ namespace Report; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:32. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:13. */ class BudgetControllerTest extends TestCase { @@ -16,7 +16,7 @@ class BudgetControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Report/CategoryControllerTest.php b/tests/acceptance/Controllers/Report/CategoryControllerTest.php index b56c517109..e6065b0c08 100644 --- a/tests/acceptance/Controllers/Report/CategoryControllerTest.php +++ b/tests/acceptance/Controllers/Report/CategoryControllerTest.php @@ -3,7 +3,7 @@ namespace Report; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:32. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:13. */ class CategoryControllerTest extends TestCase { @@ -16,7 +16,7 @@ class CategoryControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Report/InOutControllerTest.php b/tests/acceptance/Controllers/Report/InOutControllerTest.php index 2998f583c2..33b3f03759 100644 --- a/tests/acceptance/Controllers/Report/InOutControllerTest.php +++ b/tests/acceptance/Controllers/Report/InOutControllerTest.php @@ -3,7 +3,7 @@ namespace Report; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:32. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:13. */ class InOutControllerTest extends TestCase { @@ -16,7 +16,7 @@ class InOutControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/ReportControllerTest.php b/tests/acceptance/Controllers/ReportControllerTest.php index b0a60cade1..5552a3b651 100644 --- a/tests/acceptance/Controllers/ReportControllerTest.php +++ b/tests/acceptance/Controllers/ReportControllerTest.php @@ -2,7 +2,7 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:33. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:13. */ class ReportControllerTest extends TestCase { @@ -15,7 +15,7 @@ class ReportControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/RuleControllerTest.php b/tests/acceptance/Controllers/RuleControllerTest.php index 98e2adfb9a..a0686f63d9 100644 --- a/tests/acceptance/Controllers/RuleControllerTest.php +++ b/tests/acceptance/Controllers/RuleControllerTest.php @@ -2,7 +2,7 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:33. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:13. */ class RuleControllerTest extends TestCase { @@ -15,7 +15,7 @@ class RuleControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/RuleGroupControllerTest.php b/tests/acceptance/Controllers/RuleGroupControllerTest.php index f45dc3e1e8..6303d9a227 100644 --- a/tests/acceptance/Controllers/RuleGroupControllerTest.php +++ b/tests/acceptance/Controllers/RuleGroupControllerTest.php @@ -2,7 +2,7 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:33. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:13. */ class RuleGroupControllerTest extends TestCase { @@ -15,7 +15,7 @@ class RuleGroupControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/SearchControllerTest.php b/tests/acceptance/Controllers/SearchControllerTest.php index 61219feabd..9caced22b9 100644 --- a/tests/acceptance/Controllers/SearchControllerTest.php +++ b/tests/acceptance/Controllers/SearchControllerTest.php @@ -2,7 +2,7 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:33. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:13. */ class SearchControllerTest extends TestCase { @@ -15,7 +15,7 @@ class SearchControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/TagControllerTest.php b/tests/acceptance/Controllers/TagControllerTest.php index 24bad8e4e9..428c5ef2e9 100644 --- a/tests/acceptance/Controllers/TagControllerTest.php +++ b/tests/acceptance/Controllers/TagControllerTest.php @@ -2,7 +2,7 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:33. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:13. */ class TagControllerTest extends TestCase { @@ -15,7 +15,7 @@ class TagControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Transaction/ConvertControllerTest.php b/tests/acceptance/Controllers/Transaction/ConvertControllerTest.php index 8c05269b10..5b8d8d6ff3 100644 --- a/tests/acceptance/Controllers/Transaction/ConvertControllerTest.php +++ b/tests/acceptance/Controllers/Transaction/ConvertControllerTest.php @@ -3,7 +3,7 @@ namespace Transaction; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:33. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:14. */ class ConvertControllerTest extends TestCase { @@ -16,7 +16,7 @@ class ConvertControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Transaction/MassControllerTest.php b/tests/acceptance/Controllers/Transaction/MassControllerTest.php index 6d1e24a08c..7f7b684ae5 100644 --- a/tests/acceptance/Controllers/Transaction/MassControllerTest.php +++ b/tests/acceptance/Controllers/Transaction/MassControllerTest.php @@ -3,7 +3,7 @@ namespace Transaction; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:34. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:14. */ class MassControllerTest extends TestCase { @@ -16,7 +16,7 @@ class MassControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Transaction/SingleControllerTest.php b/tests/acceptance/Controllers/Transaction/SingleControllerTest.php index a0ae1a7faa..9531e2a0c7 100644 --- a/tests/acceptance/Controllers/Transaction/SingleControllerTest.php +++ b/tests/acceptance/Controllers/Transaction/SingleControllerTest.php @@ -3,7 +3,7 @@ namespace Transaction; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:34. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:14. */ class SingleControllerTest extends TestCase { @@ -16,7 +16,7 @@ class SingleControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/Transaction/SplitControllerTest.php b/tests/acceptance/Controllers/Transaction/SplitControllerTest.php index 7700848e0f..69d94170ff 100644 --- a/tests/acceptance/Controllers/Transaction/SplitControllerTest.php +++ b/tests/acceptance/Controllers/Transaction/SplitControllerTest.php @@ -3,7 +3,7 @@ namespace Transaction; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:34. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:14. */ class SplitControllerTest extends TestCase { @@ -16,7 +16,7 @@ class SplitControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** diff --git a/tests/acceptance/Controllers/TransactionControllerTest.php b/tests/acceptance/Controllers/TransactionControllerTest.php index 5930944614..f5af20d09a 100644 --- a/tests/acceptance/Controllers/TransactionControllerTest.php +++ b/tests/acceptance/Controllers/TransactionControllerTest.php @@ -2,7 +2,7 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 06:10:34. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:14. */ class TransactionControllerTest extends TestCase { @@ -15,7 +15,7 @@ class TransactionControllerTest extends TestCase */ public function setUp() { - + parent::setUp(); } /** From 685310a36871739295fdc35a99f1503f3848bfdd Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 20 Nov 2016 08:46:02 +0100 Subject: [PATCH 126/709] First account controller tests --- app/Http/Controllers/AccountController.php | 4 +- tests/TestCase.php | 3 +- .../Controllers/AccountControllerTest.php | 84 ++++++++++--------- 3 files changed, 50 insertions(+), 41 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 750e90d53a..1273797ad6 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -26,6 +26,7 @@ use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; use Input; +use Log; use Navigation; use Preferences; use Session; @@ -248,6 +249,7 @@ class AccountController extends Controller if ($cache->has()) { $entries = $cache->get(); + Log::debug('Entries are cached, return cache.'); return view('accounts.show', compact('account', 'what', 'entries', 'subTitleIcon', 'journals', 'subTitle')); } @@ -257,7 +259,7 @@ class AccountController extends Controller if (in_array($account->accountType->type, [AccountType::ASSET, AccountType::DEFAULT])) { $assets = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]); } - + Log::debug('Going to get period expenses and incomes.'); while ($end >= $start) { $end = Navigation::startOfPeriod($end, $range); $currentEnd = Navigation::endOfPeriod($end, $range); diff --git a/tests/TestCase.php b/tests/TestCase.php index 79e8cbf40b..cdcd841f02 100755 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -103,9 +103,8 @@ abstract class TestCase extends Illuminate\Foundation\Testing\TestCase */ protected function mock($class) { + Log::debug(sprintf('Will now mock %s', $class)); $object = Mockery::mock($class); - - $this->app->instance($class, $object); return $object; diff --git a/tests/acceptance/Controllers/AccountControllerTest.php b/tests/acceptance/Controllers/AccountControllerTest.php index 6e862ea3a3..ed97ae26df 100644 --- a/tests/acceptance/Controllers/AccountControllerTest.php +++ b/tests/acceptance/Controllers/AccountControllerTest.php @@ -7,7 +7,6 @@ class AccountControllerTest extends TestCase { - /** * Sets up the fixture, for example, opens a network connection. @@ -18,14 +17,6 @@ class AccountControllerTest extends TestCase parent::setUp(); } - /** - * Tears down the fixture, for example, closes a network connection. - * This method is called after a test is executed. - */ - protected function tearDown() - { - } - /** * @covers FireflyIII\Http\Controllers\AccountController::create */ @@ -38,14 +29,12 @@ class AccountControllerTest extends TestCase /** * @covers FireflyIII\Http\Controllers\AccountController::delete - * @todo Implement testDelete(). */ public function testDelete() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $this->call('GET', route('accounts.delete', [1])); + $this->assertResponseStatus(200); } /** @@ -62,50 +51,61 @@ class AccountControllerTest extends TestCase /** * @covers FireflyIII\Http\Controllers\AccountController::edit - * @todo Implement testEdit(). */ public function testEdit() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $this->call('GET', route('accounts.edit', [1])); + $this->assertResponseStatus(200); } /** - * @covers FireflyIII\Http\Controllers\AccountController::index - * @todo Implement testIndex(). + * @covers FireflyIII\Http\Controllers\AccountController::index + * @dataProvider dateRangeProvider + * + * @param string $range */ - public function testIndex() + public function testIndex(string $range) { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $this->changeDateRange($this->user(), $range); + $this->call('GET', route('accounts.index', ['asset'])); + $this->assertResponseStatus(200); } /** * @covers FireflyIII\Http\Controllers\AccountController::show - * @todo Implement testShow(). + * @dataProvider dateRangeProvider + * + * @param string $range */ - public function testShow() + public function testShow(string $range) { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + + $this->be($this->user()); + $this->changeDateRange($this->user(), $range); + + $tasker = $this->mock(\FireflyIII\Repositories\Account\AccountTaskerInterface::class); + $tasker->shouldReceive('amountOutInPeriod')->withAnyArgs()->andReturn('-1'); + $tasker->shouldReceive('amountInInPeriod')->withAnyArgs()->andReturn('1'); + + + $this->call('GET', route('accounts.show', [1])); + $this->assertResponseStatus(200); } /** * @covers FireflyIII\Http\Controllers\AccountController::showWithDate - * @todo Implement testShowWithDate(). + * @dataProvider dateRangeProvider + * + * @param string $range */ - public function testShowWithDate() + public function testShowWithDate(string $range) { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); + $this->be($this->user()); + $this->changeDateRange($this->user(), $range); + $this->call('GET', route('accounts.show', [1, '2016-01-01'])); + $this->assertResponseStatus(200); } /** @@ -131,4 +131,12 @@ class AccountControllerTest extends TestCase 'This test has not been implemented yet.' ); } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } } From 8417f45d026764acb06902196571fe00424a7591 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 20 Nov 2016 08:54:52 +0100 Subject: [PATCH 127/709] Fixed some tests. --- .../acceptance/Controllers/AccountControllerTest.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tests/acceptance/Controllers/AccountControllerTest.php b/tests/acceptance/Controllers/AccountControllerTest.php index ed97ae26df..f790c2ae24 100644 --- a/tests/acceptance/Controllers/AccountControllerTest.php +++ b/tests/acceptance/Controllers/AccountControllerTest.php @@ -74,7 +74,7 @@ class AccountControllerTest extends TestCase } /** - * @covers FireflyIII\Http\Controllers\AccountController::show + * @covers FireflyIII\Http\Controllers\AccountController::show * @dataProvider dateRangeProvider * * @param string $range @@ -82,20 +82,18 @@ class AccountControllerTest extends TestCase public function testShow(string $range) { - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $tasker = $this->mock(\FireflyIII\Repositories\Account\AccountTaskerInterface::class); $tasker->shouldReceive('amountOutInPeriod')->withAnyArgs()->andReturn('-1'); $tasker->shouldReceive('amountInInPeriod')->withAnyArgs()->andReturn('1'); - + $this->be($this->user()); + $this->changeDateRange($this->user(), $range); $this->call('GET', route('accounts.show', [1])); $this->assertResponseStatus(200); } /** - * @covers FireflyIII\Http\Controllers\AccountController::showWithDate + * @covers FireflyIII\Http\Controllers\AccountController::showWithDate * @dataProvider dateRangeProvider * * @param string $range @@ -104,7 +102,7 @@ class AccountControllerTest extends TestCase { $this->be($this->user()); $this->changeDateRange($this->user(), $range); - $this->call('GET', route('accounts.show', [1, '2016-01-01'])); + $this->call('GET', route('accounts.show.date', [1, '2016-01-01'])); $this->assertResponseStatus(200); } From e1e94a788c42587f074e6446c853e74fad6b38c0 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 20 Nov 2016 08:57:48 +0100 Subject: [PATCH 128/709] Register and use interface. --- app/Http/Controllers/HomeController.php | 4 ++-- app/Providers/JournalServiceProvider.php | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index a8109734e9..be1fceae02 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -15,7 +15,7 @@ namespace FireflyIII\Http\Controllers; use Artisan; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Helpers\Collector\JournalCollector; +use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Models\AccountType; use FireflyIII\Models\Tag; use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; @@ -143,7 +143,7 @@ class HomeController extends Controller $showDepositsFrontpage = Preferences::get('showDepositsFrontpage', false)->data; foreach ($accounts as $account) { - $collector = new JournalCollector(auth()->user()); + $collector = app(JournalCollectorInterface::class); $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit(10)->setPage(1); $set = $collector->getJournals(); diff --git a/app/Providers/JournalServiceProvider.php b/app/Providers/JournalServiceProvider.php index 5353941000..8b61fe4bf2 100644 --- a/app/Providers/JournalServiceProvider.php +++ b/app/Providers/JournalServiceProvider.php @@ -44,6 +44,23 @@ class JournalServiceProvider extends ServiceProvider { $this->registerRepository(); $this->registerTasker(); + $this->registerCollector(); + } + + private function registerCollector() { + $this->app->bind( + 'FireflyIII\Helpers\Collector\JournalCollectorInterface', + function (Application $app, array $arguments) { + if (!isset($arguments[0]) && $app->auth->check()) { + return app('FireflyIII\Helpers\Collector\JournalCollector', [auth()->user()]); + } + if (!isset($arguments[0]) && !$app->auth->check()) { + throw new FireflyException('There is no user present.'); + } + + return app('FireflyIII\Helpers\Collector\JournalCollector', $arguments); + } + ); } private function registerRepository() From 75a524c656d534eeb1e44106d03365772f533293 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 20 Nov 2016 11:40:05 +0100 Subject: [PATCH 129/709] Added debug code for a possible import issue. --- app/Import/Setup/CsvSetup.php | 12 +++++++++--- app/Import/Specifics/RabobankDescription.php | 8 +++++--- app/Models/ImportJob.php | 2 ++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/Import/Setup/CsvSetup.php b/app/Import/Setup/CsvSetup.php index cefeebe275..782dba86dd 100644 --- a/app/Import/Setup/CsvSetup.php +++ b/app/Import/Setup/CsvSetup.php @@ -112,14 +112,16 @@ class CsvSetup implements SetupInterface */ public function getDataForSettings(): array { - + Log::debug('Now in getDataForSettings()'); if ($this->doColumnRoles()) { + Log::debug('doColumnRoles() is true.'); $data = $this->getDataForColumnRoles(); return $data; } if ($this->doColumnMapping()) { + Log::debug('doColumnMapping() is true.'); $data = $this->getDataForColumnMapping(); return $data; @@ -427,10 +429,13 @@ class CsvSetup implements SetupInterface } /** + * This method collects the data that will enable a user to choose column content. + * * @return array */ private function getDataForColumnRoles():array { + Log::debug('Now in getDataForColumnRoles()'); $config = $this->job->configuration; $data = [ 'columns' => [], @@ -447,15 +452,16 @@ class CsvSetup implements SetupInterface $end = $start + config('csv.example_rows'); // collect example data in $data['columns'] + Log::debug(sprintf('While %s is smaller than %d', $start, $end)); while ($start < $end) { $row = $reader->fetchOne($start); - + Log::debug(sprintf('Row %d has %d columns', $start, count($row))); // run specifics here: // and this is the point where the specifix go to work. foreach ($config['specifics'] as $name => $enabled) { /** @var SpecificInterface $specific */ $specific = app('FireflyIII\Import\Specifics\\' . $name); - + Log::debug(sprintf('Will now apply specific "%s" to row %d.', $name, $start)); // it returns the row, possibly modified: $row = $specific->run($row); } diff --git a/app/Import/Specifics/RabobankDescription.php b/app/Import/Specifics/RabobankDescription.php index a3988f2a61..59ff2abb53 100644 --- a/app/Import/Specifics/RabobankDescription.php +++ b/app/Import/Specifics/RabobankDescription.php @@ -45,9 +45,11 @@ class RabobankDescription implements SpecificInterface */ public function run(array $row): array { - $oppositeAccount = trim($row[5]); - $oppositeName = trim($row[6]); - $alternateName = trim($row[10]); + Log::debug(sprintf('Now in RabobankSpecific::run(). Row has %d columns', count($row))); + $oppositeAccount = isset($row[5]) ? trim($row[5]) : ''; + $oppositeName = isset($row[6]) ? trim($row[6]) : ''; + $alternateName = isset($row[10]) ? trim($row[10]) : ''; + if (strlen($oppositeAccount) < 1 && strlen($oppositeName) < 1) { Log::debug( sprintf( diff --git a/app/Models/ImportJob.php b/app/Models/ImportJob.php index 131fc8a1b0..01094638e4 100644 --- a/app/Models/ImportJob.php +++ b/app/Models/ImportJob.php @@ -15,6 +15,7 @@ namespace FireflyIII\Models; use Crypt; use Illuminate\Database\Eloquent\Model; +use Log; use Storage; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -151,6 +152,7 @@ class ImportJob extends Model $disk = Storage::disk('upload'); $encryptedContent = $disk->get($fileName); $content = Crypt::decrypt($encryptedContent); + Log::debug(sprintf('Content size is %d bytes.', $content)); return $content; } From 94875adb6c5b29b7e5d4ac5744201139547c3c53 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 20 Nov 2016 11:43:19 +0100 Subject: [PATCH 130/709] Various code cleanup. --- .../Budget/ChartJsBudgetChartGenerator.php | 1 - .../CategoryChartGeneratorInterface.php | 28 ++++---- .../Report/Standard/MonthReportGenerator.php | 20 +++--- .../Collector/JournalCollectorInterface.php | 10 +-- app/Helpers/Report/BudgetReportHelper.php | 2 - app/Http/Controllers/CategoryController.php | 4 +- .../Controllers/Chart/BudgetController.php | 2 +- .../Chart/CategoryReportController.php | 9 +-- .../Controllers/Chart/ReportController.php | 6 +- app/Import/Converter/TagSplit.php | 6 +- app/Providers/JournalServiceProvider.php | 3 +- app/Support/Search/Search.php | 6 +- .../Admin/ConfigurationControllerTest.php | 18 ++--- .../Admin/DomainControllerTest.php | 18 ++--- .../Controllers/Admin/HomeControllerTest.php | 18 ++--- .../Controllers/Admin/UserControllerTest.php | 18 ++--- .../Controllers/AttachmentControllerTest.php | 17 +++-- .../Auth/ConfirmationControllerTest.php | 18 ++--- .../Auth/ForgotPasswordControllerTest.php | 30 ++++---- .../Controllers/Auth/LoginControllerTest.php | 56 +++++++-------- .../Auth/PasswordControllerTest.php | 70 +++++++++---------- .../Auth/RegisterControllerTest.php | 22 +++--- .../Auth/ResetPasswordControllerTest.php | 66 ++++++++--------- .../Auth/TwoFactorControllerTest.php | 18 ++--- .../Controllers/BillControllerTest.php | 17 +++-- .../Controllers/BudgetControllerTest.php | 17 +++-- .../Controllers/CategoryControllerTest.php | 17 +++-- .../Chart/AccountControllerTest.php | 18 ++--- .../Controllers/Chart/BillControllerTest.php | 18 ++--- .../Chart/BudgetControllerTest.php | 18 ++--- .../Chart/CategoryControllerTest.php | 18 ++--- .../Chart/CategoryReportControllerTest.php | 18 ++--- .../Chart/PiggyBankControllerTest.php | 18 ++--- .../Chart/ReportControllerTest.php | 18 ++--- .../acceptance/Controllers/ControllerTest.php | 29 ++++---- .../Controllers/CurrencyControllerTest.php | 17 +++-- .../Controllers/ExportControllerTest.php | 17 +++-- .../Controllers/HelpControllerTest.php | 17 +++-- .../Controllers/ImportControllerTest.php | 17 +++-- .../Controllers/JsonControllerTest.php | 17 +++-- .../Controllers/NewUserControllerTest.php | 17 +++-- .../Controllers/PiggyBankControllerTest.php | 17 +++-- .../Popup/ReportControllerTest.php | 18 ++--- .../Controllers/PreferencesControllerTest.php | 17 +++-- .../Controllers/ProfileControllerTest.php | 17 +++-- .../Report/AccountControllerTest.php | 18 ++--- .../Report/BalanceControllerTest.php | 18 ++--- .../Report/BudgetControllerTest.php | 18 ++--- .../Report/CategoryControllerTest.php | 18 ++--- .../Report/InOutControllerTest.php | 18 ++--- .../Controllers/ReportControllerTest.php | 17 +++-- .../Controllers/RuleControllerTest.php | 17 +++-- .../Controllers/RuleGroupControllerTest.php | 17 +++-- .../Controllers/SearchControllerTest.php | 17 +++-- .../Controllers/TagControllerTest.php | 17 +++-- .../Transaction/ConvertControllerTest.php | 18 ++--- .../Transaction/MassControllerTest.php | 18 ++--- .../Transaction/SingleControllerTest.php | 18 ++--- .../Transaction/SplitControllerTest.php | 18 ++--- .../Controllers/TransactionControllerTest.php | 17 +++-- 60 files changed, 544 insertions(+), 563 deletions(-) diff --git a/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php b/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php index 6af797b8a3..f9c8d88f59 100644 --- a/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php +++ b/app/Generator/Chart/Budget/ChartJsBudgetChartGenerator.php @@ -14,7 +14,6 @@ namespace FireflyIII\Generator\Chart\Budget; use Illuminate\Support\Collection; -use Navigation; /** * Class ChartJsBudgetChartGenerator diff --git a/app/Generator/Chart/Category/CategoryChartGeneratorInterface.php b/app/Generator/Chart/Category/CategoryChartGeneratorInterface.php index 2b95ba820b..d5950bbaeb 100644 --- a/app/Generator/Chart/Category/CategoryChartGeneratorInterface.php +++ b/app/Generator/Chart/Category/CategoryChartGeneratorInterface.php @@ -23,13 +23,6 @@ use Illuminate\Support\Collection; interface CategoryChartGeneratorInterface { - /** - * @param array $data - * - * @return array - */ - public function pieChart(array $data): array; - /** * @param Collection $entries * @@ -37,13 +30,6 @@ interface CategoryChartGeneratorInterface */ public function all(Collection $entries): array; - /** - * @param array $entries - * - * @return array - */ - public function mainReportChart(array $entries): array; - /** * @param Collection $categories * @param Collection $entries @@ -59,6 +45,13 @@ interface CategoryChartGeneratorInterface */ public function frontpage(Collection $entries): array; + /** + * @param array $entries + * + * @return array + */ + public function mainReportChart(array $entries): array; + /** * @param Collection $entries * @@ -66,6 +59,13 @@ interface CategoryChartGeneratorInterface */ public function period(Collection $entries): array; + /** + * @param array $data + * + * @return array + */ + public function pieChart(array $data): array; + /** * @param Collection $categories * @param Collection $entries diff --git a/app/Generator/Report/Standard/MonthReportGenerator.php b/app/Generator/Report/Standard/MonthReportGenerator.php index fc02233e9c..5bb42eee37 100644 --- a/app/Generator/Report/Standard/MonthReportGenerator.php +++ b/app/Generator/Report/Standard/MonthReportGenerator.php @@ -64,6 +64,16 @@ class MonthReportGenerator implements ReportGeneratorInterface return $this; } + /** + * @param Collection $categories + * + * @return ReportGeneratorInterface + */ + public function setCategories(Collection $categories): ReportGeneratorInterface + { + return $this; + } + /** * @param Carbon $date * @@ -87,14 +97,4 @@ class MonthReportGenerator implements ReportGeneratorInterface return $this; } - - /** - * @param Collection $categories - * - * @return ReportGeneratorInterface - */ - public function setCategories(Collection $categories): ReportGeneratorInterface - { - return $this; - } } \ No newline at end of file diff --git a/app/Helpers/Collector/JournalCollectorInterface.php b/app/Helpers/Collector/JournalCollectorInterface.php index da72c8f809..e857bdaadc 100644 --- a/app/Helpers/Collector/JournalCollectorInterface.php +++ b/app/Helpers/Collector/JournalCollectorInterface.php @@ -43,11 +43,6 @@ interface JournalCollectorInterface */ public function getJournals(): Collection; - /** - * @return JournalCollectorInterface - */ - public function withOpposingAccount(): JournalCollectorInterface; - /** * @return LengthAwarePaginator */ @@ -136,6 +131,11 @@ interface JournalCollectorInterface */ public function setTypes(array $types): JournalCollectorInterface; + /** + * @return JournalCollectorInterface + */ + public function withOpposingAccount(): JournalCollectorInterface; + /** * @return JournalCollectorInterface */ diff --git a/app/Helpers/Report/BudgetReportHelper.php b/app/Helpers/Report/BudgetReportHelper.php index 675c4e2046..c8d417d3b8 100644 --- a/app/Helpers/Report/BudgetReportHelper.php +++ b/app/Helpers/Report/BudgetReportHelper.php @@ -19,11 +19,9 @@ use FireflyIII\Helpers\Collection\Budget as BudgetCollection; use FireflyIII\Helpers\Collection\BudgetLine; use FireflyIII\Models\Budget; use FireflyIII\Models\LimitRepetition; -use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use Illuminate\Support\Collection; use Navigation; -use stdClass; /** * Class BudgetReportHelper diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index 7068130116..e1834cd7c1 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -184,7 +184,7 @@ class CategoryController extends Controller $end = session('end', Navigation::endOfPeriod(new Carbon, $range)); $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); $hideCategory = true; // used in list. - $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); + $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $subTitle = $category->name; $subTitleIcon = 'fa-bar-chart'; @@ -253,7 +253,7 @@ class CategoryController extends Controller $end = Navigation::endOfPeriod($carbon, $range); $subTitle = $category->name; $hideCategory = true; // used in list. - $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); + $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); // new collector: diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index e337126f71..4ceb55d805 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -252,7 +252,7 @@ class BudgetController extends Controller // join them: $result = []; foreach (array_keys($periods) as $period) { - $nice = $periods[$period]; + $nice = $periods[$period]; $result[$nice] = [ 'spent' => isset($entries[$period]) ? $entries[$period] : '0', 'budgeted' => isset($entries[$period]) ? $budgeted[$period] : 0, diff --git a/app/Http/Controllers/Chart/CategoryReportController.php b/app/Http/Controllers/Chart/CategoryReportController.php index 6c292609a9..35b221d76b 100644 --- a/app/Http/Controllers/Chart/CategoryReportController.php +++ b/app/Http/Controllers/Chart/CategoryReportController.php @@ -299,16 +299,17 @@ class CategoryReportController extends Controller /** @var Category $category */ foreach ($categories as $category) { // get sum, and get label: - $categoryId = $category->id; - $data[$label]['name'][$categoryId] = $category->name; - $data[$label]['in'][$categoryId] = $income[$categoryId] ?? '0'; - $data[$label]['out'][$categoryId] = $expenses[$categoryId] ?? '0'; + $categoryId = $category->id; + $data[$label]['name'][$categoryId] = $category->name; + $data[$label]['in'][$categoryId] = $income[$categoryId] ?? '0'; + $data[$label]['out'][$categoryId] = $expenses[$categoryId] ?? '0'; } $current = Navigation::addPeriod($current, $period, 0); } $data = $this->generator->mainReportChart($data); + return Response::json($data); } diff --git a/app/Http/Controllers/Chart/ReportController.php b/app/Http/Controllers/Chart/ReportController.php index ca02e01e37..50ed37c990 100644 --- a/app/Http/Controllers/Chart/ReportController.php +++ b/app/Http/Controllers/Chart/ReportController.php @@ -128,9 +128,9 @@ class ReportController extends Controller } /** - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * * @return \Illuminate\Http\JsonResponse * @internal param AccountRepositoryInterface $repository diff --git a/app/Import/Converter/TagSplit.php b/app/Import/Converter/TagSplit.php index ab1cdf21be..53d3fc1d1c 100644 --- a/app/Import/Converter/TagSplit.php +++ b/app/Import/Converter/TagSplit.php @@ -33,8 +33,9 @@ class TagSplit * * @return Collection */ - public static function createSetFromSplits(User $user, array $mapping, array $parts): Collection { - $set = new Collection; + public static function createSetFromSplits(User $user, array $mapping, array $parts): Collection + { + $set = new Collection; Log::debug('Exploded parts.', $parts); /** @var TagRepositoryInterface $repository */ @@ -77,6 +78,7 @@ class TagSplit $set->push($tag); } } + return $set; } diff --git a/app/Providers/JournalServiceProvider.php b/app/Providers/JournalServiceProvider.php index 8b61fe4bf2..2ac7f61f64 100644 --- a/app/Providers/JournalServiceProvider.php +++ b/app/Providers/JournalServiceProvider.php @@ -47,7 +47,8 @@ class JournalServiceProvider extends ServiceProvider $this->registerCollector(); } - private function registerCollector() { + private function registerCollector() + { $this->app->bind( 'FireflyIII\Helpers\Collector\JournalCollectorInterface', function (Application $app, array $arguments) { diff --git a/app/Support/Search/Search.php b/app/Support/Search/Search.php index c5e1da96c7..a3da570829 100644 --- a/app/Support/Search/Search.php +++ b/app/Support/Search/Search.php @@ -109,7 +109,7 @@ class Search implements SearchInterface { $categories = $this->user->categories()->get(); /** @var Collection $result */ - $result = $categories->filter( + $result = $categories->filter( function (Category $category) use ($words) { if ($this->strpos_arr(strtolower($category->name), $words)) { return $category; @@ -118,7 +118,7 @@ class Search implements SearchInterface return false; } ); - $result = $result->slice(0, $this->limit); + $result = $result->slice(0, $this->limit); return $result; } @@ -131,7 +131,7 @@ class Search implements SearchInterface */ public function searchTags(array $words): Collection { - $tags = $this->user->tags()->get(); + $tags = $this->user->tags()->get(); /** @var Collection $result */ $result = $tags->filter( diff --git a/tests/acceptance/Controllers/Admin/ConfigurationControllerTest.php b/tests/acceptance/Controllers/Admin/ConfigurationControllerTest.php index 738a2037f9..7c23cc443b 100644 --- a/tests/acceptance/Controllers/Admin/ConfigurationControllerTest.php +++ b/tests/acceptance/Controllers/Admin/ConfigurationControllerTest.php @@ -1,5 +1,6 @@ markTestIncomplete( @@ -52,14 +44,22 @@ class ForgotPasswordControllerTest extends TestCase } /** - * @covers FireflyIII\Http\Controllers\Auth\ForgotPasswordController::broker - * @todo Implement testBroker(). + * @covers FireflyIII\Http\Controllers\Auth\ForgotPasswordController::showLinkRequestForm + * @todo Implement testShowLinkRequestForm(). */ - public function testBroker() + public function testShowLinkRequestForm() { // Remove the following lines when you implement this test. $this->markTestIncomplete( 'This test has not been implemented yet.' ); } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } } diff --git a/tests/acceptance/Controllers/Auth/LoginControllerTest.php b/tests/acceptance/Controllers/Auth/LoginControllerTest.php index de1d3569eb..2df971be1d 100644 --- a/tests/acceptance/Controllers/Auth/LoginControllerTest.php +++ b/tests/acceptance/Controllers/Auth/LoginControllerTest.php @@ -1,5 +1,6 @@ markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\LoginController::redirectPath + * @todo Implement testRedirectPath(). + */ + public function testRedirectPath() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + /** * @covers FireflyIII\Http\Controllers\Auth\LoginController::showLoginForm * @todo Implement testShowLoginForm(). @@ -64,26 +80,10 @@ class LoginControllerTest extends TestCase } /** - * @covers FireflyIII\Http\Controllers\Auth\LoginController::logout - * @todo Implement testLogout(). + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. */ - public function testLogout() + protected function tearDown() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } - - /** - * @covers FireflyIII\Http\Controllers\Auth\LoginController::redirectPath - * @todo Implement testRedirectPath(). - */ - public function testRedirectPath() - { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); } } diff --git a/tests/acceptance/Controllers/Auth/PasswordControllerTest.php b/tests/acceptance/Controllers/Auth/PasswordControllerTest.php index 0b04b65b09..02d793ddce 100644 --- a/tests/acceptance/Controllers/Auth/PasswordControllerTest.php +++ b/tests/acceptance/Controllers/Auth/PasswordControllerTest.php @@ -1,5 +1,6 @@ markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\PasswordController::redirectPath + * @todo Implement testRedirectPath(). + */ + public function testRedirectPath() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\PasswordController::reset + * @todo Implement testReset(). + */ + public function testReset() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); } /** @@ -52,38 +80,10 @@ class PasswordControllerTest extends TestCase } /** - * @covers FireflyIII\Http\Controllers\Auth\PasswordController::reset - * @todo Implement testReset(). + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. */ - public function testReset() + protected function tearDown() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } - - /** - * @covers FireflyIII\Http\Controllers\Auth\PasswordController::broker - * @todo Implement testBroker(). - */ - public function testBroker() - { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } - - /** - * @covers FireflyIII\Http\Controllers\Auth\PasswordController::redirectPath - * @todo Implement testRedirectPath(). - */ - public function testRedirectPath() - { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); } } diff --git a/tests/acceptance/Controllers/Auth/RegisterControllerTest.php b/tests/acceptance/Controllers/Auth/RegisterControllerTest.php index 35f9ce9fad..48de774930 100644 --- a/tests/acceptance/Controllers/Auth/RegisterControllerTest.php +++ b/tests/acceptance/Controllers/Auth/RegisterControllerTest.php @@ -1,5 +1,6 @@ markTestIncomplete( + 'This test has not been implemented yet.' + ); } /** @@ -52,14 +56,10 @@ class RegisterControllerTest extends TestCase } /** - * @covers FireflyIII\Http\Controllers\Auth\RegisterController::redirectPath - * @todo Implement testRedirectPath(). + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. */ - public function testRedirectPath() + protected function tearDown() { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); } } diff --git a/tests/acceptance/Controllers/Auth/ResetPasswordControllerTest.php b/tests/acceptance/Controllers/Auth/ResetPasswordControllerTest.php index d527bb562f..779b28614f 100644 --- a/tests/acceptance/Controllers/Auth/ResetPasswordControllerTest.php +++ b/tests/acceptance/Controllers/Auth/ResetPasswordControllerTest.php @@ -1,5 +1,6 @@ markTestIncomplete( - 'This test has not been implemented yet.' - ); - } - - /** - * @covers FireflyIII\Http\Controllers\Auth\ResetPasswordController::reset - * @todo Implement testReset(). - */ - public function testReset() - { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } - /** * @covers FireflyIII\Http\Controllers\Auth\ResetPasswordController::broker * @todo Implement testBroker(). @@ -74,4 +42,36 @@ class ResetPasswordControllerTest extends TestCase 'This test has not been implemented yet.' ); } + + /** + * @covers FireflyIII\Http\Controllers\Auth\ResetPasswordController::reset + * @todo Implement testReset(). + */ + public function testReset() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Auth\ResetPasswordController::showResetForm + * @todo Implement testShowResetForm(). + */ + public function testShowResetForm() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } } diff --git a/tests/acceptance/Controllers/Auth/TwoFactorControllerTest.php b/tests/acceptance/Controllers/Auth/TwoFactorControllerTest.php index 3e6b032ceb..cbf020c16d 100644 --- a/tests/acceptance/Controllers/Auth/TwoFactorControllerTest.php +++ b/tests/acceptance/Controllers/Auth/TwoFactorControllerTest.php @@ -1,5 +1,6 @@ markTestIncomplete( @@ -87,10 +78,10 @@ class ControllerTest extends TestCase } /** - * @covers FireflyIII\Http\Controllers\Controller::validate - * @todo Implement testValidate(). + * @covers FireflyIII\Http\Controllers\Controller::validateWith + * @todo Implement testValidateWith(). */ - public function testValidate() + public function testValidateWith() { // Remove the following lines when you implement this test. $this->markTestIncomplete( @@ -109,4 +100,12 @@ class ControllerTest extends TestCase 'This test has not been implemented yet.' ); } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } } diff --git a/tests/acceptance/Controllers/CurrencyControllerTest.php b/tests/acceptance/Controllers/CurrencyControllerTest.php index b283a23f13..a435f2ee98 100644 --- a/tests/acceptance/Controllers/CurrencyControllerTest.php +++ b/tests/acceptance/Controllers/CurrencyControllerTest.php @@ -7,7 +7,6 @@ class CurrencyControllerTest extends TestCase { - /** * Sets up the fixture, for example, opens a network connection. @@ -18,14 +17,6 @@ class CurrencyControllerTest extends TestCase parent::setUp(); } - /** - * Tears down the fixture, for example, closes a network connection. - * This method is called after a test is executed. - */ - protected function tearDown() - { - } - /** * @covers FireflyIII\Http\Controllers\CurrencyController::create * @todo Implement testCreate(). @@ -121,4 +112,12 @@ class CurrencyControllerTest extends TestCase 'This test has not been implemented yet.' ); } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } } diff --git a/tests/acceptance/Controllers/ExportControllerTest.php b/tests/acceptance/Controllers/ExportControllerTest.php index 9850eb4392..5d9c205b40 100644 --- a/tests/acceptance/Controllers/ExportControllerTest.php +++ b/tests/acceptance/Controllers/ExportControllerTest.php @@ -7,7 +7,6 @@ class ExportControllerTest extends TestCase { - /** * Sets up the fixture, for example, opens a network connection. @@ -18,14 +17,6 @@ class ExportControllerTest extends TestCase parent::setUp(); } - /** - * Tears down the fixture, for example, closes a network connection. - * This method is called after a test is executed. - */ - protected function tearDown() - { - } - /** * @covers FireflyIII\Http\Controllers\ExportController::download * @todo Implement testDownload(). @@ -73,4 +64,12 @@ class ExportControllerTest extends TestCase 'This test has not been implemented yet.' ); } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } } diff --git a/tests/acceptance/Controllers/HelpControllerTest.php b/tests/acceptance/Controllers/HelpControllerTest.php index 9b45e5fb48..2b4aa55e42 100644 --- a/tests/acceptance/Controllers/HelpControllerTest.php +++ b/tests/acceptance/Controllers/HelpControllerTest.php @@ -7,7 +7,6 @@ class HelpControllerTest extends TestCase { - /** * Sets up the fixture, for example, opens a network connection. @@ -18,14 +17,6 @@ class HelpControllerTest extends TestCase parent::setUp(); } - /** - * Tears down the fixture, for example, closes a network connection. - * This method is called after a test is executed. - */ - protected function tearDown() - { - } - /** * @covers FireflyIII\Http\Controllers\HelpController::show * @todo Implement testShow(). @@ -37,4 +28,12 @@ class HelpControllerTest extends TestCase 'This test has not been implemented yet.' ); } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } } diff --git a/tests/acceptance/Controllers/ImportControllerTest.php b/tests/acceptance/Controllers/ImportControllerTest.php index ca995c45f6..130d8c98bf 100644 --- a/tests/acceptance/Controllers/ImportControllerTest.php +++ b/tests/acceptance/Controllers/ImportControllerTest.php @@ -7,7 +7,6 @@ class ImportControllerTest extends TestCase { - /** * Sets up the fixture, for example, opens a network connection. @@ -18,14 +17,6 @@ class ImportControllerTest extends TestCase parent::setUp(); } - /** - * Tears down the fixture, for example, closes a network connection. - * This method is called after a test is executed. - */ - protected function tearDown() - { - } - /** * @covers FireflyIII\Http\Controllers\ImportController::complete * @todo Implement testComplete(). @@ -169,4 +160,12 @@ class ImportControllerTest extends TestCase 'This test has not been implemented yet.' ); } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } } diff --git a/tests/acceptance/Controllers/JsonControllerTest.php b/tests/acceptance/Controllers/JsonControllerTest.php index 5bb9a3dc32..123b31ade3 100644 --- a/tests/acceptance/Controllers/JsonControllerTest.php +++ b/tests/acceptance/Controllers/JsonControllerTest.php @@ -7,7 +7,6 @@ class JsonControllerTest extends TestCase { - /** * Sets up the fixture, for example, opens a network connection. @@ -18,14 +17,6 @@ class JsonControllerTest extends TestCase parent::setUp(); } - /** - * Tears down the fixture, for example, closes a network connection. - * This method is called after a test is executed. - */ - protected function tearDown() - { - } - /** * @covers FireflyIII\Http\Controllers\JsonController::action * @todo Implement testAction(). @@ -181,4 +172,12 @@ class JsonControllerTest extends TestCase 'This test has not been implemented yet.' ); } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } } diff --git a/tests/acceptance/Controllers/NewUserControllerTest.php b/tests/acceptance/Controllers/NewUserControllerTest.php index 1a90642f51..6c0b6979af 100644 --- a/tests/acceptance/Controllers/NewUserControllerTest.php +++ b/tests/acceptance/Controllers/NewUserControllerTest.php @@ -7,7 +7,6 @@ class NewUserControllerTest extends TestCase { - /** * Sets up the fixture, for example, opens a network connection. @@ -18,14 +17,6 @@ class NewUserControllerTest extends TestCase parent::setUp(); } - /** - * Tears down the fixture, for example, closes a network connection. - * This method is called after a test is executed. - */ - protected function tearDown() - { - } - /** * @covers FireflyIII\Http\Controllers\NewUserController::index * @todo Implement testIndex(). @@ -49,4 +40,12 @@ class NewUserControllerTest extends TestCase 'This test has not been implemented yet.' ); } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } } diff --git a/tests/acceptance/Controllers/PiggyBankControllerTest.php b/tests/acceptance/Controllers/PiggyBankControllerTest.php index e3ad2bf35f..cba932a6a5 100644 --- a/tests/acceptance/Controllers/PiggyBankControllerTest.php +++ b/tests/acceptance/Controllers/PiggyBankControllerTest.php @@ -7,7 +7,6 @@ class PiggyBankControllerTest extends TestCase { - /** * Sets up the fixture, for example, opens a network connection. @@ -18,14 +17,6 @@ class PiggyBankControllerTest extends TestCase parent::setUp(); } - /** - * Tears down the fixture, for example, closes a network connection. - * This method is called after a test is executed. - */ - protected function tearDown() - { - } - /** * @covers FireflyIII\Http\Controllers\PiggyBankController::add * @todo Implement testAdd(). @@ -205,4 +196,12 @@ class PiggyBankControllerTest extends TestCase 'This test has not been implemented yet.' ); } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } } diff --git a/tests/acceptance/Controllers/Popup/ReportControllerTest.php b/tests/acceptance/Controllers/Popup/ReportControllerTest.php index a240c9c7d1..c98eae9006 100644 --- a/tests/acceptance/Controllers/Popup/ReportControllerTest.php +++ b/tests/acceptance/Controllers/Popup/ReportControllerTest.php @@ -1,5 +1,6 @@ Date: Sun, 20 Nov 2016 11:44:27 +0100 Subject: [PATCH 131/709] Found a bug in the import routine where "default accounts" (an account type no longer used by default) is found. --- app/Import/ImportValidator.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Import/ImportValidator.php b/app/Import/ImportValidator.php index 20705ca605..d6d15d3ed2 100644 --- a/app/Import/ImportValidator.php +++ b/app/Import/ImportValidator.php @@ -416,6 +416,7 @@ class ImportValidator Log::debug('Transaction type is now deposit.'); return $entry; + case AccountType::DEFAULT: case AccountType::ASSET: $entry->fields['transaction-type'] = TransactionType::whereType(TransactionType::TRANSFER)->first(); Log::debug('Transaction type is now transfer.'); From 9340ca09e68fcf224b8df531f59fbbde2f450f7d Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 20 Nov 2016 12:08:43 +0100 Subject: [PATCH 132/709] Fixed #408 --- .../Report/Standard/MonthReportGenerator.php | 7 +-- app/Helpers/Collection/Bill.php | 62 ++++++++++++++++++- app/Helpers/Collection/BillLine.php | 41 ++++++++---- app/Helpers/Report/ReportHelper.php | 13 ++-- 4 files changed, 102 insertions(+), 21 deletions(-) diff --git a/app/Generator/Report/Standard/MonthReportGenerator.php b/app/Generator/Report/Standard/MonthReportGenerator.php index 5bb42eee37..bcffeedff2 100644 --- a/app/Generator/Report/Standard/MonthReportGenerator.php +++ b/app/Generator/Report/Standard/MonthReportGenerator.php @@ -38,10 +38,9 @@ class MonthReportGenerator implements ReportGeneratorInterface */ public function generate(): string { - $helper = app(ReportHelperInterface::class); - $bills = $helper->getBillReport($this->start, $this->end, $this->accounts); - - // and some id's, joined: + /** @var ReportHelperInterface $helper */ + $helper = app(ReportHelperInterface::class); + $bills = $helper->getBillReport($this->start, $this->end, $this->accounts); $accountIds = join(',', $this->accounts->pluck('id')->toArray()); $reportType = 'default'; diff --git a/app/Helpers/Collection/Bill.php b/app/Helpers/Collection/Bill.php index ce83c87551..1cfac94d20 100644 --- a/app/Helpers/Collection/Bill.php +++ b/app/Helpers/Collection/Bill.php @@ -13,7 +13,10 @@ declare(strict_types = 1); namespace FireflyIII\Helpers\Collection; +use Carbon\Carbon; +use FireflyIII\Repositories\Bill\BillRepositoryInterface; use Illuminate\Support\Collection; +use Log; /** * Class Bill @@ -26,7 +29,11 @@ class Bill /** * @var Collection */ - protected $bills; + private $bills; + /** @var Carbon */ + private $endDate; + /** @var Carbon */ + private $startDate; /** * @@ -44,6 +51,43 @@ class Bill $this->bills->push($bill); } + /** + * + */ + public function filterBills() + { + Log::debug('Now in filterBills()'); + /** @var BillRepositoryInterface $repository */ + $repository = app(BillRepositoryInterface::class); + $start = $this->startDate; + $end = $this->endDate; + $lines = $this->bills->filter( + function (BillLine $line) use ($repository, $start, $end) { + // next expected match? + $date = $start; + Log::debug(sprintf('Now at bill line for bill "%s"', $line->getBill()->name)); + Log::debug(sprintf('Default date to use is start date: %s', $date->format('Y-m-d'))); + if ($line->isHit()) { + $date = $line->getLastHitDate(); + Log::debug(sprintf('Line was hit, see date: %s. Always include it.', $date->format('Y-m-d'))); + + return $line; + } + $expected = $repository->nextExpectedMatch($line->getBill(), $date); + Log::debug(sprintf('Next expected match is %s', $expected->format('Y-m-d'))); + if ($expected <= $end && $expected >= $start) { + Log::debug('This date is inside report limits'); + + return $line; + } + Log::debug('This date is OUTSIDE report limits'); + + return false; + } + ); + $this->bills = $lines; + } + /** * @return Collection */ @@ -62,4 +106,20 @@ class Bill return $set; } + /** + * @param Carbon $endDate + */ + public function setEndDate(Carbon $endDate) + { + $this->endDate = $endDate; + } + + /** + * @param Carbon $startDate + */ + public function setStartDate(Carbon $startDate) + { + $this->startDate = $startDate; + } + } diff --git a/app/Helpers/Collection/BillLine.php b/app/Helpers/Collection/BillLine.php index 07e49bc30b..fb28afed18 100644 --- a/app/Helpers/Collection/BillLine.php +++ b/app/Helpers/Collection/BillLine.php @@ -12,6 +12,7 @@ declare(strict_types = 1); namespace FireflyIII\Helpers\Collection; +use Carbon\Carbon; use FireflyIII\Models\Bill as BillModel; /** @@ -23,8 +24,6 @@ use FireflyIII\Models\Bill as BillModel; class BillLine { - /** @var bool */ - protected $active; /** @var string */ protected $amount; /** @var BillModel */ @@ -35,10 +34,19 @@ class BillLine protected $max; /** @var string */ protected $min; - + /** @var Carbon */ + private $lastHitDate; /** @var int */ private $transactionJournalId; + /** + * BillLine constructor. + */ + public function __construct() + { + $this->lastHitDate = new Carbon; + } + /** * @return string */ @@ -124,15 +132,7 @@ class BillLine */ public function isActive(): bool { - return $this->active; - } - - /** - * @param bool $active - */ - public function setActive(bool $active) - { - $this->active = $active; + return intval($this->bill->active) === 1; } /** @@ -151,4 +151,21 @@ class BillLine $this->hit = $hit; } + /** + * @param Carbon $lastHitDate + */ + public function setLastHitDate(Carbon $lastHitDate) + { + $this->lastHitDate = $lastHitDate; + } + + /** + * @return Carbon + */ + public function getLastHitDate(): Carbon + { + return $this->lastHitDate; + } + + } diff --git a/app/Helpers/Report/ReportHelper.php b/app/Helpers/Report/ReportHelper.php index 063264df06..53e1a28fb9 100644 --- a/app/Helpers/Report/ReportHelper.php +++ b/app/Helpers/Report/ReportHelper.php @@ -19,7 +19,7 @@ use FireflyIII\Helpers\Collection\BillLine; use FireflyIII\Helpers\Collection\Category as CategoryCollection; use FireflyIII\Helpers\Collection\Expense; use FireflyIII\Helpers\Collection\Income; -use FireflyIII\Helpers\Collector\JournalCollector; +use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Helpers\FiscalHelperInterface; use FireflyIII\Models\Bill; use FireflyIII\Models\Category; @@ -70,16 +70,17 @@ class ReportHelper implements ReportHelperInterface /** @var BillRepositoryInterface $repository */ $repository = app(BillRepositoryInterface::class); $bills = $repository->getBillsForAccounts($accounts); - $collector = new JournalCollector(auth()->user()); + $collector = app(JournalCollectorInterface::class, [auth()->user()]); $collector->setAccounts($accounts)->setRange($start, $end)->setBills($bills); $journals = $collector->getJournals(); $collection = new BillCollection; + $collection->setStartDate($start); + $collection->setEndDate($end); /** @var Bill $bill */ foreach ($bills as $bill) { $billLine = new BillLine; $billLine->setBill($bill); - $billLine->setActive(intval($bill->active) === 1); $billLine->setMin(strval($bill->amount_min)); $billLine->setMax(strval($bill->amount_max)); $billLine->setHit(false); @@ -94,15 +95,19 @@ class ReportHelper implements ReportHelperInterface if (!is_null($first)) { $billLine->setTransactionJournalId($first->id); $billLine->setAmount($first->transaction_amount); + $billLine->setLastHitDate($first->date); $billLine->setHit(true); } - // non active AND non hit? do not add: + // bill is active, or bill is hit: if ($billLine->isActive() || $billLine->isHit()) { $collection->addBill($billLine); } } + // do some extra filtering. + $collection->filterBills(); + return $collection; } From 350e0b08b148a6e53d1db48a3126065baa7d4ca8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 20 Nov 2016 12:51:33 +0100 Subject: [PATCH 133/709] This implements #377 --- app/Http/Controllers/AccountController.php | 20 ++++ .../Controllers/TransactionController.php | 103 +++++++++++++++++- app/Http/breadcrumbs.php | 33 ++++++ resources/lang/en_US/firefly.php | 1 + resources/views/accounts/show.twig | 6 + resources/views/transactions/index-all.twig | 23 ++++ resources/views/transactions/index-date.twig | 23 ++++ resources/views/transactions/index.twig | 24 +++- routes/web.php | 15 ++- 9 files changed, 242 insertions(+), 6 deletions(-) create mode 100644 resources/views/transactions/index-all.twig create mode 100644 resources/views/transactions/index-date.twig diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 1273797ad6..fca6f6f678 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -276,6 +276,26 @@ class AccountController extends Controller return view('accounts.show', compact('account', 'what', 'entries', 'subTitleIcon', 'journals', 'subTitle')); } + /** + * @param Account $account + * + * @return View + */ + public function showAll(Account $account) + { + $subTitle = sprintf('%s (%s)', $account->name, strtolower(trans('firefly.everything'))); + $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); + + // replace with journal collector: + $collector = new JournalCollector(auth()->user()); + $collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page); + $journals = $collector->getPaginatedJournals(); + $journals->setPath('accounts/show/' . $account->id . '/all'); + + return view('accounts.show_with_date', compact('category', 'date', 'account', 'journals', 'subTitle', 'carbon')); + } + /** * @param Account $account * @param string $date diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 594f5f103e..70c9dd7164 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -14,12 +14,14 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; use Carbon\Carbon; -use FireflyIII\Helpers\Collector\JournalCollector; +use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalTaskerInterface; use Illuminate\Http\Request; +use Illuminate\Support\Collection; use Log; +use Navigation; use Preferences; use Response; use View; @@ -56,15 +58,22 @@ class TransactionController extends Controller * * @return View */ - public function index(Request $request, string $what) + public function index(Request $request, JournalRepositoryInterface $repository, string $what) { $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $subTitleIcon = config('firefly.transactionIconsByWhat.' . $what); $types = config('firefly.transactionTypesByWhat.' . $what); $subTitle = trans('firefly.title_' . $what); + $range = Preferences::get('viewRange', '1M')->data; $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); - $collector = new JournalCollector(auth()->user()); + // to make sure we only grab a subset, based on the current date (in session): + $start = session('start', Navigation::startOfPeriod(new Carbon, $range)); + $end = session('end', Navigation::endOfPeriod(new Carbon, $range)); + + + $collector = app(JournalCollectorInterface::class, [auth()->user()]); $collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts(); + $collector->setRange($start, $end); // do not filter transfers if $what = transfer. if (!in_array($what, ['transfer', 'transfers'])) { @@ -75,7 +84,93 @@ class TransactionController extends Controller $journals = $collector->getPaginatedJournals(); $journals->setPath('transactions/' . $what); - return view('transactions.index', compact('subTitle', 'what', 'subTitleIcon', 'journals')); + unset($start, $end); + + // then also show a list of periods where the user can click on, based on the + // user's range and the oldest journal the user has: + $first = $repository->first(); + $blockStart = is_null($first->id) ? new Carbon : $first->date; + $blockStart = Navigation::startOfPeriod($blockStart, $range); + $blockEnd = Navigation::endOfX(new Carbon, $range); + $entries = new Collection; + + while ($blockEnd >= $blockStart) { + Log::debug(sprintf('Now at blockEnd: %s', $blockEnd->format('Y-m-d'))); + $blockEnd = Navigation::startOfPeriod($blockEnd, $range); + $dateStr = $blockEnd->format('Y-m-d'); + $dateName = Navigation::periodShow($blockEnd, $range); + $entries->push([$dateStr, $dateName]); + $blockEnd = Navigation::subtractPeriod($blockEnd, $range, 1); + } + + return view('transactions.index', compact('subTitle', 'what', 'subTitleIcon', 'journals', 'entries')); + + } + + /** + * @param Request $request + * @param string $what + * + * @return View + */ + public function indexAll(Request $request, string $what) + { + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); + $subTitleIcon = config('firefly.transactionIconsByWhat.' . $what); + $types = config('firefly.transactionTypesByWhat.' . $what); + $subTitle = sprintf('%s (%s)', trans('firefly.title_' . $what), strtolower(trans('firefly.everything'))); + $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); + + $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts(); + + // do not filter transfers if $what = transfer. + if (!in_array($what, ['transfer', 'transfers'])) { + Log::debug('Also get opposing account info.'); + $collector->withOpposingAccount(); + } + + $journals = $collector->getPaginatedJournals(); + $journals->setPath('transactions/' . $what . '/all'); + + return view('transactions.index-all', compact('subTitle', 'what', 'subTitleIcon', 'journals')); + + } + + /** + * @param Request $request + * @param string $what + * + * @return View + */ + public function indexDate(Request $request, string $what, string $date) + { + $carbon = new Carbon($date); + $range = Preferences::get('viewRange', '1M')->data; + $start = Navigation::startOfPeriod($carbon, $range); + $end = Navigation::endOfPeriod($carbon, $range); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); + $subTitleIcon = config('firefly.transactionIconsByWhat.' . $what); + $types = config('firefly.transactionTypesByWhat.' . $what); + $subTitle = trans('firefly.title_' . $what) . ' (' . Navigation::periodShow($carbon, $range) . ')'; + $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); + + Log::debug(sprintf('Transaction index by date will show between %s and %s', $start->format('Y-m-d'), $end->format('Y-m-d'))); + + $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts(); + $collector->setRange($start, $end); + + // do not filter transfers if $what = transfer. + if (!in_array($what, ['transfer', 'transfers'])) { + Log::debug('Also get opposing account info.'); + $collector->withOpposingAccount(); + } + + $journals = $collector->getPaginatedJournals(); + $journals->setPath('transactions/' . $what . '/' . $date); + + return view('transactions.index-date', compact('subTitle', 'what', 'subTitleIcon', 'journals', 'carbon')); } diff --git a/app/Http/breadcrumbs.php b/app/Http/breadcrumbs.php index 9de3b720ac..925c0cac99 100644 --- a/app/Http/breadcrumbs.php +++ b/app/Http/breadcrumbs.php @@ -83,6 +83,17 @@ Breadcrumbs::register( } ); +Breadcrumbs::register( + 'accounts.show.all', function (BreadCrumbGenerator $breadcrumbs, Account $account) { + $breadcrumbs->parent('accounts.show', $account); + + $title = sprintf('%s (%s)', $account->name, strtolower(trans('firefly.everything'))); + + $breadcrumbs->push($title, route('accounts.show.all', [$account->id])); +} +); + + Breadcrumbs::register( 'accounts.delete', function (BreadCrumbGenerator $breadcrumbs, Account $account) { $breadcrumbs->parent('accounts.show', $account); @@ -573,6 +584,28 @@ Breadcrumbs::register( $breadcrumbs->push(trans('breadcrumbs.' . $what . '_list'), route('transactions.index', [$what])); } ); + +Breadcrumbs::register( + 'transactions.index.all', function (BreadCrumbGenerator $breadcrumbs, string $what) { + $breadcrumbs->parent('transactions.index', $what); + + $title = sprintf('%s (%s)', trans('breadcrumbs.' . $what . '_list'), strtolower(trans('firefly.everything'))); + + $breadcrumbs->push($title, route('transactions.index.all', [$what])); +} +); + +Breadcrumbs::register( + 'transactions.index.date', function (BreadCrumbGenerator $breadcrumbs, string $what, Carbon $date) { + $breadcrumbs->parent('transactions.index', $what); + + $range = Preferences::get('viewRange', '1M')->data; + $title = trans('breadcrumbs.' . $what . '_list') . ' (' . Navigation::periodShow($date, $range) . ')'; + + $breadcrumbs->push($title, route('transactions.index.date', [$what, $date->format('Y-m-d')])); +} +); + Breadcrumbs::register( 'transactions.create', function (BreadCrumbGenerator $breadcrumbs, string $what) { $breadcrumbs->parent('transactions.index', $what); diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 5935bdfc48..2befbc056f 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -86,6 +86,7 @@ return [ 'field_supports_markdown' => 'This field supports Markdown.', 'need_more_help' => 'If you need more help using Firefly III, please open a ticker on Github.', 'nothing_to_display' => 'There are no transactions to show you', + 'show_all_no_filter' => 'Show all transactions without grouping them by date.', // repeat frequencies: 'repeat_freq_yearly' => 'yearly', diff --git a/resources/views/accounts/show.twig b/resources/views/accounts/show.twig index b69befe609..280f881978 100644 --- a/resources/views/accounts/show.twig +++ b/resources/views/accounts/show.twig @@ -38,6 +38,12 @@
{% include 'list.journals-tasker' with {sorting:true} %} +

+ + + {{ 'show_all_no_filter'|_ }} + +

diff --git a/resources/views/transactions/index-all.twig b/resources/views/transactions/index-all.twig new file mode 100644 index 0000000000..270afeb6d1 --- /dev/null +++ b/resources/views/transactions/index-all.twig @@ -0,0 +1,23 @@ +{% extends "./layout/default" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, what) }} +{% endblock %} + +{% block content %} +
+
+
+
+

{{ subTitle }}

+
+
+ {% include 'list.journals-tasker' %} +
+
+
+
+{% endblock %} +{% block scripts %} + +{% endblock %} \ No newline at end of file diff --git a/resources/views/transactions/index-date.twig b/resources/views/transactions/index-date.twig new file mode 100644 index 0000000000..8250e832f7 --- /dev/null +++ b/resources/views/transactions/index-date.twig @@ -0,0 +1,23 @@ +{% extends "./layout/default" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, what, carbon) }} +{% endblock %} + +{% block content %} +
+
+
+
+

{{ subTitle }}

+
+
+ {% include 'list.journals-tasker' %} +
+
+
+
+{% endblock %} +{% block scripts %} + +{% endblock %} \ No newline at end of file diff --git a/resources/views/transactions/index.twig b/resources/views/transactions/index.twig index 3d63c4e0b1..9aa5eb2b73 100644 --- a/resources/views/transactions/index.twig +++ b/resources/views/transactions/index.twig @@ -6,16 +6,38 @@ {% block content %}
-
+

{{ subTitle }}

{% include 'list.journals-tasker' %} +

+ + + {{ 'show_all_no_filter'|_ }} + +

+ +
+ {% for entry in entries %} +
+
+

+ {{ entry[1] }} +

+
+
+   +
+
+ {% endfor %} +
+
{% endblock %} {% block scripts %} diff --git a/routes/web.php b/routes/web.php index 5fa77bcc3b..58f4a070f3 100755 --- a/routes/web.php +++ b/routes/web.php @@ -92,6 +92,7 @@ Route::group( Route::get('/accounts/edit/{account}', ['uses' => 'AccountController@edit', 'as' => 'accounts.edit']); Route::get('/accounts/delete/{account}', ['uses' => 'AccountController@delete', 'as' => 'accounts.delete']); Route::get('/accounts/show/{account}', ['uses' => 'AccountController@show', 'as' => 'accounts.show']); + Route::get('/accounts/show/{account}/all', ['uses' => 'AccountController@showAll', 'as' => 'accounts.show.all']); Route::get('/accounts/show/{account}/{date}', ['uses' => 'AccountController@showWithDate', 'as' => 'accounts.show.date']); @@ -455,10 +456,22 @@ Route::group( * Transaction Controller */ - // normal controller + // normal controller: index for session range Route::get('/transactions/{what}', ['uses' => 'TransactionController@index', 'as' => 'transactions.index'])->where( ['what' => 'expenses|revenue|withdrawal|deposit|transfer|transfers'] ); + + // normal controller: index showing ALL: + Route::get('/transactions/{what}/all', ['uses' => 'TransactionController@indexAll', 'as' => 'transactions.index.all'])->where( + ['what' => 'expenses|revenue|withdrawal|deposit|transfer|transfers'] + ); + + // normal controller: index for specific date range: + Route::get('/transactions/{what}/{date}', ['uses' => 'TransactionController@indexDate', 'as' => 'transactions.index.date'])->where( + ['what' => 'expenses|revenue|withdrawal|deposit|transfer|transfers'] + ); + + Route::get('/transaction/show/{tj}', ['uses' => 'TransactionController@show', 'as' => 'transactions.show']); Route::post('/transaction/reorder', ['uses' => 'TransactionController@reorder', 'as' => 'transactions.reorder']); From ec4ec1a1477ffaef12234a3f3dae6947888236ad Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 20 Nov 2016 12:53:04 +0100 Subject: [PATCH 134/709] New (not implemented) tests. --- .../Controllers/TransactionControllerTest.php | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/tests/acceptance/Controllers/TransactionControllerTest.php b/tests/acceptance/Controllers/TransactionControllerTest.php index fb16bd53de..b1ebfe89bf 100644 --- a/tests/acceptance/Controllers/TransactionControllerTest.php +++ b/tests/acceptance/Controllers/TransactionControllerTest.php @@ -2,11 +2,12 @@ /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:14. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 11:51:45. */ class TransactionControllerTest extends TestCase { + /** * Sets up the fixture, for example, opens a network connection. @@ -17,6 +18,14 @@ class TransactionControllerTest extends TestCase parent::setUp(); } + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } + /** * @covers FireflyIII\Http\Controllers\TransactionController::index * @todo Implement testIndex(). @@ -29,6 +38,30 @@ class TransactionControllerTest extends TestCase ); } + /** + * @covers FireflyIII\Http\Controllers\TransactionController::indexAll + * @todo Implement testIndexAll(). + */ + public function testIndexAll() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\TransactionController::indexDate + * @todo Implement testIndexDate(). + */ + public function testIndexDate() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + /** * @covers FireflyIII\Http\Controllers\TransactionController::reorder * @todo Implement testReorder(). @@ -52,12 +85,4 @@ class TransactionControllerTest extends TestCase 'This test has not been implemented yet.' ); } - - /** - * Tears down the fixture, for example, closes a network connection. - * This method is called after a test is executed. - */ - protected function tearDown() - { - } } From 78f297e18f8f9f9b6af205cc3120cad1bc9539f0 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 20 Nov 2016 14:17:16 +0100 Subject: [PATCH 135/709] Fixed some display bugs for split journals. --- .../Collector/JournalExportCollector.php | 2 +- app/Helpers/Collector/JournalCollector.php | 3 +- app/Models/Account.php | 1 + app/Repositories/Budget/BudgetRepository.php | 2 +- app/Repositories/Journal/JournalTasker.php | 28 ------------------- 5 files changed, 5 insertions(+), 31 deletions(-) diff --git a/app/Export/Collector/JournalExportCollector.php b/app/Export/Collector/JournalExportCollector.php index 927869718b..b9bd7fe0ed 100644 --- a/app/Export/Collector/JournalExportCollector.php +++ b/app/Export/Collector/JournalExportCollector.php @@ -298,7 +298,7 @@ class JournalExportCollector extends BasicCollector implements CollectorInterfac 'transactions AS opposing', function (JoinClause $join) { $join->on('opposing.transaction_journal_id', '=', 'transactions.transaction_journal_id') ->where('opposing.amount', '=', DB::raw('transactions.amount * -1')) - ->where('transactions.identifier', '=', 'opposing.identifier'); + ->where('transactions.identifier', '=', DB::raw('opposing.identifier')); } ) ->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id') diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index 3894133bdd..a3c0ad1397 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -541,12 +541,13 @@ class JournalCollector implements JournalCollectorInterface $this->query->leftJoin( 'transactions as opposing', function (JoinClause $join) { $join->on('opposing.transaction_journal_id', '=', 'transactions.transaction_journal_id') - ->where('opposing.identifier', '=', 'transactions.identifier') + ->where('opposing.identifier', '=', DB::raw('transactions.identifier')) ->where('opposing.amount', '=', DB::raw('transactions.amount * -1')); } ); $this->query->leftJoin('accounts as opposing_accounts', 'opposing.account_id', '=', 'opposing_accounts.id'); $this->query->leftJoin('account_types as opposing_account_types', 'opposing_accounts.account_type_id', '=', 'opposing_account_types.id'); + $this->query->whereNull('opposing.deleted_at'); $this->fields[] = 'opposing.account_id as opposing_account_id'; $this->fields[] = 'opposing_accounts.name as opposing_account_name'; diff --git a/app/Models/Account.php b/app/Models/Account.php index 2b13ed17a5..efecd2fa28 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -15,6 +15,7 @@ namespace FireflyIII\Models; use Carbon\Carbon; use Crypt; +use DB; use FireflyIII\Exceptions\FireflyException; use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index e1cc7fa0f9..62a9add9c1 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -334,7 +334,7 @@ class BudgetRepository implements BudgetRepositoryInterface ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') ->leftJoin( 'transactions', function (JoinClause $join) { - $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', '0'); + $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0); } ) ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) diff --git a/app/Repositories/Journal/JournalTasker.php b/app/Repositories/Journal/JournalTasker.php index f4337eb8d6..d717fdbda5 100644 --- a/app/Repositories/Journal/JournalTasker.php +++ b/app/Repositories/Journal/JournalTasker.php @@ -74,34 +74,6 @@ class JournalTasker implements JournalTaskerInterface public function getTransactionsOverview(TransactionJournal $journal): array { // get all transaction data + the opposite site in one list. - /** - * select - * - * source.id, - * source.account_id, - * source_accounts.name as account_name, - * source_accounts.encrypted as account_encrypted, - * source.amount, - * source.description, - * - * destination.id as destination_id, - * destination.account_id as destination_account_id, - * destination_accounts.name as destination_account_name, - * destination_accounts.encrypted as destination_account_encrypted - * - * - * from transactions as source - * - * left join transactions as destination ON source.transaction_journal_id = - * destination.transaction_journal_id AND source.amount = destination.amount * -1 AND source.identifier = destination.identifier - * -- left join source account name: - * left join accounts as source_accounts ON source.account_id = source_accounts.id - * left join accounts as destination_accounts ON destination.account_id = destination_accounts.id - * - * where source.transaction_journal_id = 6600 - * and source.amount < 0 - * and source.deleted_at is null - */ $set = $journal ->transactions()// "source" ->leftJoin( From 0b613c3b8cf4cfacfe1f72e65ec3d5bcb9c6714b Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 20 Nov 2016 15:30:16 +0100 Subject: [PATCH 136/709] Improve sortability in various lists. --- app/Http/Controllers/Admin/UserController.php | 12 ++++---- app/Support/Preferences.php | 24 +++++++++++++++ resources/views/admin/domains/index.twig | 30 ++++++++----------- resources/views/admin/users/index.twig | 26 +++++++++------- resources/views/list/accounts.twig | 16 +++++----- resources/views/list/bills.twig | 26 ++++++++-------- resources/views/list/categories.twig | 4 +-- 7 files changed, 83 insertions(+), 55 deletions(-) diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 69a329c2c9..ac1f2866f0 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -57,20 +57,22 @@ class UserController extends Controller // add meta stuff. $users->each( function (User $user) use ($mustConfirmAccount) { - // is user activated? - $isConfirmed = Preferences::getForUser($user, 'user_confirmed', false)->data; + $list = ['user_confirmed', 'twoFactorAuthEnabled', 'twoFactorAuthSecret', 'registration_ip_address', 'confirmation_ip_address']; + $preferences = Preferences::getArrayForUser($user, $list); + $user->activated = true; - if ($isConfirmed === false && $mustConfirmAccount === true) { + if (!($preferences['user_confirmed'] === true) && $mustConfirmAccount === true) { $user->activated = false; } $user->isAdmin = $user->hasRole('owner'); - $is2faEnabled = Preferences::getForUser($user, 'twoFactorAuthEnabled', false)->data; - $has2faSecret = !is_null(Preferences::getForUser($user, 'twoFactorAuthSecret')); + $is2faEnabled = $preferences['twoFactorAuthEnabled'] === true; + $has2faSecret = !is_null($preferences['twoFactorAuthSecret']); $user->has2FA = false; if ($is2faEnabled && $has2faSecret) { $user->has2FA = true; } + $user->prefs = $preferences; } ); diff --git a/app/Support/Preferences.php b/app/Support/Preferences.php index 55ed08c6dc..44f709de98 100644 --- a/app/Support/Preferences.php +++ b/app/Support/Preferences.php @@ -57,6 +57,30 @@ class Preferences return $this->getForUser(auth()->user(), $name, $default); } + /** + * @param User $user + * @param array $list + * + * @return array + */ + public function getArrayForUser(User $user, array $list): array + { + $result = []; + $preferences = Preference::where('user_id', $user->id)->whereIn('name', $list)->get(['id', 'name', 'data']); + /** @var Preference $preference */ + foreach ($preferences as $preference) { + $result[$preference->name] = $preference->data; + } + foreach ($list as $name) { + if (!isset($result[$name])) { + $result[$name] = null; + } + } + + return $result; + + } + /** * @param \FireflyIII\User $user * @param string $name diff --git a/resources/views/admin/domains/index.twig b/resources/views/admin/domains/index.twig index 5245f5901c..2ea2a88005 100644 --- a/resources/views/admin/domains/index.twig +++ b/resources/views/admin/domains/index.twig @@ -5,19 +5,18 @@ {% endblock %} {% block content %}
-
+

{{ 'all_blocked_domains'|_ }}

-
+
{% if domains|length > 0 %} - +
- - - + + @@ -27,9 +26,9 @@ unblock - - {% endfor %} @@ -43,12 +42,9 @@ - - - -
-
+ +

{{ 'all_user_domains'|_ }}

@@ -59,11 +55,11 @@ {{ 'all_domains_is_filtered'|_ }}

-
 {{ trans('list.domain') }}{{ trans('list.is_blocked') }} {{ trans('list.domain') }}
{{ domain }} - + + {{ domain }} + (whois)
+
- - + + diff --git a/resources/views/admin/users/index.twig b/resources/views/admin/users/index.twig index cbe5832e92..f71843647c 100644 --- a/resources/views/admin/users/index.twig +++ b/resources/views/admin/users/index.twig @@ -14,16 +14,16 @@
 {{ trans('list.domain') }} {{ trans('list.domain') }}
- - - - - + + + + + - + @@ -41,11 +41,17 @@ {{ user.created_at.formatLocalized(monthAndDayFormat) }} {{ user.created_at.format('H:i') }} - - - + {% if what == 'asset' %} - + {% endif %} - - + + - - + + @@ -42,11 +42,11 @@ {% endif %} {% if account.lastActivityDate %} - {% else %} - {% endif %} diff --git a/resources/views/list/bills.twig b/resources/views/list/bills.twig index 2443bda422..02ae6daedb 100644 --- a/resources/views/list/bills.twig +++ b/resources/views/list/bills.twig @@ -3,13 +3,13 @@ - - - - + + + + - + @@ -37,36 +37,36 @@ {% if entry.paidDates.count() == 0 and entry.payDates.count() == 0 and entry.active %} - - {% endif %} {% if entry.paidDates.count() == 0 and entry.payDates.count() > 0 and entry.active %} - - {% endif %} {% if entry.paidDates.count() == entry.payDates.count() and entry.payDates.count() > 0 and entry.active %} - - {% endif %} {% if not entry.active %} - - {% endif %} diff --git a/resources/views/list/categories.twig b/resources/views/list/categories.twig index 11cda8e04e..16deef6773 100644 --- a/resources/views/list/categories.twig +++ b/resources/views/list/categories.twig @@ -24,11 +24,11 @@ {{ category.name }} {% if category.lastActivity.year != "1900" %} - {% else %} - {% endif %} From c56f937521085356a11b5feffa55e72a19e6fb2b Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 20 Nov 2016 17:36:11 +0100 Subject: [PATCH 137/709] Improved sorting in various views. --- resources/views/admin/users/index.twig | 2 +- resources/views/list/accounts.twig | 6 +++--- resources/views/list/bills.twig | 16 ++++++++-------- resources/views/list/categories.twig | 10 +++++----- resources/views/reports/category/month.twig | 16 ++++++++-------- resources/views/reports/default/year.twig | 4 ++++ resources/views/reports/partials/accounts.twig | 10 +++++----- resources/views/reports/partials/bills.twig | 10 +++++----- .../views/reports/partials/budget-period.twig | 8 ++++---- resources/views/rules/index.twig | 2 +- resources/views/search/index.twig | 15 ++++++++++----- resources/views/search/partials/accounts.twig | 12 +++++++----- resources/views/search/partials/budgets.twig | 6 ++++-- resources/views/search/partials/categories.twig | 6 ++++-- resources/views/search/partials/tags.twig | 8 +++++--- .../views/search/partials/transactions.twig | 16 ++++++++-------- 16 files changed, 82 insertions(+), 65 deletions(-) diff --git a/resources/views/admin/users/index.twig b/resources/views/admin/users/index.twig index f71843647c..af0a04df9f 100644 --- a/resources/views/admin/users/index.twig +++ b/resources/views/admin/users/index.twig @@ -37,7 +37,7 @@ - diff --git a/resources/views/list/accounts.twig b/resources/views/list/accounts.twig index 995ae450ee..2f0eb6544f 100644 --- a/resources/views/list/accounts.twig +++ b/resources/views/list/accounts.twig @@ -22,7 +22,7 @@ - + {% if what == "asset" %} {% if account.lastActivityDate %} - {% else %} - {% endif %} diff --git a/resources/views/list/bills.twig b/resources/views/list/bills.twig index 02ae6daedb..2bf6f5b128 100644 --- a/resources/views/list/bills.twig +++ b/resources/views/list/bills.twig @@ -21,7 +21,7 @@ - {% if entry.paidDates.count() == 0 and entry.payDates.count() == 0 and entry.active %} - - {% endif %} {% if entry.paidDates.count() == 0 and entry.payDates.count() > 0 and entry.active %} - - {% endif %} {% if entry.paidDates.count() == entry.payDates.count() and entry.payDates.count() > 0 and entry.active %} - - {% endif %} {% if not entry.active %} - - - + + @@ -20,15 +20,15 @@ - {% if category.lastActivity.year != "1900" %} - {% else %} - {% endif %} diff --git a/resources/views/reports/category/month.twig b/resources/views/reports/category/month.twig index 7e860d70c5..49556850ac 100644 --- a/resources/views/reports/category/month.twig +++ b/resources/views/reports/category/month.twig @@ -276,13 +276,13 @@ - - - @@ -304,9 +304,9 @@ - + - + @@ -325,15 +325,15 @@ {% endif %} - - - diff --git a/resources/views/reports/default/year.twig b/resources/views/reports/default/year.twig index de0a4df7bb..8ef09d32af 100644 --- a/resources/views/reports/default/year.twig +++ b/resources/views/reports/default/year.twig @@ -115,6 +115,7 @@ {% endblock %} {% block scripts %} + @@ -140,3 +141,6 @@ {% endblock %} +{% block styles %} + +{% endblock %} \ No newline at end of file diff --git a/resources/views/reports/partials/accounts.twig b/resources/views/reports/partials/accounts.twig index cbdeaa68f5..ef3ad787e7 100644 --- a/resources/views/reports/partials/accounts.twig +++ b/resources/views/reports/partials/accounts.twig @@ -1,16 +1,16 @@
{{ trans('list.email') }}{{ trans('list.email') }} {{ trans('list.is_activated') }} {{ trans('list.is_blocked') }}
{{ trans('list.name') }}{{ trans('list.name') }}{{ trans('list.currentBalance') }}{{ trans('list.currentBalance') }}
{{ trans('list.name') }}{{ trans('list.matchingAmount') }}{{ trans('list.matchingAmount') }}
+ {{ 'not_expected_period'|_ }} + {{ 'not_or_not_yet'|_ }} + {% for date in entry.paidDates %} {{ date.formatLocalized(monthAndDayFormat) }}
{% endfor %}
+ ~ {{ user.email }} {{ account.name }}{{ account.name }} + {{ entry.name }} + {{ 'not_expected_period'|_ }} + {{ 'not_or_not_yet'|_ }} + {% for date in entry.paidDates %} {{ date.formatLocalized(monthAndDayFormat) }}
{% endfor %}
+ ~
 {{ trans('list.name') }}{{ trans('list.name') }}
+ {{ category.name }} {{ row.name }} + {{ row.average|formatAmount }} + {{ row.sum|formatAmount }} + {{ row.count }}
{{ 'description'|_ }}{{ 'date'|_ }}{{ 'date'|_ }} {{ 'account'|_ }}{{ 'amount'|_ }}{{ 'amount'|_ }}
+ {{ row.date.formatLocalized(monthAndDayFormat) }} + {{ row.opposing_account_name }} + {{ row.transaction_amount|formatAmount }}
- - - - + + + + {% for account in accountReport.getAccounts %} - diff --git a/resources/views/reports/partials/bills.twig b/resources/views/reports/partials/bills.twig index 22c84668f3..fe234655ef 100644 --- a/resources/views/reports/partials/bills.twig +++ b/resources/views/reports/partials/bills.twig @@ -6,11 +6,11 @@
{{ 'name'|_ }}{{ 'difference'|_ }}{{ 'name'|_ }}{{ 'difference'|_ }}
+ {{ account.name }}
- - - - - + + + + + diff --git a/resources/views/reports/partials/budget-period.twig b/resources/views/reports/partials/budget-period.twig index e801a6e1b6..53308f4606 100644 --- a/resources/views/reports/partials/budget-period.twig +++ b/resources/views/reports/partials/budget-period.twig @@ -1,17 +1,17 @@
{{ trans('form.name') }}{{ trans('form.amount') }}{{ trans('form.under') }}{{ trans('form.name') }}{{ trans('form.amount') }}{{ trans('form.under') }}
- + {% for period in periods %} - + {% endfor %} - + {% for id, info in budgets %} - {% for amount in info.entries %} diff --git a/resources/views/rules/index.twig b/resources/views/rules/index.twig index 86e530da0f..f126463521 100644 --- a/resources/views/rules/index.twig +++ b/resources/views/rules/index.twig @@ -64,7 +64,7 @@

{% if ruleGroup.rules.count > 0 %} -
{{ 'budget'|_ }}{{ 'budget'|_ }}{{ period }}{{ period }}{{ 'sum'|_ }}{{ 'sum'|_ }}
+ {{ info.name }}
+
diff --git a/resources/views/search/index.twig b/resources/views/search/index.twig index 88107c52d6..e2e3a76ffe 100644 --- a/resources/views/search/index.twig +++ b/resources/views/search/index.twig @@ -104,8 +104,13 @@ {% endblock %} - {% block scripts %} - - {% endblock %} +{% block scripts %} + + +{% endblock %} + +{% block styles %} + +{% endblock %} \ No newline at end of file diff --git a/resources/views/search/partials/accounts.twig b/resources/views/search/partials/accounts.twig index e21b443d59..45c4a2466a 100644 --- a/resources/views/search/partials/accounts.twig +++ b/resources/views/search/partials/accounts.twig @@ -2,10 +2,10 @@ - - - - + + + + @@ -17,7 +17,9 @@ - + - + @@ -14,7 +14,9 @@ - + {% endfor %} diff --git a/resources/views/search/partials/categories.twig b/resources/views/search/partials/categories.twig index 4176de3240..7dfef5da15 100644 --- a/resources/views/search/partials/categories.twig +++ b/resources/views/search/partials/categories.twig @@ -2,7 +2,7 @@ - + @@ -14,7 +14,9 @@ - + {% endfor %} diff --git a/resources/views/search/partials/tags.twig b/resources/views/search/partials/tags.twig index c4b3e21a2c..919fa19daf 100644 --- a/resources/views/search/partials/tags.twig +++ b/resources/views/search/partials/tags.twig @@ -2,8 +2,8 @@ - - + + @@ -15,7 +15,9 @@ - + {% endfor %} diff --git a/resources/views/search/partials/transactions.twig b/resources/views/search/partials/transactions.twig index c20da1ff07..279a336a3c 100644 --- a/resources/views/search/partials/transactions.twig +++ b/resources/views/search/partials/transactions.twig @@ -1,17 +1,17 @@ {{ journals.render|raw }} -
{{ 'rule_name'|_ }}
{{ trans('list.name') }}{{ trans('list.name') }}
{{ account.name }} + {{ account.name }} + {{ trans('firefly.'~account.accountType.type) }}
{{ trans('list.name') }}{{ trans('list.name') }}
{{ budget.name }} + {{ budget.name }} +
{{ trans('list.name') }}{{ trans('list.name') }}
{{ category.name }} + {{ category.name }} +
{{ trans('list.name') }}{{ trans('list.type') }}{{ trans('list.name') }}{{ trans('list.type') }}
{{ tag.tag }} + {{ tag.tag }}< + /td> {{ ('tag'~tag.tagMode)|_ }}
+
- - - - + + + + {% for transaction in transactions %} - + - - From 8baea2feb9f4aabf5341c02a5749d8214395c0b6 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 20 Nov 2016 18:31:29 +0100 Subject: [PATCH 138/709] Code for #385 --- .../AccountChartGeneratorInterface.php | 9 + .../Account/ChartJsAccountChartGenerator.php | 32 ++++ app/Helpers/Collector/JournalCollector.php | 29 ++- .../Collector/JournalCollectorInterface.php | 10 + app/Http/Controllers/AccountController.php | 120 ++++++------ .../Controllers/Chart/AccountController.php | 171 +++++++++++++++++- public/js/ff/accounts/show.js | 9 +- public/js/ff/accounts/show_with_date.js | 14 +- resources/lang/en_US/firefly.php | 3 + resources/views/accounts/show.twig | 45 ++++- resources/views/accounts/show_with_date.twig | 45 +++++ routes/web.php | 13 ++ 12 files changed, 433 insertions(+), 67 deletions(-) diff --git a/app/Generator/Chart/Account/AccountChartGeneratorInterface.php b/app/Generator/Chart/Account/AccountChartGeneratorInterface.php index 0cb57197da..13bd02b88d 100644 --- a/app/Generator/Chart/Account/AccountChartGeneratorInterface.php +++ b/app/Generator/Chart/Account/AccountChartGeneratorInterface.php @@ -24,6 +24,15 @@ use Illuminate\Support\Collection; */ interface AccountChartGeneratorInterface { + + /** + * @param array $values + * @param array $names + * + * @return array + */ + public function pieChart(array $values, array $names): array; + /** * @param Collection $accounts * @param Carbon $start diff --git a/app/Generator/Chart/Account/ChartJsAccountChartGenerator.php b/app/Generator/Chart/Account/ChartJsAccountChartGenerator.php index a8a222e16b..13253299bf 100644 --- a/app/Generator/Chart/Account/ChartJsAccountChartGenerator.php +++ b/app/Generator/Chart/Account/ChartJsAccountChartGenerator.php @@ -14,6 +14,7 @@ namespace FireflyIII\Generator\Chart\Account; use Carbon\Carbon; use FireflyIII\Models\Account; +use FireflyIII\Support\ChartColour; use Illuminate\Support\Collection; /** @@ -83,6 +84,37 @@ class ChartJsAccountChartGenerator implements AccountChartGeneratorInterface return $data; } + /** + * @param array $values + * @param array $names + * + * @return array + */ + public function pieChart(array $values, array $names): array + { + $data = [ + 'datasets' => [ + 0 => [], + ], + 'labels' => [], + ]; + $index = 0; + foreach ($values as $categoryId => $value) { + + // make larger than 0 + if (bccomp($value, '0') === -1) { + $value = bcmul($value, '-1'); + } + + $data['datasets'][0]['data'][] = round($value, 2); + $data['datasets'][0]['backgroundColor'][] = ChartColour::getColour($index); + $data['labels'][] = $names[$categoryId]; + $index++; + } + + return $data; + } + /** * @param Collection $accounts * @param Carbon $start diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index a3c0ad1397..47aab7b635 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -259,8 +259,10 @@ class JournalCollector implements JournalCollectorInterface $this->query->where( function (EloquentBuilder $q) use ($categoryIds) { - $q->whereIn('category_transaction.category_id', $categoryIds); - $q->orWhereIn('category_transaction_journal.category_id', $categoryIds); + if (count($categoryIds) > 0) { + $q->whereIn('category_transaction.category_id', $categoryIds); + $q->orWhereIn('category_transaction_journal.category_id', $categoryIds); + } } ); @@ -383,6 +385,27 @@ class JournalCollector implements JournalCollectorInterface return $this; } + /** + * @return JournalCollectorInterface + */ + public function withBudgetInformation(): JournalCollectorInterface + { + $this->joinBudgetTables(); + + return $this; + } + + /** + * @return JournalCollectorInterface + */ + public function withCategoryInformation(): JournalCollectorInterface + { + + $this->joinCategoryTables(); + + return $this; + } + /** * @return JournalCollectorInterface */ @@ -516,6 +539,8 @@ class JournalCollector implements JournalCollectorInterface $this->joinedBudget = true; $this->query->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'); $this->query->leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'transactions.id'); + $this->fields[] = 'budget_transaction_journal.budget_id as transaction_journal_budget_id'; + $this->fields[] = 'budget_transaction.budget_id as transaction_budget_id'; } } diff --git a/app/Helpers/Collector/JournalCollectorInterface.php b/app/Helpers/Collector/JournalCollectorInterface.php index e857bdaadc..1b37f7eb81 100644 --- a/app/Helpers/Collector/JournalCollectorInterface.php +++ b/app/Helpers/Collector/JournalCollectorInterface.php @@ -131,6 +131,16 @@ interface JournalCollectorInterface */ public function setTypes(array $types): JournalCollectorInterface; + /** + * @return JournalCollectorInterface + */ + public function withBudgetInformation(): JournalCollectorInterface; + + /** + * @return JournalCollectorInterface + */ + public function withCategoryInformation(): JournalCollectorInterface; + /** * @return JournalCollectorInterface */ diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index fca6f6f678..c1c89ef897 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -17,6 +17,7 @@ use Carbon\Carbon; use ExpandedForm; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\JournalCollector; +use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Http\Requests\AccountFormRequest; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; @@ -209,7 +210,7 @@ class AccountController extends Controller * * @return View */ - public function show(AccountTaskerInterface $tasker, ARI $repository, Account $account) + public function show(JournalCollectorInterface $collector, Account $account) { if ($account->accountType->type === AccountType::INITIAL_BALANCE) { return $this->redirectToOriginalAccount($account); @@ -218,62 +219,20 @@ class AccountController extends Controller $subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type); $subTitle = $account->name; $range = Preferences::get('viewRange', '1M')->data; - /** @var Carbon $start */ - $start = session('start', Navigation::startOfPeriod(new Carbon, $range)); - /** @var Carbon $end */ - $end = session('end', Navigation::endOfPeriod(new Carbon, $range)); - $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); - $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); + $start = session('start', Navigation::startOfPeriod(new Carbon, $range)); + $end = session('end', Navigation::endOfPeriod(new Carbon, $range)); + $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); - // replace with journal collector: - $collector = new JournalCollector(auth()->user()); + // grab those journals: $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page); $journals = $collector->getPaginatedJournals(); $journals->setPath('accounts/show/' . $account->id); - // grouped other months thing: - // oldest transaction in account: - $start = $repository->oldestJournalDate($account); - $range = Preferences::get('viewRange', '1M')->data; - $start = Navigation::startOfPeriod($start, $range); - $end = Navigation::endOfX(new Carbon, $range); - $entries = new Collection; + // generate entries for each period (and cache those) + $entries = $this->periodEntries($account); - // chart properties for cache: - $cache = new CacheProperties; - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty('account-show'); - $cache->addProperty($account->id); - - - if ($cache->has()) { - $entries = $cache->get(); - Log::debug('Entries are cached, return cache.'); - - return view('accounts.show', compact('account', 'what', 'entries', 'subTitleIcon', 'journals', 'subTitle')); - } - - // only include asset accounts when this account is an asset: - $assets = new Collection; - if (in_array($account->accountType->type, [AccountType::ASSET, AccountType::DEFAULT])) { - $assets = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]); - } - Log::debug('Going to get period expenses and incomes.'); - while ($end >= $start) { - $end = Navigation::startOfPeriod($end, $range); - $currentEnd = Navigation::endOfPeriod($end, $range); - $spent = $tasker->amountOutInPeriod(new Collection([$account]), $assets, $end, $currentEnd); - $earned = $tasker->amountInInPeriod(new Collection([$account]), $assets, $end, $currentEnd); - $dateStr = $end->format('Y-m-d'); - $dateName = Navigation::periodShow($end, $range); - $entries->push([$dateStr, $dateName, $spent, $earned]); - $end = Navigation::subtractPeriod($end, $range, 1); - - } - $cache->store($entries); - - return view('accounts.show', compact('account', 'what', 'entries', 'subTitleIcon', 'journals', 'subTitle')); + return view('accounts.show', compact('account', 'what', 'entries', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end')); } /** @@ -318,7 +277,7 @@ class AccountController extends Controller $journals = $collector->getPaginatedJournals(); $journals->setPath('accounts/show/' . $account->id . '/' . $date); - return view('accounts.show_with_date', compact('category', 'date', 'account', 'journals', 'subTitle', 'carbon')); + return view('accounts.show_with_date', compact('category', 'date', 'account', 'journals', 'subTitle', 'carbon', 'start', 'end')); } /** @@ -397,6 +356,63 @@ class AccountController extends Controller return ''; } + /** + * This method returns "period entries", so nov-2015, dec-2015, etc etc (this depends on the users session range) + * and for each period, the amount of money spent and earned. This is a complex operation which is cached for + * performance reasons. + * + * @param Account $account The account involved. + * + * @return Collection + */ + private function periodEntries(Account $account): Collection + { + /** @var ARI $repository */ + $repository = app(ARI::class); + /** @var AccountTaskerInterface $tasker */ + $tasker = app(AccountTaskerInterface::class); + + $start = $repository->oldestJournalDate($account); + $range = Preferences::get('viewRange', '1M')->data; + $start = Navigation::startOfPeriod($start, $range); + $end = Navigation::endOfX(new Carbon, $range); + $entries = new Collection; + + // properties for cache + $cache = new CacheProperties; + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('account-show-period-entries'); + $cache->addProperty($account->id); + + if ($cache->has()) { + Log::debug('Entries are cached, return cache.'); + + return $cache->get(); + } + + // only include asset accounts when this account is an asset: + $assets = new Collection; + if (in_array($account->accountType->type, [AccountType::ASSET, AccountType::DEFAULT])) { + $assets = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]); + } + Log::debug('Going to get period expenses and incomes.'); + while ($end >= $start) { + $end = Navigation::startOfPeriod($end, $range); + $currentEnd = Navigation::endOfPeriod($end, $range); + $spent = $tasker->amountOutInPeriod(new Collection([$account]), $assets, $end, $currentEnd); + $earned = $tasker->amountInInPeriod(new Collection([$account]), $assets, $end, $currentEnd); + $dateStr = $end->format('Y-m-d'); + $dateName = Navigation::periodShow($end, $range); + $entries->push([$dateStr, $dateName, $spent, $earned]); + $end = Navigation::subtractPeriod($end, $range, 1); + + } + $cache->store($entries); + + return $entries; + } + /** * @param Account $account * diff --git a/app/Http/Controllers/Chart/AccountController.php b/app/Http/Controllers/Chart/AccountController.php index 4ddca05bce..1b21d83905 100644 --- a/app/Http/Controllers/Chart/AccountController.php +++ b/app/Http/Controllers/Chart/AccountController.php @@ -17,10 +17,15 @@ use Carbon\Carbon; use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Generator\Chart\Account\AccountChartGeneratorInterface; +use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; +use FireflyIII\Models\Transaction; +use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; use Log; @@ -99,6 +104,87 @@ class AccountController extends Controller return Response::json($data); } + /** + * @param JournalCollectorInterface $collector + * @param Account $account + * @param Carbon $start + * @param Carbon $end + * + * @return \Illuminate\Http\JsonResponse + */ + public function expenseByBudget(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end) + { + $cache = new CacheProperties; + $cache->addProperty($account->id); + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('expenseByBudget'); + if ($cache->has()) { + return Response::json($cache->get()); + } + + + // grab all journals: + $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withBudgetInformation()->setTypes([TransactionType::WITHDRAWAL]); + $transactions = $collector->getJournals(); + + $result = []; + /** @var Transaction $transaction */ + foreach ($transactions as $transaction) { + $jrnlBudgetId = intval($transaction->transaction_journal_budget_id); + $transBudgetId = intval($transaction->transaction_budget_id); + $budgetId = max($jrnlBudgetId, $transBudgetId); + + $result[$budgetId] = $result[$budgetId] ?? '0'; + $result[$budgetId] = bcadd($transaction->transaction_amount, $result[$budgetId]); + } + $names = $this->getBudgetNames(array_keys($result)); + $data = $this->generator->pieChart($result, $names); + $cache->store($data); + + return Response::json($data); + } + + /** + * @param JournalCollectorInterface $collector + * @param Account $account + * @param Carbon $start + * @param Carbon $end + * + * @return \Illuminate\Http\JsonResponse + */ + public function expenseByCategory(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end) + { + $cache = new CacheProperties; + $cache->addProperty($account->id); + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('expenseByCategory'); + if ($cache->has()) { + return Response::json($cache->get()); + } + + // grab all journals: + $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionType::WITHDRAWAL]); + $transactions = $collector->getJournals(); + $result = []; + /** @var Transaction $transaction */ + foreach ($transactions 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]); + } + $names = $this->getCategoryNames(array_keys($result)); + $data = $this->generator->pieChart($result, $names); + $cache->store($data); + + return Response::json($data); + + } + /** * Shows the balances for all the user's frontpage accounts. * @@ -116,6 +202,43 @@ class AccountController extends Controller return Response::json($this->accountBalanceChart($start, $end, $accounts)); } + /** + * @param Account $account + * @param Carbon $start + * @param Carbon $end + */ + public function incomeByCategory(JournalCollectorInterface $collector, Account $account, Carbon $start, Carbon $end) + { + $cache = new CacheProperties; + $cache->addProperty($account->id); + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('incomeByCategory'); + if ($cache->has()) { + return Response::json($cache->get()); + } + + // grab all journals: + $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionType::DEPOSIT]); + $transactions = $collector->getJournals(); + $result = []; + /** @var Transaction $transaction */ + foreach ($transactions 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]); + } + $names = $this->getCategoryNames(array_keys($result)); + $data = $this->generator->pieChart($result, $names); + $cache->store($data); + + return Response::json($data); + + } + /** * Shows the balances for a given set of dates and accounts. * @@ -227,7 +350,6 @@ class AccountController extends Controller return Response::json($data); } - /** * @param Account $account * @param string $date @@ -319,4 +441,51 @@ class AccountController extends Controller return $data; } + /** + * @param array $budgetIds + * + * @return array + */ + private function getBudgetNames(array $budgetIds): array + { + + /** @var BudgetRepositoryInterface $repository */ + $repository = app(BudgetRepositoryInterface::class); + $budgets = $repository->getBudgets(); + $grouped = $budgets->groupBy('id')->toArray(); + $return = []; + foreach ($budgetIds as $budgetId) { + if (isset($grouped[$budgetId])) { + $return[$budgetId] = $grouped[$budgetId][0]['name']; + } + } + $return[0] = trans('firefly.no_budget'); + + return $return; + } + + /** + * Small helper function for some of the charts. + * + * @param array $categoryIds + * + * @return array + */ + private function getCategoryNames(array $categoryIds): array + { + /** @var CategoryRepositoryInterface $repository */ + $repository = app(CategoryRepositoryInterface::class); + $categories = $repository->getCategories(); + $grouped = $categories->groupBy('id')->toArray(); + $return = []; + foreach ($categoryIds as $categoryId) { + if (isset($grouped[$categoryId])) { + $return[$categoryId] = $grouped[$categoryId][0]['name']; + } + } + $return[0] = trans('firefly.noCategory'); + + return $return; + } + } diff --git a/public/js/ff/accounts/show.js b/public/js/ff/accounts/show.js index f913802ab3..0bfb8e9a69 100644 --- a/public/js/ff/accounts/show.js +++ b/public/js/ff/accounts/show.js @@ -1,4 +1,4 @@ -/* global $, lineChart, accountID, token */ +/* global $, lineChart, accountID, token, incomeByCategoryUri, expenseByCategoryUri, expenseByBudgetUri */ // Return a helper with preserved width of cells @@ -15,10 +15,11 @@ var fixHelper = function (e, tr) { $(function () { "use strict"; - if (typeof(lineChart) === "function" && typeof accountID !== 'undefined') { - lineChart('chart/account/' + accountID, 'overview-chart'); - } + pieChart(incomeByCategoryUri, 'account-cat-in'); + pieChart(expenseByCategoryUri, 'account-cat-out'); + pieChart(expenseByBudgetUri, 'account-budget-out'); + // sortable! if (typeof $(".sortable-table tbody").sortable !== "undefined") { diff --git a/public/js/ff/accounts/show_with_date.js b/public/js/ff/accounts/show_with_date.js index 8ad1376312..c34d4ce952 100644 --- a/public/js/ff/accounts/show_with_date.js +++ b/public/js/ff/accounts/show_with_date.js @@ -6,7 +6,7 @@ * of the MIT license. See the LICENSE file for details. */ -/* global $, lineChart, dateString, accountID, token */ +/* global $, lineChart, dateString, accountID, token, incomeByCategoryUri, expenseByCategoryUri, expenseByBudgetUri */ // Return a helper with preserved width of cells @@ -23,13 +23,13 @@ var fixHelper = function (e, tr) { $(function () { "use strict"; - if (typeof(lineChart) === "function" && - typeof accountID !== 'undefined' && - typeof dateString !== 'undefined' - ) { - lineChart('chart/account/' + accountID + '/' + dateString, 'period-specific-account'); - } + lineChart('chart/account/' + accountID + '/' + dateString, 'period-specific-account'); + + pieChart(incomeByCategoryUri, 'account-cat-in'); + pieChart(expenseByCategoryUri, 'account-cat-out'); + pieChart(expenseByBudgetUri, 'account-budget-out'); + // sortable! if (typeof $(".sortable-table tbody").sortable !== "undefined") { diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 2befbc056f..665e43838f 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -87,6 +87,9 @@ return [ 'need_more_help' => 'If you need more help using Firefly III, please open a ticker on Github.', 'nothing_to_display' => 'There are no transactions to show you', 'show_all_no_filter' => 'Show all transactions without grouping them by date.', + 'expenses_by_category' => 'Expenses by category', + 'expenses_by_budget' => 'Expenses by budget', + 'income_by_category' => 'Income by category', // repeat frequencies: 'repeat_freq_yearly' => 'yearly', diff --git a/resources/views/accounts/show.twig b/resources/views/accounts/show.twig index 280f881978..28f1b770b0 100644 --- a/resources/views/accounts/show.twig +++ b/resources/views/accounts/show.twig @@ -28,7 +28,44 @@ - +
+
+
+
+

{{ 'expenses_by_category'|_ }}

+
+
+
+ +
+
+
+
+
+
+
+

{{ 'expenses_by_budget'|_ }}

+
+
+
+ +
+
+
+
+
+
+
+

{{ 'income_by_category'|_ }}

+
+
+
+ +
+
+
+
+
@@ -85,7 +122,13 @@ {% block scripts %} + diff --git a/resources/views/accounts/show_with_date.twig b/resources/views/accounts/show_with_date.twig index ae3011c52f..92b700eb21 100644 --- a/resources/views/accounts/show_with_date.twig +++ b/resources/views/accounts/show_with_date.twig @@ -32,6 +32,45 @@
+
+
+
+
+

{{ 'expenses_by_category'|_ }}

+
+
+
+ +
+
+
+
+
+
+
+

{{ 'expenses_by_budget'|_ }}

+
+
+
+ +
+
+
+
+
+
+
+

{{ 'income_by_category'|_ }}

+
+
+
+ +
+
+
+
+
+
@@ -53,6 +92,12 @@ diff --git a/routes/web.php b/routes/web.php index 58f4a070f3..4a54029b1d 100755 --- a/routes/web.php +++ b/routes/web.php @@ -190,6 +190,19 @@ Route::group( Route::get('/chart/account/{account}', ['uses' => 'Chart\AccountController@single']); Route::get('/chart/account/{account}/{date}', ['uses' => 'Chart\AccountController@specificPeriod']); + Route::get( + '/chart/account/income-by-category/{account}/{start_date}/{end_date}', + ['uses' => 'Chart\AccountController@incomeByCategory', 'as' => 'chart.account.incomeByCategory'] + ); + Route::get( + '/chart/account/expense-by-category/{account}/{start_date}/{end_date}', + ['uses' => 'Chart\AccountController@expenseByCategory', 'as' => 'chart.account.expenseByCategory'] + ); + Route::get( + '/chart/account/expense-by-budget/{account}/{start_date}/{end_date}', + ['uses' => 'Chart\AccountController@expenseByBudget', 'as' => 'chart.account.expenseByBudget'] + ); + // bills: Route::get('/chart/bill/frontpage', ['uses' => 'Chart\BillController@frontpage']); From 670fa77dd717c92b1e1b67c44336b0f75d193117 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 20 Nov 2016 18:34:49 +0100 Subject: [PATCH 139/709] New tests. --- .../Chart/AccountControllerTest.php | 38 ++++++++++++++++++- .../Controllers/TransactionControllerTest.php | 17 ++++----- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/tests/acceptance/Controllers/Chart/AccountControllerTest.php b/tests/acceptance/Controllers/Chart/AccountControllerTest.php index 3548e84e20..3a198e40e1 100644 --- a/tests/acceptance/Controllers/Chart/AccountControllerTest.php +++ b/tests/acceptance/Controllers/Chart/AccountControllerTest.php @@ -4,7 +4,7 @@ namespace Chart; use TestCase; /** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:09. + * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 17:30:36. */ class AccountControllerTest extends TestCase { @@ -31,6 +31,30 @@ class AccountControllerTest extends TestCase ); } + /** + * @covers FireflyIII\Http\Controllers\Chart\AccountController::expenseByBudget + * @todo Implement testExpenseByBudget(). + */ + public function testExpenseByBudget() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + + /** + * @covers FireflyIII\Http\Controllers\Chart\AccountController::expenseByCategory + * @todo Implement testExpenseByCategory(). + */ + public function testExpenseByCategory() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + /** * @covers FireflyIII\Http\Controllers\Chart\AccountController::frontpage * @todo Implement testFrontpage(). @@ -43,6 +67,18 @@ class AccountControllerTest extends TestCase ); } + /** + * @covers FireflyIII\Http\Controllers\Chart\AccountController::incomeByCategory + * @todo Implement testIncomeByCategory(). + */ + public function testIncomeByCategory() + { + // Remove the following lines when you implement this test. + $this->markTestIncomplete( + 'This test has not been implemented yet.' + ); + } + /** * @covers FireflyIII\Http\Controllers\Chart\AccountController::report * @todo Implement testReport(). diff --git a/tests/acceptance/Controllers/TransactionControllerTest.php b/tests/acceptance/Controllers/TransactionControllerTest.php index b1ebfe89bf..aac6f87791 100644 --- a/tests/acceptance/Controllers/TransactionControllerTest.php +++ b/tests/acceptance/Controllers/TransactionControllerTest.php @@ -7,7 +7,6 @@ class TransactionControllerTest extends TestCase { - /** * Sets up the fixture, for example, opens a network connection. @@ -18,14 +17,6 @@ class TransactionControllerTest extends TestCase parent::setUp(); } - /** - * Tears down the fixture, for example, closes a network connection. - * This method is called after a test is executed. - */ - protected function tearDown() - { - } - /** * @covers FireflyIII\Http\Controllers\TransactionController::index * @todo Implement testIndex(). @@ -85,4 +76,12 @@ class TransactionControllerTest extends TestCase 'This test has not been implemented yet.' ); } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + } } From 4b3c31a11a69d7e766f71bdc22c7afd85bf2ee99 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 20 Nov 2016 19:03:08 +0100 Subject: [PATCH 140/709] Ignore deleted transactions. [skip ci] --- app/Repositories/Account/AccountTasker.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Repositories/Account/AccountTasker.php b/app/Repositories/Account/AccountTasker.php index 1711fed6bb..f26b61a0d2 100644 --- a/app/Repositories/Account/AccountTasker.php +++ b/app/Repositories/Account/AccountTasker.php @@ -317,6 +317,7 @@ class AccountTasker implements AccountTaskerInterface ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) ->where('transaction_journals.user_id', $this->user->id) ->whereNull('transactions.deleted_at') + ->whereNull('other_side.deleted_at') ->whereNull('transaction_journals.deleted_at') ->whereIn('transactions.account_id', $accounts['accounts']) ->where('other_side.amount', '=', DB::raw('transactions.amount * -1')) From 04c59304da63ef4299d102944499641d48b7a1b2 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 20 Nov 2016 19:11:10 +0100 Subject: [PATCH 141/709] Add sorting to a report table [skip ci] --- resources/views/reports/partials/budgets.twig | 68 +++++++++++-------- 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/resources/views/reports/partials/budgets.twig b/resources/views/reports/partials/budgets.twig index d43978699e..8caff22d66 100644 --- a/resources/views/reports/partials/budgets.twig +++ b/resources/views/reports/partials/budgets.twig @@ -1,45 +1,59 @@ -
{{ trans('list.description') }}{{ trans('list.amount') }}{{ trans('list.description') }}{{ trans('list.amount') }}
+ {{ formatAmountWithCode(transaction.transaction_amount, transaction.transaction_currency_code) }} @@ -47,7 +47,7 @@
+
- - - - - - + + + + + + {% for budgetLine in budgets.getBudgetLines %} - + {% else %} + - + {% endif %} + + + {% if budgetLine.getRepetition.id %} + - + {% else %} + + {% endif %} + + + {% if budgetLine.getRepetition.id %} + + {% else %} + - + {% endif %} + + - - ",e.document[0]).appendTo(n)):"tr"===s?e._createTrPlaceholder(e.currentItem,n):"img"===s&&n.attr("src",e.currentItem.attr("src")),i||n.css("visibility","hidden"),n},update:function(t,n){(!i||s.forcePlaceholderSize)&&(n.height()||n.height(e.currentItem.innerHeight()-parseInt(e.currentItem.css("paddingTop")||0,10)-parseInt(e.currentItem.css("paddingBottom")||0,10)),n.width()||n.width(e.currentItem.innerWidth()-parseInt(e.currentItem.css("paddingLeft")||0,10)-parseInt(e.currentItem.css("paddingRight")||0,10)))}}),e.placeholder=t(s.placeholder.element.call(e.element,e.currentItem)),e.currentItem.after(e.placeholder),s.placeholder.update(e,e.placeholder)},_createTrPlaceholder:function(e,i){var s=this;e.children().each(function(){t("",s.document[0]).attr("colspan",t(this).attr("colspan")||1).appendTo(i)})},_contactContainers:function(e){var i,s,n,o,a,r,h,l,c,u,d=null,p=null;for(i=this.containers.length-1;i>=0;i--)if(!t.contains(this.currentItem[0],this.containers[i].element[0]))if(this._intersectsWith(this.containers[i].containerCache)){if(d&&t.contains(this.containers[i].element[0],d.element[0]))continue;d=this.containers[i],p=i}else this.containers[i].containerCache.over&&(this.containers[i]._trigger("out",e,this._uiHash(this)),this.containers[i].containerCache.over=0);if(d)if(1===this.containers.length)this.containers[p].containerCache.over||(this.containers[p]._trigger("over",e,this._uiHash(this)),this.containers[p].containerCache.over=1);else{for(n=1e4,o=null,c=d.floating||this._isFloating(this.currentItem),a=c?"left":"top",r=c?"width":"height",u=c?"pageX":"pageY",s=this.items.length-1;s>=0;s--)t.contains(this.containers[p].element[0],this.items[s].item[0])&&this.items[s].item[0]!==this.currentItem[0]&&(h=this.items[s].item.offset()[a],l=!1,e[u]-h>this.items[s][r]/2&&(l=!0),n>Math.abs(e[u]-h)&&(n=Math.abs(e[u]-h),o=this.items[s],this.direction=l?"up":"down"));if(!o&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[p])return this.currentContainer.containerCache.over||(this.containers[p]._trigger("over",e,this._uiHash()),this.currentContainer.containerCache.over=1),void 0;o?this._rearrange(e,o,null,!0):this._rearrange(e,null,this.containers[p].element,!0),this._trigger("change",e,this._uiHash()),this.containers[p]._trigger("change",e,this._uiHash(this)),this.currentContainer=this.containers[p],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[p]._trigger("over",e,this._uiHash(this)),this.containers[p].containerCache.over=1}},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper)?t(i.helper.apply(this.element[0],[e,this.currentItem])):"clone"===i.helper?this.currentItem.clone():this.currentItem;return s.parents("body").length||t("parent"!==i.appendTo?i.appendTo:this.currentItem[0].parentNode)[0].appendChild(s[0]),s[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!s[0].style.width||i.forceHelperSize)&&s.width(this.currentItem.width()),(!s[0].style.height||i.forceHelperSize)&&s.height(this.currentItem.height()),s},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var e=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===this.document[0].body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&t.ui.ie)&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var t=this.currentItem.position();return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:t.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options;"parent"===n.containment&&(n.containment=this.helper[0].parentNode),("document"===n.containment||"window"===n.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,"document"===n.containment?this.document.width():this.window.width()-this.helperProportions.width-this.margins.left,("document"===n.containment?this.document.height()||document.body.parentNode.scrollHeight:this.window.height()||this.document[0].body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(n.containment)||(e=t(n.containment)[0],i=t(n.containment).offset(),s="hidden"!==t(e).css("overflow"),this.containment=[i.left+(parseInt(t(e).css("borderLeftWidth"),10)||0)+(parseInt(t(e).css("paddingLeft"),10)||0)-this.margins.left,i.top+(parseInt(t(e).css("borderTopWidth"),10)||0)+(parseInt(t(e).css("paddingTop"),10)||0)-this.margins.top,i.left+(s?Math.max(e.scrollWidth,e.offsetWidth):e.offsetWidth)-(parseInt(t(e).css("borderLeftWidth"),10)||0)-(parseInt(t(e).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,i.top+(s?Math.max(e.scrollHeight,e.offsetHeight):e.offsetHeight)-(parseInt(t(e).css("borderTopWidth"),10)||0)-(parseInt(t(e).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(e,i){i||(i=this.position);var s="absolute"===e?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,o=/(html|body)/i.test(n[0].tagName);return{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():o?0:n.scrollTop())*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():o?0:n.scrollLeft())*s}},_generatePosition:function(e){var i,s,n=this.options,o=e.pageX,a=e.pageY,r="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=/(html|body)/i.test(r[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(e.pageX-this.offset.click.leftthis.containment[2]&&(o=this.containment[2]+this.offset.click.left),e.pageY-this.offset.click.top>this.containment[3]&&(a=this.containment[3]+this.offset.click.top)),n.grid&&(i=this.originalPageY+Math.round((a-this.originalPageY)/n.grid[1])*n.grid[1],a=this.containment?i-this.offset.click.top>=this.containment[1]&&i-this.offset.click.top<=this.containment[3]?i:i-this.offset.click.top>=this.containment[1]?i-n.grid[1]:i+n.grid[1]:i,s=this.originalPageX+Math.round((o-this.originalPageX)/n.grid[0])*n.grid[0],o=this.containment?s-this.offset.click.left>=this.containment[0]&&s-this.offset.click.left<=this.containment[2]?s:s-this.offset.click.left>=this.containment[0]?s-n.grid[0]:s+n.grid[0]:s)),{top:a-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():h?0:r.scrollTop()),left:o-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():h?0:r.scrollLeft())}},_rearrange:function(t,e,i,s){i?i[0].appendChild(this.placeholder[0]):e.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?e.item[0]:e.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter; -this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(t,e){function i(t,e,i){return function(s){i._trigger(t,s,e._uiHash(e))}}this.reverting=!1;var s,n=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(s in this._storedCSS)("auto"===this._storedCSS[s]||"static"===this._storedCSS[s])&&(this._storedCSS[s]="");this.currentItem.css(this._storedCSS),this._removeClass(this.currentItem,"ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!e&&n.push(function(t){this._trigger("receive",t,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||e||n.push(function(t){this._trigger("update",t,this._uiHash())}),this!==this.currentContainer&&(e||(n.push(function(t){this._trigger("remove",t,this._uiHash())}),n.push(function(t){return function(e){t._trigger("receive",e,this._uiHash(this))}}.call(this,this.currentContainer)),n.push(function(t){return function(e){t._trigger("update",e,this._uiHash(this))}}.call(this,this.currentContainer)))),s=this.containers.length-1;s>=0;s--)e||n.push(i("deactivate",this,this.containers[s])),this.containers[s].containerCache.over&&(n.push(i("out",this,this.containers[s])),this.containers[s].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,e||this._trigger("beforeStop",t,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.cancelHelperRemoval||(this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null),!e){for(s=0;n.length>s;s++)n[s].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!this.cancelHelperRemoval},_trigger:function(){t.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(e){var i=e||this;return{helper:i.helper,placeholder:i.placeholder||t([]),position:i.position,originalPosition:i.originalPosition,offset:i.positionAbs,item:i.currentItem,sender:e?e.element:null}}}),t.widget("ui.spinner",{version:"1.12.1",defaultElement:"",widgetEventPrefix:"spin",options:{classes:{"ui-spinner":"ui-corner-all","ui-spinner-down":"ui-corner-br","ui-spinner-up":"ui-corner-tr"},culture:null,icons:{down:"ui-icon-triangle-1-s",up:"ui-icon-triangle-1-n"},incremental:!0,max:null,min:null,numberFormat:null,page:10,step:1,change:null,spin:null,start:null,stop:null},_create:function(){this._setOption("max",this.options.max),this._setOption("min",this.options.min),this._setOption("step",this.options.step),""!==this.value()&&this._value(this.element.val(),!0),this._draw(),this._on(this._events),this._refresh(),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_getCreateOptions:function(){var e=this._super(),i=this.element;return t.each(["min","max","step"],function(t,s){var n=i.attr(s);null!=n&&n.length&&(e[s]=n)}),e},_events:{keydown:function(t){this._start(t)&&this._keydown(t)&&t.preventDefault()},keyup:"_stop",focus:function(){this.previous=this.element.val()},blur:function(t){return this.cancelBlur?(delete this.cancelBlur,void 0):(this._stop(),this._refresh(),this.previous!==this.element.val()&&this._trigger("change",t),void 0)},mousewheel:function(t,e){if(e){if(!this.spinning&&!this._start(t))return!1;this._spin((e>0?1:-1)*this.options.step,t),clearTimeout(this.mousewheelTimer),this.mousewheelTimer=this._delay(function(){this.spinning&&this._stop(t)},100),t.preventDefault()}},"mousedown .ui-spinner-button":function(e){function i(){var e=this.element[0]===t.ui.safeActiveElement(this.document[0]);e||(this.element.trigger("focus"),this.previous=s,this._delay(function(){this.previous=s}))}var s;s=this.element[0]===t.ui.safeActiveElement(this.document[0])?this.previous:this.element.val(),e.preventDefault(),i.call(this),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur,i.call(this)}),this._start(e)!==!1&&this._repeat(null,t(e.currentTarget).hasClass("ui-spinner-up")?1:-1,e)},"mouseup .ui-spinner-button":"_stop","mouseenter .ui-spinner-button":function(e){return t(e.currentTarget).hasClass("ui-state-active")?this._start(e)===!1?!1:(this._repeat(null,t(e.currentTarget).hasClass("ui-spinner-up")?1:-1,e),void 0):void 0},"mouseleave .ui-spinner-button":"_stop"},_enhance:function(){this.uiSpinner=this.element.attr("autocomplete","off").wrap("").parent().append("")},_draw:function(){this._enhance(),this._addClass(this.uiSpinner,"ui-spinner","ui-widget ui-widget-content"),this._addClass("ui-spinner-input"),this.element.attr("role","spinbutton"),this.buttons=this.uiSpinner.children("a").attr("tabIndex",-1).attr("aria-hidden",!0).button({classes:{"ui-button":""}}),this._removeClass(this.buttons,"ui-corner-all"),this._addClass(this.buttons.first(),"ui-spinner-button ui-spinner-up"),this._addClass(this.buttons.last(),"ui-spinner-button ui-spinner-down"),this.buttons.first().button({icon:this.options.icons.up,showLabel:!1}),this.buttons.last().button({icon:this.options.icons.down,showLabel:!1}),this.buttons.height()>Math.ceil(.5*this.uiSpinner.height())&&this.uiSpinner.height()>0&&this.uiSpinner.height(this.uiSpinner.height())},_keydown:function(e){var i=this.options,s=t.ui.keyCode;switch(e.keyCode){case s.UP:return this._repeat(null,1,e),!0;case s.DOWN:return this._repeat(null,-1,e),!0;case s.PAGE_UP:return this._repeat(null,i.page,e),!0;case s.PAGE_DOWN:return this._repeat(null,-i.page,e),!0}return!1},_start:function(t){return this.spinning||this._trigger("start",t)!==!1?(this.counter||(this.counter=1),this.spinning=!0,!0):!1},_repeat:function(t,e,i){t=t||500,clearTimeout(this.timer),this.timer=this._delay(function(){this._repeat(40,e,i)},t),this._spin(e*this.options.step,i)},_spin:function(t,e){var i=this.value()||0;this.counter||(this.counter=1),i=this._adjustValue(i+t*this._increment(this.counter)),this.spinning&&this._trigger("spin",e,{value:i})===!1||(this._value(i),this.counter++)},_increment:function(e){var i=this.options.incremental;return i?t.isFunction(i)?i(e):Math.floor(e*e*e/5e4-e*e/500+17*e/200+1):1},_precision:function(){var t=this._precisionOf(this.options.step);return null!==this.options.min&&(t=Math.max(t,this._precisionOf(this.options.min))),t},_precisionOf:function(t){var e=""+t,i=e.indexOf(".");return-1===i?0:e.length-i-1},_adjustValue:function(t){var e,i,s=this.options;return e=null!==s.min?s.min:0,i=t-e,i=Math.round(i/s.step)*s.step,t=e+i,t=parseFloat(t.toFixed(this._precision())),null!==s.max&&t>s.max?s.max:null!==s.min&&s.min>t?s.min:t},_stop:function(t){this.spinning&&(clearTimeout(this.timer),clearTimeout(this.mousewheelTimer),this.counter=0,this.spinning=!1,this._trigger("stop",t))},_setOption:function(t,e){var i,s,n;return"culture"===t||"numberFormat"===t?(i=this._parse(this.element.val()),this.options[t]=e,this.element.val(this._format(i)),void 0):(("max"===t||"min"===t||"step"===t)&&"string"==typeof e&&(e=this._parse(e)),"icons"===t&&(s=this.buttons.first().find(".ui-icon"),this._removeClass(s,null,this.options.icons.up),this._addClass(s,null,e.up),n=this.buttons.last().find(".ui-icon"),this._removeClass(n,null,this.options.icons.down),this._addClass(n,null,e.down)),this._super(t,e),void 0)},_setOptionDisabled:function(t){this._super(t),this._toggleClass(this.uiSpinner,null,"ui-state-disabled",!!t),this.element.prop("disabled",!!t),this.buttons.button(t?"disable":"enable")},_setOptions:r(function(t){this._super(t)}),_parse:function(t){return"string"==typeof t&&""!==t&&(t=window.Globalize&&this.options.numberFormat?Globalize.parseFloat(t,10,this.options.culture):+t),""===t||isNaN(t)?null:t},_format:function(t){return""===t?"":window.Globalize&&this.options.numberFormat?Globalize.format(t,this.options.numberFormat,this.options.culture):t},_refresh:function(){this.element.attr({"aria-valuemin":this.options.min,"aria-valuemax":this.options.max,"aria-valuenow":this._parse(this.element.val())})},isValid:function(){var t=this.value();return null===t?!1:t===this._adjustValue(t)},_value:function(t,e){var i;""!==t&&(i=this._parse(t),null!==i&&(e||(i=this._adjustValue(i)),t=this._format(i))),this.element.val(t),this._refresh()},_destroy:function(){this.element.prop("disabled",!1).removeAttr("autocomplete role aria-valuemin aria-valuemax aria-valuenow"),this.uiSpinner.replaceWith(this.element)},stepUp:r(function(t){this._stepUp(t)}),_stepUp:function(t){this._start()&&(this._spin((t||1)*this.options.step),this._stop())},stepDown:r(function(t){this._stepDown(t)}),_stepDown:function(t){this._start()&&(this._spin((t||1)*-this.options.step),this._stop())},pageUp:r(function(t){this._stepUp((t||1)*this.options.page)}),pageDown:r(function(t){this._stepDown((t||1)*this.options.page)}),value:function(t){return arguments.length?(r(this._value).call(this,t),void 0):this._parse(this.element.val())},widget:function(){return this.uiSpinner}}),t.uiBackCompat!==!1&&t.widget("ui.spinner",t.ui.spinner,{_enhance:function(){this.uiSpinner=this.element.attr("autocomplete","off").wrap(this._uiSpinnerHtml()).parent().append(this._buttonHtml())},_uiSpinnerHtml:function(){return""},_buttonHtml:function(){return""}}),t.ui.spinner,t.widget("ui.tabs",{version:"1.12.1",delay:300,options:{active:null,classes:{"ui-tabs":"ui-corner-all","ui-tabs-nav":"ui-corner-all","ui-tabs-panel":"ui-corner-bottom","ui-tabs-tab":"ui-corner-top"},collapsible:!1,event:"click",heightStyle:"content",hide:null,show:null,activate:null,beforeActivate:null,beforeLoad:null,load:null},_isLocal:function(){var t=/#.*$/;return function(e){var i,s;i=e.href.replace(t,""),s=location.href.replace(t,"");try{i=decodeURIComponent(i)}catch(n){}try{s=decodeURIComponent(s)}catch(n){}return e.hash.length>1&&i===s}}(),_create:function(){var e=this,i=this.options;this.running=!1,this._addClass("ui-tabs","ui-widget ui-widget-content"),this._toggleClass("ui-tabs-collapsible",null,i.collapsible),this._processTabs(),i.active=this._initialActive(),t.isArray(i.disabled)&&(i.disabled=t.unique(i.disabled.concat(t.map(this.tabs.filter(".ui-state-disabled"),function(t){return e.tabs.index(t)}))).sort()),this.active=this.options.active!==!1&&this.anchors.length?this._findActive(i.active):t(),this._refresh(),this.active.length&&this.load(i.active)},_initialActive:function(){var e=this.options.active,i=this.options.collapsible,s=location.hash.substring(1);return null===e&&(s&&this.tabs.each(function(i,n){return t(n).attr("aria-controls")===s?(e=i,!1):void 0}),null===e&&(e=this.tabs.index(this.tabs.filter(".ui-tabs-active"))),(null===e||-1===e)&&(e=this.tabs.length?0:!1)),e!==!1&&(e=this.tabs.index(this.tabs.eq(e)),-1===e&&(e=i?!1:0)),!i&&e===!1&&this.anchors.length&&(e=0),e},_getCreateEventData:function(){return{tab:this.active,panel:this.active.length?this._getPanelForTab(this.active):t()}},_tabKeydown:function(e){var i=t(t.ui.safeActiveElement(this.document[0])).closest("li"),s=this.tabs.index(i),n=!0;if(!this._handlePageNav(e)){switch(e.keyCode){case t.ui.keyCode.RIGHT:case t.ui.keyCode.DOWN:s++;break;case t.ui.keyCode.UP:case t.ui.keyCode.LEFT:n=!1,s--;break;case t.ui.keyCode.END:s=this.anchors.length-1;break;case t.ui.keyCode.HOME:s=0;break;case t.ui.keyCode.SPACE:return e.preventDefault(),clearTimeout(this.activating),this._activate(s),void 0;case t.ui.keyCode.ENTER:return e.preventDefault(),clearTimeout(this.activating),this._activate(s===this.options.active?!1:s),void 0;default:return}e.preventDefault(),clearTimeout(this.activating),s=this._focusNextTab(s,n),e.ctrlKey||e.metaKey||(i.attr("aria-selected","false"),this.tabs.eq(s).attr("aria-selected","true"),this.activating=this._delay(function(){this.option("active",s)},this.delay))}},_panelKeydown:function(e){this._handlePageNav(e)||e.ctrlKey&&e.keyCode===t.ui.keyCode.UP&&(e.preventDefault(),this.active.trigger("focus"))},_handlePageNav:function(e){return e.altKey&&e.keyCode===t.ui.keyCode.PAGE_UP?(this._activate(this._focusNextTab(this.options.active-1,!1)),!0):e.altKey&&e.keyCode===t.ui.keyCode.PAGE_DOWN?(this._activate(this._focusNextTab(this.options.active+1,!0)),!0):void 0},_findNextTab:function(e,i){function s(){return e>n&&(e=0),0>e&&(e=n),e}for(var n=this.tabs.length-1;-1!==t.inArray(s(),this.options.disabled);)e=i?e+1:e-1;return e},_focusNextTab:function(t,e){return t=this._findNextTab(t,e),this.tabs.eq(t).trigger("focus"),t},_setOption:function(t,e){return"active"===t?(this._activate(e),void 0):(this._super(t,e),"collapsible"===t&&(this._toggleClass("ui-tabs-collapsible",null,e),e||this.options.active!==!1||this._activate(0)),"event"===t&&this._setupEvents(e),"heightStyle"===t&&this._setupHeightStyle(e),void 0)},_sanitizeSelector:function(t){return t?t.replace(/[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g,"\\$&"):""},refresh:function(){var e=this.options,i=this.tablist.children(":has(a[href])");e.disabled=t.map(i.filter(".ui-state-disabled"),function(t){return i.index(t)}),this._processTabs(),e.active!==!1&&this.anchors.length?this.active.length&&!t.contains(this.tablist[0],this.active[0])?this.tabs.length===e.disabled.length?(e.active=!1,this.active=t()):this._activate(this._findNextTab(Math.max(0,e.active-1),!1)):e.active=this.tabs.index(this.active):(e.active=!1,this.active=t()),this._refresh()},_refresh:function(){this._setOptionDisabled(this.options.disabled),this._setupEvents(this.options.event),this._setupHeightStyle(this.options.heightStyle),this.tabs.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}),this.panels.not(this._getPanelForTab(this.active)).hide().attr({"aria-hidden":"true"}),this.active.length?(this.active.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}),this._addClass(this.active,"ui-tabs-active","ui-state-active"),this._getPanelForTab(this.active).show().attr({"aria-hidden":"false"})):this.tabs.eq(0).attr("tabIndex",0)},_processTabs:function(){var e=this,i=this.tabs,s=this.anchors,n=this.panels;this.tablist=this._getList().attr("role","tablist"),this._addClass(this.tablist,"ui-tabs-nav","ui-helper-reset ui-helper-clearfix ui-widget-header"),this.tablist.on("mousedown"+this.eventNamespace,"> li",function(e){t(this).is(".ui-state-disabled")&&e.preventDefault()}).on("focus"+this.eventNamespace,".ui-tabs-anchor",function(){t(this).closest("li").is(".ui-state-disabled")&&this.blur()}),this.tabs=this.tablist.find("> li:has(a[href])").attr({role:"tab",tabIndex:-1}),this._addClass(this.tabs,"ui-tabs-tab","ui-state-default"),this.anchors=this.tabs.map(function(){return t("a",this)[0]}).attr({role:"presentation",tabIndex:-1}),this._addClass(this.anchors,"ui-tabs-anchor"),this.panels=t(),this.anchors.each(function(i,s){var n,o,a,r=t(s).uniqueId().attr("id"),h=t(s).closest("li"),l=h.attr("aria-controls");e._isLocal(s)?(n=s.hash,a=n.substring(1),o=e.element.find(e._sanitizeSelector(n))):(a=h.attr("aria-controls")||t({}).uniqueId()[0].id,n="#"+a,o=e.element.find(n),o.length||(o=e._createPanel(a),o.insertAfter(e.panels[i-1]||e.tablist)),o.attr("aria-live","polite")),o.length&&(e.panels=e.panels.add(o)),l&&h.data("ui-tabs-aria-controls",l),h.attr({"aria-controls":a,"aria-labelledby":r}),o.attr("aria-labelledby",r)}),this.panels.attr("role","tabpanel"),this._addClass(this.panels,"ui-tabs-panel","ui-widget-content"),i&&(this._off(i.not(this.tabs)),this._off(s.not(this.anchors)),this._off(n.not(this.panels)))},_getList:function(){return this.tablist||this.element.find("ol, ul").eq(0)},_createPanel:function(e){return t("
").attr("id",e).data("ui-tabs-destroy",!0)},_setOptionDisabled:function(e){var i,s,n;for(t.isArray(e)&&(e.length?e.length===this.anchors.length&&(e=!0):e=!1),n=0;s=this.tabs[n];n++)i=t(s),e===!0||-1!==t.inArray(n,e)?(i.attr("aria-disabled","true"),this._addClass(i,null,"ui-state-disabled")):(i.removeAttr("aria-disabled"),this._removeClass(i,null,"ui-state-disabled"));this.options.disabled=e,this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,e===!0)},_setupEvents:function(e){var i={};e&&t.each(e.split(" "),function(t,e){i[e]="_eventHandler"}),this._off(this.anchors.add(this.tabs).add(this.panels)),this._on(!0,this.anchors,{click:function(t){t.preventDefault()}}),this._on(this.anchors,i),this._on(this.tabs,{keydown:"_tabKeydown"}),this._on(this.panels,{keydown:"_panelKeydown"}),this._focusable(this.tabs),this._hoverable(this.tabs)},_setupHeightStyle:function(e){var i,s=this.element.parent();"fill"===e?(i=s.height(),i-=this.element.outerHeight()-this.element.height(),this.element.siblings(":visible").each(function(){var e=t(this),s=e.css("position");"absolute"!==s&&"fixed"!==s&&(i-=e.outerHeight(!0))}),this.element.children().not(this.panels).each(function(){i-=t(this).outerHeight(!0)}),this.panels.each(function(){t(this).height(Math.max(0,i-t(this).innerHeight()+t(this).height()))}).css("overflow","auto")):"auto"===e&&(i=0,this.panels.each(function(){i=Math.max(i,t(this).height("").height())}).height(i))},_eventHandler:function(e){var i=this.options,s=this.active,n=t(e.currentTarget),o=n.closest("li"),a=o[0]===s[0],r=a&&i.collapsible,h=r?t():this._getPanelForTab(o),l=s.length?this._getPanelForTab(s):t(),c={oldTab:s,oldPanel:l,newTab:r?t():o,newPanel:h};e.preventDefault(),o.hasClass("ui-state-disabled")||o.hasClass("ui-tabs-loading")||this.running||a&&!i.collapsible||this._trigger("beforeActivate",e,c)===!1||(i.active=r?!1:this.tabs.index(o),this.active=a?t():o,this.xhr&&this.xhr.abort(),l.length||h.length||t.error("jQuery UI Tabs: Mismatching fragment identifier."),h.length&&this.load(this.tabs.index(o),e),this._toggle(e,c))},_toggle:function(e,i){function s(){o.running=!1,o._trigger("activate",e,i)}function n(){o._addClass(i.newTab.closest("li"),"ui-tabs-active","ui-state-active"),a.length&&o.options.show?o._show(a,o.options.show,s):(a.show(),s())}var o=this,a=i.newPanel,r=i.oldPanel;this.running=!0,r.length&&this.options.hide?this._hide(r,this.options.hide,function(){o._removeClass(i.oldTab.closest("li"),"ui-tabs-active","ui-state-active"),n()}):(this._removeClass(i.oldTab.closest("li"),"ui-tabs-active","ui-state-active"),r.hide(),n()),r.attr("aria-hidden","true"),i.oldTab.attr({"aria-selected":"false","aria-expanded":"false"}),a.length&&r.length?i.oldTab.attr("tabIndex",-1):a.length&&this.tabs.filter(function(){return 0===t(this).attr("tabIndex")}).attr("tabIndex",-1),a.attr("aria-hidden","false"),i.newTab.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0})},_activate:function(e){var i,s=this._findActive(e);s[0]!==this.active[0]&&(s.length||(s=this.active),i=s.find(".ui-tabs-anchor")[0],this._eventHandler({target:i,currentTarget:i,preventDefault:t.noop}))},_findActive:function(e){return e===!1?t():this.tabs.eq(e)},_getIndex:function(e){return"string"==typeof e&&(e=this.anchors.index(this.anchors.filter("[href$='"+t.ui.escapeSelector(e)+"']"))),e},_destroy:function(){this.xhr&&this.xhr.abort(),this.tablist.removeAttr("role").off(this.eventNamespace),this.anchors.removeAttr("role tabIndex").removeUniqueId(),this.tabs.add(this.panels).each(function(){t.data(this,"ui-tabs-destroy")?t(this).remove():t(this).removeAttr("role tabIndex aria-live aria-busy aria-selected aria-labelledby aria-hidden aria-expanded")}),this.tabs.each(function(){var e=t(this),i=e.data("ui-tabs-aria-controls");i?e.attr("aria-controls",i).removeData("ui-tabs-aria-controls"):e.removeAttr("aria-controls")}),this.panels.show(),"content"!==this.options.heightStyle&&this.panels.css("height","")},enable:function(e){var i=this.options.disabled;i!==!1&&(void 0===e?i=!1:(e=this._getIndex(e),i=t.isArray(i)?t.map(i,function(t){return t!==e?t:null}):t.map(this.tabs,function(t,i){return i!==e?i:null})),this._setOptionDisabled(i))},disable:function(e){var i=this.options.disabled;if(i!==!0){if(void 0===e)i=!0;else{if(e=this._getIndex(e),-1!==t.inArray(e,i))return;i=t.isArray(i)?t.merge([e],i).sort():[e]}this._setOptionDisabled(i)}},load:function(e,i){e=this._getIndex(e);var s=this,n=this.tabs.eq(e),o=n.find(".ui-tabs-anchor"),a=this._getPanelForTab(n),r={tab:n,panel:a},h=function(t,e){"abort"===e&&s.panels.stop(!1,!0),s._removeClass(n,"ui-tabs-loading"),a.removeAttr("aria-busy"),t===s.xhr&&delete s.xhr};this._isLocal(o[0])||(this.xhr=t.ajax(this._ajaxSettings(o,i,r)),this.xhr&&"canceled"!==this.xhr.statusText&&(this._addClass(n,"ui-tabs-loading"),a.attr("aria-busy","true"),this.xhr.done(function(t,e,n){setTimeout(function(){a.html(t),s._trigger("load",i,r),h(n,e)},1)}).fail(function(t,e){setTimeout(function(){h(t,e)},1)})))},_ajaxSettings:function(e,i,s){var n=this;return{url:e.attr("href").replace(/#.*$/,""),beforeSend:function(e,o){return n._trigger("beforeLoad",i,t.extend({jqXHR:e,ajaxSettings:o},s))}}},_getPanelForTab:function(e){var i=t(e).attr("aria-controls");return this.element.find(this._sanitizeSelector("#"+i))}}),t.uiBackCompat!==!1&&t.widget("ui.tabs",t.ui.tabs,{_processTabs:function(){this._superApply(arguments),this._addClass(this.tabs,"ui-tab")}}),t.ui.tabs,t.widget("ui.tooltip",{version:"1.12.1",options:{classes:{"ui-tooltip":"ui-corner-all ui-widget-shadow"},content:function(){var e=t(this).attr("title")||"";return t("").text(e).html()},hide:!0,items:"[title]:not([disabled])",position:{my:"left top+15",at:"left bottom",collision:"flipfit flip"},show:!0,track:!1,close:null,open:null},_addDescribedBy:function(e,i){var s=(e.attr("aria-describedby")||"").split(/\s+/);s.push(i),e.data("ui-tooltip-id",i).attr("aria-describedby",t.trim(s.join(" ")))},_removeDescribedBy:function(e){var i=e.data("ui-tooltip-id"),s=(e.attr("aria-describedby")||"").split(/\s+/),n=t.inArray(i,s);-1!==n&&s.splice(n,1),e.removeData("ui-tooltip-id"),s=t.trim(s.join(" ")),s?e.attr("aria-describedby",s):e.removeAttr("aria-describedby")},_create:function(){this._on({mouseover:"open",focusin:"open"}),this.tooltips={},this.parents={},this.liveRegion=t("
").attr({role:"log","aria-live":"assertive","aria-relevant":"additions"}).appendTo(this.document[0].body),this._addClass(this.liveRegion,null,"ui-helper-hidden-accessible"),this.disabledTitles=t([])},_setOption:function(e,i){var s=this;this._super(e,i),"content"===e&&t.each(this.tooltips,function(t,e){s._updateContent(e.element)})},_setOptionDisabled:function(t){this[t?"_disable":"_enable"]()},_disable:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event("blur");n.target=n.currentTarget=s.element[0],e.close(n,!0)}),this.disabledTitles=this.disabledTitles.add(this.element.find(this.options.items).addBack().filter(function(){var e=t(this);return e.is("[title]")?e.data("ui-tooltip-title",e.attr("title")).removeAttr("title"):void 0}))},_enable:function(){this.disabledTitles.each(function(){var e=t(this);e.data("ui-tooltip-title")&&e.attr("title",e.data("ui-tooltip-title"))}),this.disabledTitles=t([])},open:function(e){var i=this,s=t(e?e.target:this.element).closest(this.options.items);s.length&&!s.data("ui-tooltip-id")&&(s.attr("title")&&s.data("ui-tooltip-title",s.attr("title")),s.data("ui-tooltip-open",!0),e&&"mouseover"===e.type&&s.parents().each(function(){var e,s=t(this);s.data("ui-tooltip-open")&&(e=t.Event("blur"),e.target=e.currentTarget=this,i.close(e,!0)),s.attr("title")&&(s.uniqueId(),i.parents[this.id]={element:this,title:s.attr("title")},s.attr("title",""))}),this._registerCloseHandlers(e,s),this._updateContent(s,e))},_updateContent:function(t,e){var i,s=this.options.content,n=this,o=e?e.type:null;return"string"==typeof s||s.nodeType||s.jquery?this._open(e,t,s):(i=s.call(t[0],function(i){n._delay(function(){t.data("ui-tooltip-open")&&(e&&(e.type=o),this._open(e,t,i))})}),i&&this._open(e,t,i),void 0)},_open:function(e,i,s){function n(t){l.of=t,a.is(":hidden")||a.position(l)}var o,a,r,h,l=t.extend({},this.options.position);if(s){if(o=this._find(i))return o.tooltip.find(".ui-tooltip-content").html(s),void 0;i.is("[title]")&&(e&&"mouseover"===e.type?i.attr("title",""):i.removeAttr("title")),o=this._tooltip(i),a=o.tooltip,this._addDescribedBy(i,a.attr("id")),a.find(".ui-tooltip-content").html(s),this.liveRegion.children().hide(),h=t("
").html(a.find(".ui-tooltip-content").html()),h.removeAttr("name").find("[name]").removeAttr("name"),h.removeAttr("id").find("[id]").removeAttr("id"),h.appendTo(this.liveRegion),this.options.track&&e&&/^mouse/.test(e.type)?(this._on(this.document,{mousemove:n}),n(e)):a.position(t.extend({of:i},this.options.position)),a.hide(),this._show(a,this.options.show),this.options.track&&this.options.show&&this.options.show.delay&&(r=this.delayedShow=setInterval(function(){a.is(":visible")&&(n(l.of),clearInterval(r))},t.fx.interval)),this._trigger("open",e,{tooltip:a})}},_registerCloseHandlers:function(e,i){var s={keyup:function(e){if(e.keyCode===t.ui.keyCode.ESCAPE){var s=t.Event(e);s.currentTarget=i[0],this.close(s,!0)}}};i[0]!==this.element[0]&&(s.remove=function(){this._removeTooltip(this._find(i).tooltip)}),e&&"mouseover"!==e.type||(s.mouseleave="close"),e&&"focusin"!==e.type||(s.focusout="close"),this._on(!0,i,s)},close:function(e){var i,s=this,n=t(e?e.currentTarget:this.element),o=this._find(n);return o?(i=o.tooltip,o.closing||(clearInterval(this.delayedShow),n.data("ui-tooltip-title")&&!n.attr("title")&&n.attr("title",n.data("ui-tooltip-title")),this._removeDescribedBy(n),o.hiding=!0,i.stop(!0),this._hide(i,this.options.hide,function(){s._removeTooltip(t(this))}),n.removeData("ui-tooltip-open"),this._off(n,"mouseleave focusout keyup"),n[0]!==this.element[0]&&this._off(n,"remove"),this._off(this.document,"mousemove"),e&&"mouseleave"===e.type&&t.each(this.parents,function(e,i){t(i.element).attr("title",i.title),delete s.parents[e]}),o.closing=!0,this._trigger("close",e,{tooltip:i}),o.hiding||(o.closing=!1)),void 0):(n.removeData("ui-tooltip-open"),void 0)},_tooltip:function(e){var i=t("
").attr("role","tooltip"),s=t("
").appendTo(i),n=i.uniqueId().attr("id");return this._addClass(s,"ui-tooltip-content"),this._addClass(i,"ui-tooltip","ui-widget ui-widget-content"),i.appendTo(this._appendTo(e)),this.tooltips[n]={element:e,tooltip:i}},_find:function(t){var e=t.data("ui-tooltip-id");return e?this.tooltips[e]:null},_removeTooltip:function(t){t.remove(),delete this.tooltips[t.attr("id")]},_appendTo:function(t){var e=t.closest(".ui-front, dialog");return e.length||(e=this.document[0].body),e},_destroy:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event("blur"),o=s.element;n.target=n.currentTarget=o[0],e.close(n,!0),t("#"+i).remove(),o.data("ui-tooltip-title")&&(o.attr("title")||o.attr("title",o.data("ui-tooltip-title")),o.removeData("ui-tooltip-title"))}),this.liveRegion.remove()}}),t.uiBackCompat!==!1&&t.widget("ui.tooltip",t.ui.tooltip,{options:{tooltipClass:null},_tooltip:function(){var t=this._superApply(arguments);return this.options.tooltipClass&&t.tooltip.addClass(this.options.tooltipClass),t}}),t.ui.tooltip}); \ No newline at end of file +(function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)})(function(t){t.ui=t.ui||{},t.ui.version="1.12.1";var e=0,i=Array.prototype.slice;t.cleanData=function(e){return function(i){var s,n,o;for(o=0;null!=(n=i[o]);o++)try{s=t._data(n,"events"),s&&s.remove&&t(n).triggerHandler("remove")}catch(a){}e(i)}}(t.cleanData),t.widget=function(e,i,s){var n,o,a,r={},h=e.split(".")[0];e=e.split(".")[1];var l=h+"-"+e;return s||(s=i,i=t.Widget),t.isArray(s)&&(s=t.extend.apply(null,[{}].concat(s))),t.expr[":"][l.toLowerCase()]=function(e){return!!t.data(e,l)},t[h]=t[h]||{},n=t[h][e],o=t[h][e]=function(t,e){return this._createWidget?(arguments.length&&this._createWidget(t,e),void 0):new o(t,e)},t.extend(o,n,{version:s.version,_proto:t.extend({},s),_childConstructors:[]}),a=new i,a.options=t.widget.extend({},a.options),t.each(s,function(e,s){return t.isFunction(s)?(r[e]=function(){function t(){return i.prototype[e].apply(this,arguments)}function n(t){return i.prototype[e].apply(this,t)}return function(){var e,i=this._super,o=this._superApply;return this._super=t,this._superApply=n,e=s.apply(this,arguments),this._super=i,this._superApply=o,e}}(),void 0):(r[e]=s,void 0)}),o.prototype=t.widget.extend(a,{widgetEventPrefix:n?a.widgetEventPrefix||e:e},r,{constructor:o,namespace:h,widgetName:e,widgetFullName:l}),n?(t.each(n._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete n._childConstructors):i._childConstructors.push(o),t.widget.bridge(e,o),o},t.widget.extend=function(e){for(var s,n,o=i.call(arguments,1),a=0,r=o.length;r>a;a++)for(s in o[a])n=o[a][s],o[a].hasOwnProperty(s)&&void 0!==n&&(e[s]=t.isPlainObject(n)?t.isPlainObject(e[s])?t.widget.extend({},e[s],n):t.widget.extend({},n):n);return e},t.widget.bridge=function(e,s){var n=s.prototype.widgetFullName||e;t.fn[e]=function(o){var a="string"==typeof o,r=i.call(arguments,1),h=this;return a?this.length||"instance"!==o?this.each(function(){var i,s=t.data(this,n);return"instance"===o?(h=s,!1):s?t.isFunction(s[o])&&"_"!==o.charAt(0)?(i=s[o].apply(s,r),i!==s&&void 0!==i?(h=i&&i.jquery?h.pushStack(i.get()):i,!1):void 0):t.error("no such method '"+o+"' for "+e+" widget instance"):t.error("cannot call methods on "+e+" prior to initialization; "+"attempted to call method '"+o+"'")}):h=void 0:(r.length&&(o=t.widget.extend.apply(null,[o].concat(r))),this.each(function(){var e=t.data(this,n);e?(e.option(o||{}),e._init&&e._init()):t.data(this,n,new s(o,this))})),h}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
",options:{classes:{},disabled:!1,create:null},_createWidget:function(i,s){s=t(s||this.defaultElement||this)[0],this.element=t(s),this.uuid=e++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),this.classesElementLookup={},s!==this&&(t.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===s&&this.destroy()}}),this.document=t(s.style?s.ownerDocument:s.document||s),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),i),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){var e=this;this._destroy(),t.each(this.classesElementLookup,function(t,i){e._removeClass(i,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,n,o,a=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(a={},s=e.split("."),e=s.shift(),s.length){for(n=a[e]=t.widget.extend({},this.options[e]),o=0;s.length-1>o;o++)n[s[o]]=n[s[o]]||{},n=n[s[o]];if(e=s.pop(),1===arguments.length)return void 0===n[e]?null:n[e];n[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];a[e]=i}return this._setOptions(a),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(e){var i,s,n;for(i in e)n=this.classesElementLookup[i],e[i]!==this.options.classes[i]&&n&&n.length&&(s=t(n.get()),this._removeClass(n,i),s.addClass(this._classes({element:s,keys:i,classes:e,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(e){function i(i,o){var a,r;for(r=0;i.length>r;r++)a=n.classesElementLookup[i[r]]||t(),a=e.add?t(t.unique(a.get().concat(e.element.get()))):t(a.not(e.element).get()),n.classesElementLookup[i[r]]=a,s.push(i[r]),o&&e.classes[i[r]]&&s.push(e.classes[i[r]])}var s=[],n=this;return e=t.extend({element:this.element,classes:this.options.classes||{}},e),this._on(e.element,{remove:"_untrackClassesElement"}),e.keys&&i(e.keys.match(/\S+/g)||[],!0),e.extra&&i(e.extra.match(/\S+/g)||[]),s.join(" ")},_untrackClassesElement:function(e){var i=this;t.each(i.classesElementLookup,function(s,n){-1!==t.inArray(e.target,n)&&(i.classesElementLookup[s]=t(n.not(e.target).get()))})},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){s="boolean"==typeof s?s:i;var n="string"==typeof t||null===t,o={extra:n?e:i,keys:n?t:e,element:n?this.element:t,add:s};return o.element.toggleClass(this._classes(o),s),this},_on:function(e,i,s){var n,o=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=n=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),t.each(s,function(s,a){function r(){return e||o.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof a?o[a]:a).apply(o,arguments):void 0}"string"!=typeof a&&(r.guid=a.guid=a.guid||r.guid||t.guid++);var h=s.match(/^([\w:-]*)\s*(.*)$/),l=h[1]+o.eventNamespace,c=h[2];c?n.on(l,c,r):i.on(l,r)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.off(i).off(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){this._addClass(t(e.currentTarget),null,"ui-state-hover")},mouseleave:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){this._addClass(t(e.currentTarget),null,"ui-state-focus")},focusout:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}}),t.widget,t.extend(t.expr[":"],{data:t.expr.createPseudo?t.expr.createPseudo(function(e){return function(i){return!!t.data(i,e)}}):function(e,i,s){return!!t.data(e,s[3])}}),t.fn.scrollParent=function(e){var i=this.css("position"),s="absolute"===i,n=e?/(auto|scroll|hidden)/:/(auto|scroll)/,o=this.parents().filter(function(){var e=t(this);return s&&"static"===e.css("position")?!1:n.test(e.css("overflow")+e.css("overflow-y")+e.css("overflow-x"))}).eq(0);return"fixed"!==i&&o.length?o:t(this[0].ownerDocument||document)},t.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase());var s=!1;t(document).on("mouseup",function(){s=!1}),t.widget("ui.mouse",{version:"1.12.1",options:{cancel:"input, textarea, button, select, option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.on("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).on("click."+this.widgetName,function(i){return!0===t.data(i.target,e.widgetName+".preventClickEvent")?(t.removeData(i.target,e.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.off("."+this.widgetName),this._mouseMoveDelegate&&this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(e){if(!s){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(e),this._mouseDownEvent=e;var i=this,n=1===e.which,o="string"==typeof this.options.cancel&&e.target.nodeName?t(e.target).closest(this.options.cancel).length:!1;return n&&!o&&this._mouseCapture(e)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(e)!==!1,!this._mouseStarted)?(e.preventDefault(),!0):(!0===t.data(e.target,this.widgetName+".preventClickEvent")&&t.removeData(e.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return i._mouseMove(t)},this._mouseUpDelegate=function(t){return i._mouseUp(t)},this.document.on("mousemove."+this.widgetName,this._mouseMoveDelegate).on("mouseup."+this.widgetName,this._mouseUpDelegate),e.preventDefault(),s=!0,!0)):!0}},_mouseMove:function(e){if(this._mouseMoved){if(t.ui.ie&&(!document.documentMode||9>document.documentMode)&&!e.button)return this._mouseUp(e);if(!e.which)if(e.originalEvent.altKey||e.originalEvent.ctrlKey||e.originalEvent.metaKey||e.originalEvent.shiftKey)this.ignoreMissingWhich=!0;else if(!this.ignoreMissingWhich)return this._mouseUp(e)}return(e.which||e.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(e),e.preventDefault()):(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,e)!==!1,this._mouseStarted?this._mouseDrag(e):this._mouseUp(e)),!this._mouseStarted)},_mouseUp:function(e){this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,e.target===this._mouseDownEvent.target&&t.data(e.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(e)),this._mouseDelayTimer&&(clearTimeout(this._mouseDelayTimer),delete this._mouseDelayTimer),this.ignoreMissingWhich=!1,s=!1,e.preventDefault()},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),t.widget("ui.sortable",t.ui.mouse,{version:"1.12.1",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_isOverAxis:function(t,e,i){return t>=e&&e+i>t},_isFloating:function(t){return/left|right/.test(t.css("float"))||/inline|table-cell/.test(t.css("display"))},_create:function(){this.containerCache={},this._addClass("ui-sortable"),this.refresh(),this.offset=this.element.offset(),this._mouseInit(),this._setHandleClassName(),this.ready=!0},_setOption:function(t,e){this._super(t,e),"handle"===t&&this._setHandleClassName()},_setHandleClassName:function(){var e=this;this._removeClass(this.element.find(".ui-sortable-handle"),"ui-sortable-handle"),t.each(this.items,function(){e._addClass(this.instance.options.handle?this.item.find(this.instance.options.handle):this.item,"ui-sortable-handle")})},_destroy:function(){this._mouseDestroy();for(var t=this.items.length-1;t>=0;t--)this.items[t].item.removeData(this.widgetName+"-item");return this},_mouseCapture:function(e,i){var s=null,n=!1,o=this;return this.reverting?!1:this.options.disabled||"static"===this.options.type?!1:(this._refreshItems(e),t(e.target).parents().each(function(){return t.data(this,o.widgetName+"-item")===o?(s=t(this),!1):void 0}),t.data(e.target,o.widgetName+"-item")===o&&(s=t(e.target)),s?!this.options.handle||i||(t(this.options.handle,s).find("*").addBack().each(function(){this===e.target&&(n=!0)}),n)?(this.currentItem=s,this._removeCurrentsFromItems(),!0):!1:!1)},_mouseStart:function(e,i,s){var n,o,a=this.options;if(this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(e),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},t.extend(this.offset,{click:{left:e.pageX-this.offset.left,top:e.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),this.originalPosition=this._generatePosition(e),this.originalPageX=e.pageX,this.originalPageY=e.pageY,a.cursorAt&&this._adjustOffsetFromHelper(a.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!==this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),a.containment&&this._setContainment(),a.cursor&&"auto"!==a.cursor&&(o=this.document.find("body"),this.storedCursor=o.css("cursor"),o.css("cursor",a.cursor),this.storedStylesheet=t("").appendTo(o)),a.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",a.opacity)),a.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",a.zIndex)),this.scrollParent[0]!==this.document[0]&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",e,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions(),!s)for(n=this.containers.length-1;n>=0;n--)this.containers[n]._trigger("activate",e,this._uiHash(this));return t.ui.ddmanager&&(t.ui.ddmanager.current=this),t.ui.ddmanager&&!a.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this.dragging=!0,this._addClass(this.helper,"ui-sortable-helper"),this._mouseDrag(e),!0},_mouseDrag:function(e){var i,s,n,o,a=this.options,r=!1;for(this.position=this._generatePosition(e),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs),this.options.scroll&&(this.scrollParent[0]!==this.document[0]&&"HTML"!==this.scrollParent[0].tagName?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-e.pageY=0;i--)if(s=this.items[i],n=s.item[0],o=this._intersectsWithPointer(s),o&&s.instance===this.currentContainer&&n!==this.currentItem[0]&&this.placeholder[1===o?"next":"prev"]()[0]!==n&&!t.contains(this.placeholder[0],n)&&("semi-dynamic"===this.options.type?!t.contains(this.element[0],n):!0)){if(this.direction=1===o?"down":"up","pointer"!==this.options.tolerance&&!this._intersectsWithSides(s))break;this._rearrange(e,s),this._trigger("change",e,this._uiHash());break}return this._contactContainers(e),t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),this._trigger("sort",e,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(e,i){if(e){if(t.ui.ddmanager&&!this.options.dropBehaviour&&t.ui.ddmanager.drop(this,e),this.options.revert){var s=this,n=this.placeholder.offset(),o=this.options.axis,a={};o&&"x"!==o||(a.left=n.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]===this.document[0].body?0:this.offsetParent[0].scrollLeft)),o&&"y"!==o||(a.top=n.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]===this.document[0].body?0:this.offsetParent[0].scrollTop)),this.reverting=!0,t(this.helper).animate(a,parseInt(this.options.revert,10)||500,function(){s._clear(e)})}else this._clear(e,i);return!1}},cancel:function(){if(this.dragging){this._mouseUp(new t.Event("mouseup",{target:null})),"original"===this.options.helper?(this.currentItem.css(this._storedCSS),this._removeClass(this.currentItem,"ui-sortable-helper")):this.currentItem.show();for(var e=this.containers.length-1;e>=0;e--)this.containers[e]._trigger("deactivate",null,this._uiHash(this)),this.containers[e].containerCache.over&&(this.containers[e]._trigger("out",null,this._uiHash(this)),this.containers[e].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),"original"!==this.options.helper&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),t.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?t(this.domPosition.prev).after(this.currentItem):t(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},t(i).each(function(){var i=(t(e.item||this).attr(e.attribute||"id")||"").match(e.expression||/(.+)[\-=_](.+)/);i&&s.push((e.key||i[1]+"[]")+"="+(e.key&&e.expression?i[1]:i[2]))}),!s.length&&e.key&&s.push(e.key+"="),s.join("&")},toArray:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},i.each(function(){s.push(t(e.item||this).attr(e.attribute||"id")||"")}),s},_intersectsWith:function(t){var e=this.positionAbs.left,i=e+this.helperProportions.width,s=this.positionAbs.top,n=s+this.helperProportions.height,o=t.left,a=o+t.width,r=t.top,h=r+t.height,l=this.offset.click.top,c=this.offset.click.left,u="x"===this.options.axis||s+l>r&&h>s+l,d="y"===this.options.axis||e+c>o&&a>e+c,p=u&&d;return"pointer"===this.options.tolerance||this.options.forcePointerForContainers||"pointer"!==this.options.tolerance&&this.helperProportions[this.floating?"width":"height"]>t[this.floating?"width":"height"]?p:e+this.helperProportions.width/2>o&&a>i-this.helperProportions.width/2&&s+this.helperProportions.height/2>r&&h>n-this.helperProportions.height/2},_intersectsWithPointer:function(t){var e,i,s="x"===this.options.axis||this._isOverAxis(this.positionAbs.top+this.offset.click.top,t.top,t.height),n="y"===this.options.axis||this._isOverAxis(this.positionAbs.left+this.offset.click.left,t.left,t.width),o=s&&n;return o?(e=this._getDragVerticalDirection(),i=this._getDragHorizontalDirection(),this.floating?"right"===i||"down"===e?2:1:e&&("down"===e?2:1)):!1},_intersectsWithSides:function(t){var e=this._isOverAxis(this.positionAbs.top+this.offset.click.top,t.top+t.height/2,t.height),i=this._isOverAxis(this.positionAbs.left+this.offset.click.left,t.left+t.width/2,t.width),s=this._getDragVerticalDirection(),n=this._getDragHorizontalDirection();return this.floating&&n?"right"===n&&i||"left"===n&&!i:s&&("down"===s&&e||"up"===s&&!e)},_getDragVerticalDirection:function(){var t=this.positionAbs.top-this.lastPositionAbs.top;return 0!==t&&(t>0?"down":"up")},_getDragHorizontalDirection:function(){var t=this.positionAbs.left-this.lastPositionAbs.left;return 0!==t&&(t>0?"right":"left")},refresh:function(t){return this._refreshItems(t),this._setHandleClassName(),this.refreshPositions(),this},_connectWith:function(){var t=this.options;return t.connectWith.constructor===String?[t.connectWith]:t.connectWith},_getItemsAsjQuery:function(e){function i(){r.push(this)}var s,n,o,a,r=[],h=[],l=this._connectWith();if(l&&e)for(s=l.length-1;s>=0;s--)for(o=t(l[s],this.document[0]),n=o.length-1;n>=0;n--)a=t.data(o[n],this.widgetFullName),a&&a!==this&&!a.options.disabled&&h.push([t.isFunction(a.options.items)?a.options.items.call(a.element):t(a.options.items,a.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),a]);for(h.push([t.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):t(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]),s=h.length-1;s>=0;s--)h[s][0].each(i);return t(r)},_removeCurrentsFromItems:function(){var e=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=t.grep(this.items,function(t){for(var i=0;e.length>i;i++)if(e[i]===t.item[0])return!1;return!0})},_refreshItems:function(e){this.items=[],this.containers=[this];var i,s,n,o,a,r,h,l,c=this.items,u=[[t.isFunction(this.options.items)?this.options.items.call(this.element[0],e,{item:this.currentItem}):t(this.options.items,this.element),this]],d=this._connectWith();if(d&&this.ready)for(i=d.length-1;i>=0;i--)for(n=t(d[i],this.document[0]),s=n.length-1;s>=0;s--)o=t.data(n[s],this.widgetFullName),o&&o!==this&&!o.options.disabled&&(u.push([t.isFunction(o.options.items)?o.options.items.call(o.element[0],e,{item:this.currentItem}):t(o.options.items,o.element),o]),this.containers.push(o));for(i=u.length-1;i>=0;i--)for(a=u[i][1],r=u[i][0],s=0,l=r.length;l>s;s++)h=t(r[s]),h.data(this.widgetName+"-item",a),c.push({item:h,instance:a,width:0,height:0,left:0,top:0})},refreshPositions:function(e){this.floating=this.items.length?"x"===this.options.axis||this._isFloating(this.items[0].item):!1,this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());var i,s,n,o;for(i=this.items.length-1;i>=0;i--)s=this.items[i],s.instance!==this.currentContainer&&this.currentContainer&&s.item[0]!==this.currentItem[0]||(n=this.options.toleranceElement?t(this.options.toleranceElement,s.item):s.item,e||(s.width=n.outerWidth(),s.height=n.outerHeight()),o=n.offset(),s.left=o.left,s.top=o.top);if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(i=this.containers.length-1;i>=0;i--)o=this.containers[i].element.offset(),this.containers[i].containerCache.left=o.left,this.containers[i].containerCache.top=o.top,this.containers[i].containerCache.width=this.containers[i].element.outerWidth(),this.containers[i].containerCache.height=this.containers[i].element.outerHeight();return this},_createPlaceholder:function(e){e=e||this;var i,s=e.options;s.placeholder&&s.placeholder.constructor!==String||(i=s.placeholder,s.placeholder={element:function(){var s=e.currentItem[0].nodeName.toLowerCase(),n=t("<"+s+">",e.document[0]);return e._addClass(n,"ui-sortable-placeholder",i||e.currentItem[0].className)._removeClass(n,"ui-sortable-helper"),"tbody"===s?e._createTrPlaceholder(e.currentItem.find("tr").eq(0),t("
",e.document[0]).appendTo(n)):"tr"===s?e._createTrPlaceholder(e.currentItem,n):"img"===s&&n.attr("src",e.currentItem.attr("src")),i||n.css("visibility","hidden"),n},update:function(t,n){(!i||s.forcePlaceholderSize)&&(n.height()||n.height(e.currentItem.innerHeight()-parseInt(e.currentItem.css("paddingTop")||0,10)-parseInt(e.currentItem.css("paddingBottom")||0,10)),n.width()||n.width(e.currentItem.innerWidth()-parseInt(e.currentItem.css("paddingLeft")||0,10)-parseInt(e.currentItem.css("paddingRight")||0,10)))}}),e.placeholder=t(s.placeholder.element.call(e.element,e.currentItem)),e.currentItem.after(e.placeholder),s.placeholder.update(e,e.placeholder)},_createTrPlaceholder:function(e,i){var s=this;e.children().each(function(){t("",s.document[0]).attr("colspan",t(this).attr("colspan")||1).appendTo(i)})},_contactContainers:function(e){var i,s,n,o,a,r,h,l,c,u,d=null,p=null;for(i=this.containers.length-1;i>=0;i--)if(!t.contains(this.currentItem[0],this.containers[i].element[0]))if(this._intersectsWith(this.containers[i].containerCache)){if(d&&t.contains(this.containers[i].element[0],d.element[0]))continue;d=this.containers[i],p=i}else this.containers[i].containerCache.over&&(this.containers[i]._trigger("out",e,this._uiHash(this)),this.containers[i].containerCache.over=0);if(d)if(1===this.containers.length)this.containers[p].containerCache.over||(this.containers[p]._trigger("over",e,this._uiHash(this)),this.containers[p].containerCache.over=1);else{for(n=1e4,o=null,c=d.floating||this._isFloating(this.currentItem),a=c?"left":"top",r=c?"width":"height",u=c?"pageX":"pageY",s=this.items.length-1;s>=0;s--)t.contains(this.containers[p].element[0],this.items[s].item[0])&&this.items[s].item[0]!==this.currentItem[0]&&(h=this.items[s].item.offset()[a],l=!1,e[u]-h>this.items[s][r]/2&&(l=!0),n>Math.abs(e[u]-h)&&(n=Math.abs(e[u]-h),o=this.items[s],this.direction=l?"up":"down"));if(!o&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[p])return this.currentContainer.containerCache.over||(this.containers[p]._trigger("over",e,this._uiHash()),this.currentContainer.containerCache.over=1),void 0;o?this._rearrange(e,o,null,!0):this._rearrange(e,null,this.containers[p].element,!0),this._trigger("change",e,this._uiHash()),this.containers[p]._trigger("change",e,this._uiHash(this)),this.currentContainer=this.containers[p],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[p]._trigger("over",e,this._uiHash(this)),this.containers[p].containerCache.over=1}},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper)?t(i.helper.apply(this.element[0],[e,this.currentItem])):"clone"===i.helper?this.currentItem.clone():this.currentItem;return s.parents("body").length||t("parent"!==i.appendTo?i.appendTo:this.currentItem[0].parentNode)[0].appendChild(s[0]),s[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!s[0].style.width||i.forceHelperSize)&&s.width(this.currentItem.width()),(!s[0].style.height||i.forceHelperSize)&&s.height(this.currentItem.height()),s},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var e=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===this.document[0].body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&t.ui.ie)&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var t=this.currentItem.position();return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:t.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options;"parent"===n.containment&&(n.containment=this.helper[0].parentNode),("document"===n.containment||"window"===n.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,"document"===n.containment?this.document.width():this.window.width()-this.helperProportions.width-this.margins.left,("document"===n.containment?this.document.height()||document.body.parentNode.scrollHeight:this.window.height()||this.document[0].body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(n.containment)||(e=t(n.containment)[0],i=t(n.containment).offset(),s="hidden"!==t(e).css("overflow"),this.containment=[i.left+(parseInt(t(e).css("borderLeftWidth"),10)||0)+(parseInt(t(e).css("paddingLeft"),10)||0)-this.margins.left,i.top+(parseInt(t(e).css("borderTopWidth"),10)||0)+(parseInt(t(e).css("paddingTop"),10)||0)-this.margins.top,i.left+(s?Math.max(e.scrollWidth,e.offsetWidth):e.offsetWidth)-(parseInt(t(e).css("borderLeftWidth"),10)||0)-(parseInt(t(e).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,i.top+(s?Math.max(e.scrollHeight,e.offsetHeight):e.offsetHeight)-(parseInt(t(e).css("borderTopWidth"),10)||0)-(parseInt(t(e).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(e,i){i||(i=this.position);var s="absolute"===e?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,o=/(html|body)/i.test(n[0].tagName);return{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():o?0:n.scrollTop())*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():o?0:n.scrollLeft())*s} +},_generatePosition:function(e){var i,s,n=this.options,o=e.pageX,a=e.pageY,r="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=/(html|body)/i.test(r[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(e.pageX-this.offset.click.leftthis.containment[2]&&(o=this.containment[2]+this.offset.click.left),e.pageY-this.offset.click.top>this.containment[3]&&(a=this.containment[3]+this.offset.click.top)),n.grid&&(i=this.originalPageY+Math.round((a-this.originalPageY)/n.grid[1])*n.grid[1],a=this.containment?i-this.offset.click.top>=this.containment[1]&&i-this.offset.click.top<=this.containment[3]?i:i-this.offset.click.top>=this.containment[1]?i-n.grid[1]:i+n.grid[1]:i,s=this.originalPageX+Math.round((o-this.originalPageX)/n.grid[0])*n.grid[0],o=this.containment?s-this.offset.click.left>=this.containment[0]&&s-this.offset.click.left<=this.containment[2]?s:s-this.offset.click.left>=this.containment[0]?s-n.grid[0]:s+n.grid[0]:s)),{top:a-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():h?0:r.scrollTop()),left:o-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():h?0:r.scrollLeft())}},_rearrange:function(t,e,i,s){i?i[0].appendChild(this.placeholder[0]):e.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?e.item[0]:e.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter;this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(t,e){function i(t,e,i){return function(s){i._trigger(t,s,e._uiHash(e))}}this.reverting=!1;var s,n=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(s in this._storedCSS)("auto"===this._storedCSS[s]||"static"===this._storedCSS[s])&&(this._storedCSS[s]="");this.currentItem.css(this._storedCSS),this._removeClass(this.currentItem,"ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!e&&n.push(function(t){this._trigger("receive",t,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||e||n.push(function(t){this._trigger("update",t,this._uiHash())}),this!==this.currentContainer&&(e||(n.push(function(t){this._trigger("remove",t,this._uiHash())}),n.push(function(t){return function(e){t._trigger("receive",e,this._uiHash(this))}}.call(this,this.currentContainer)),n.push(function(t){return function(e){t._trigger("update",e,this._uiHash(this))}}.call(this,this.currentContainer)))),s=this.containers.length-1;s>=0;s--)e||n.push(i("deactivate",this,this.containers[s])),this.containers[s].containerCache.over&&(n.push(i("out",this,this.containers[s])),this.containers[s].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,e||this._trigger("beforeStop",t,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.cancelHelperRemoval||(this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null),!e){for(s=0;n.length>s;s++)n[s].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!this.cancelHelperRemoval},_trigger:function(){t.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(e){var i=e||this;return{helper:i.helper,placeholder:i.placeholder||t([]),position:i.position,originalPosition:i.originalPosition,offset:i.positionAbs,item:i.currentItem,sender:e?e.element:null}}})}); \ No newline at end of file diff --git a/resources/views/list/piggy-banks.twig b/resources/views/list/piggy-banks.twig index 269c675dfc..6fe3af9bff 100644 --- a/resources/views/list/piggy-banks.twig +++ b/resources/views/list/piggy-banks.twig @@ -1,4 +1,4 @@ -
{{ 'budget'|_ }}{{ 'budgeted'|_ }}{{ 'spent'|_ }}{{ 'left'|_ }}{{ 'overspent'|_ }}{{ 'budget'|_ }}{{ 'budgeted'|_ }}{{ 'spent'|_ }}{{ 'left'|_ }}{{ 'overspent'|_ }}
- {% if budgetLine.getBudget.id %} + + {% if budgetLine.getBudget.id %} + {{ budgetLine.getBudget.name }} - {% else %} + {{ 'no_budget'|_ }} - {% endif %} - - {% if budgetLine.getRepetition.id %} + + + {{ budgetLine.getRepetition.amount|formatAmount }} - {% else %} + {{ 0|formatAmount }} - {% endif %} - + {% if budgetLine.getSpent != 0 %} - {{ budgetLine.getSpent|formatAmount }} + {{ budgetLine.getSpent|formatAmount }} + {% endif %} {% if budgetLine.getSpent == 0 %} @@ -47,12 +61,12 @@ {% endif %} + {% if(budgetLine.getOverspent == 0) %} {{ budgetLine.getLeft|formatAmount }} {% endif %} + {% if budgetLine.getOverspent != 0 %} {{ budgetLine.getOverspent|formatAmount }} {% endif %} From 6075d75ee201160c4eab859ffdcdd71f4632684a Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 21 Nov 2016 20:15:59 +0100 Subject: [PATCH 142/709] Fix debug code [skip ci] --- public/js/ff/charts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/ff/charts.js b/public/js/ff/charts.js index ad0576fa1a..d0a928ca2b 100644 --- a/public/js/ff/charts.js +++ b/public/js/ff/charts.js @@ -186,5 +186,5 @@ function drawAChart(URI, container, chartType, options, colorData) { console.log('Failed to draw ' + chartType + ' in container ' + container); $('#' + container).addClass('general-chart-error'); }); - console.log('URL for ' + chartType + ' chart : ' + URL); + console.log('URL for ' + chartType + ' chart : ' + URI); } From 4a99399952ff2f720e0e493cc342644dfa888aa2 Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 21 Nov 2016 20:23:25 +0100 Subject: [PATCH 143/709] Fix chart for account/all overview. --- app/Http/Controllers/AccountController.php | 9 ++++- .../Account/AccountRepository.php | 38 ++++++++++++++----- .../Account/AccountRepositoryInterface.php | 9 +++++ 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index c1c89ef897..7ad7b4a0d1 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -22,6 +22,7 @@ use FireflyIII\Http\Requests\AccountFormRequest; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use FireflyIII\Models\Transaction; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Support\CacheProperties; @@ -240,7 +241,7 @@ class AccountController extends Controller * * @return View */ - public function showAll(Account $account) + public function showAll(AccountRepositoryInterface $repository, Account $account) { $subTitle = sprintf('%s (%s)', $account->name, strtolower(trans('firefly.everything'))); $page = intval(Input::get('page')) === 0 ? 1 : intval(Input::get('page')); @@ -252,7 +253,11 @@ class AccountController extends Controller $journals = $collector->getPaginatedJournals(); $journals->setPath('accounts/show/' . $account->id . '/all'); - return view('accounts.show_with_date', compact('category', 'date', 'account', 'journals', 'subTitle', 'carbon')); + // get oldest and newest journal for account: + $start = $repository->oldestJournalDate($account); + $end = $repository->newestJournalDate($account); + + return view('accounts.show_with_date', compact('account', 'journals', 'subTitle', 'start', 'end')); } /** diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index b7421c1e6b..e9c21082e2 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -260,6 +260,29 @@ class AccountRepository implements AccountRepositoryInterface return $result; } + /** + * Returns the date of the very last transaction in this account. + * + * @param Account $account + * + * @return Carbon + */ + public function newestJournalDate(Account $account): Carbon + { + $last = new Carbon; + $date = $account->transactions() + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->orderBy('transaction_journals.date', 'DESC') + ->orderBy('transaction_journals.order', 'ASC') + ->orderBy('transaction_journals.id', 'DESC') + ->first(['transaction_journals.date']); + if (!is_null($date)) { + $last = new Carbon($date->date); + } + + return $last; + } + /** * Returns the date of the very first transaction in this account. * @@ -270,14 +293,12 @@ class AccountRepository implements AccountRepositoryInterface public function oldestJournalDate(Account $account): Carbon { $first = new Carbon; - - /** @var Transaction $first */ - $date = $account->transactions() - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->orderBy('transaction_journals.date', 'ASC') - ->orderBy('transaction_journals.order', 'DESC') - ->orderBy('transaction_journals.id', 'ASC') - ->first(['transaction_journals.date']); + $date = $account->transactions() + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->orderBy('transaction_journals.date', 'ASC') + ->orderBy('transaction_journals.order', 'DESC') + ->orderBy('transaction_journals.id', 'ASC') + ->first(['transaction_journals.date']); if (!is_null($date)) { $first = new Carbon($date->date); } @@ -602,5 +623,4 @@ class AccountRepository implements AccountRepositoryInterface return false; } - } diff --git a/app/Repositories/Account/AccountRepositoryInterface.php b/app/Repositories/Account/AccountRepositoryInterface.php index ab7b1f5dc9..d306e433de 100644 --- a/app/Repositories/Account/AccountRepositoryInterface.php +++ b/app/Repositories/Account/AccountRepositoryInterface.php @@ -96,6 +96,15 @@ interface AccountRepositoryInterface */ public function getActiveAccountsByType(array $types): Collection; + /** + * Returns the date of the very last transaction in this account. + * + * @param Account $account + * + * @return Carbon + */ + public function newestJournalDate(Account $account): Carbon; + /** * Returns the date of the very first transaction in this account. * From eb78cf20c2d27c9c4f91764672e79e0ef3c51802 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 22 Nov 2016 19:10:17 +0100 Subject: [PATCH 144/709] This fixes #414 --- app/Http/Controllers/Controller.php | 38 +++++++++++++++++++ .../Transaction/ConvertController.php | 8 ++++ .../Transaction/SingleController.php | 22 +++++++++-- .../Transaction/SplitController.php | 9 +++++ .../Controllers/TransactionController.php | 5 +++ app/Models/TransactionJournal.php | 12 +++--- resources/lang/en_US/firefly.php | 2 +- 7 files changed, 86 insertions(+), 10 deletions(-) diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 6109a79967..9aaad05724 100755 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -13,10 +13,15 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; +use FireflyIII\Models\AccountType; +use FireflyIII\Models\Transaction; +use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\TransactionType; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Routing\Controller as BaseController; +use Session; use View; /** @@ -60,4 +65,37 @@ class Controller extends BaseController } + + /** + * @param TransactionJournal $journal + * + * @return bool + */ + protected function isOpeningBalance(TransactionJournal $journal): bool + { + return TransactionJournal::transactionTypeStr($journal) === TransactionType::OPENING_BALANCE; + } + + /** + * @param TransactionJournal $journal + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + */ + protected function redirectToAccount(TransactionJournal $journal) + { + $valid = [AccountType::DEFAULT, AccountType::ASSET]; + $transactions = $journal->transactions; + /** @var Transaction $transaction */ + foreach ($transactions as $transaction) { + $account = $transaction->account; + if (in_array($account->accountType->type, $valid)) { + return redirect(route('accounts.show', [$account->id])); + } + + } + Session::flash('error', strval(trans('firefly.cannot_redirect_to_account'))); + + return redirect(route('index')); + } + } diff --git a/app/Http/Controllers/Transaction/ConvertController.php b/app/Http/Controllers/Transaction/ConvertController.php index f00fb809ad..1941da2f6a 100644 --- a/app/Http/Controllers/Transaction/ConvertController.php +++ b/app/Http/Controllers/Transaction/ConvertController.php @@ -64,6 +64,10 @@ class ConvertController extends Controller */ public function convert(TransactionType $destinationType, TransactionJournal $journal) { + if ($this->isOpeningBalance($journal)) { + return $this->redirectToAccount($journal); + } + $positiveAmount = TransactionJournal::amountPositive($journal); $assetAccounts = ExpandedForm::makeSelectList($this->accounts->getActiveAccountsByType([AccountType::DEFAULT, AccountType::ASSET])); $sourceType = $journal->transactionType; @@ -113,6 +117,10 @@ class ConvertController extends Controller */ public function submit(Request $request, JournalRepositoryInterface $repository, TransactionType $destinationType, TransactionJournal $journal) { + if ($this->isOpeningBalance($journal)) { + return $this->redirectToAccount($journal); + } + $data = $request->all(); // cannot convert to its own type. diff --git a/app/Http/Controllers/Transaction/SingleController.php b/app/Http/Controllers/Transaction/SingleController.php index d917fac89b..803971fe52 100644 --- a/app/Http/Controllers/Transaction/SingleController.php +++ b/app/Http/Controllers/Transaction/SingleController.php @@ -125,6 +125,10 @@ class SingleController extends Controller */ public function delete(TransactionJournal $journal) { + if ($this->isOpeningBalance($journal)) { + return $this->redirectToAccount($journal); + } + $what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type); $subTitle = trans('firefly.delete_' . $what, ['description' => $journal->description]); @@ -146,6 +150,10 @@ class SingleController extends Controller */ public function destroy(JournalRepositoryInterface $repository, TransactionJournal $transactionJournal) { + if ($this->isOpeningBalance($transactionJournal)) { + return $this->redirectToAccount($transactionJournal); + } + $type = TransactionJournal::transactionTypeStr($transactionJournal); Session::flash('success', strval(trans('firefly.deleted_' . $type, ['description' => e($transactionJournal->description)]))); @@ -164,17 +172,23 @@ class SingleController extends Controller */ public function edit(TransactionJournal $journal) { + if ($this->isOpeningBalance($journal)) { + return $this->redirectToAccount($journal); + } + $count = $journal->transactions()->count(); + if ($count > 2) { return redirect(route('transactions.edit-split', [$journal->id])); } + $what = strtolower(TransactionJournal::transactionTypeStr($journal)); $assetAccounts = ExpandedForm::makeSelectList($this->accounts->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])); $budgetList = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets()); // view related code $subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]); - $what = strtolower(TransactionJournal::transactionTypeStr($journal)); + // journal related code $sourceAccounts = TransactionJournal::sourceAccountList($journal); @@ -290,6 +304,10 @@ class SingleController extends Controller */ public function update(JournalFormRequest $request, JournalRepositoryInterface $repository, TransactionJournal $journal) { + if ($this->isOpeningBalance($journal)) { + return $this->redirectToAccount($journal); + } + $data = $request->getJournalData(); $journal = $repository->update($journal, $data); $this->attachments->saveAttachmentsForModel($journal); @@ -324,6 +342,4 @@ class SingleController extends Controller return redirect(session('transactions.edit.url')); } - - } diff --git a/app/Http/Controllers/Transaction/SplitController.php b/app/Http/Controllers/Transaction/SplitController.php index 7c80d3f392..f14cde1885 100644 --- a/app/Http/Controllers/Transaction/SplitController.php +++ b/app/Http/Controllers/Transaction/SplitController.php @@ -89,6 +89,10 @@ class SplitController extends Controller */ public function edit(Request $request, TransactionJournal $journal) { + if ($this->isOpeningBalance($journal)) { + return $this->redirectToAccount($journal); + } + $uploadSize = min(Steam::phpBytes(ini_get('upload_max_filesize')), Steam::phpBytes(ini_get('post_max_size'))); $currencies = ExpandedForm::makeSelectList($this->currencies->get()); $assetAccounts = ExpandedForm::makeSelectList($this->accounts->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])); @@ -127,6 +131,10 @@ class SplitController extends Controller */ public function update(Request $request, JournalRepositoryInterface $repository, TransactionJournal $journal) { + if ($this->isOpeningBalance($journal)) { + return $this->redirectToAccount($journal); + } + $data = $this->arrayFromInput($request); $journal = $repository->updateSplitJournal($journal, $data); @@ -284,4 +292,5 @@ class SplitController extends Controller return $return; } + } diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 70c9dd7164..b8e269be0a 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -210,6 +210,10 @@ class TransactionController extends Controller */ public function show(TransactionJournal $journal, JournalTaskerInterface $tasker) { + if ($this->isOpeningBalance($journal)) { + return $this->redirectToAccount($journal); + } + $events = $tasker->getPiggyBankEvents($journal); $transactions = $tasker->getTransactionsOverview($journal); $what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type); @@ -219,4 +223,5 @@ class TransactionController extends Controller } + } diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index 0441e6e8a0..f76f8bcdcf 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -64,12 +64,12 @@ class TransactionJournal extends TransactionJournalSupport public static function routeBinder($value) { if (auth()->check()) { - $validTypes = [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER]; - $object = TransactionJournal::where('transaction_journals.id', $value) - ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') - ->whereIn('transaction_types.type', $validTypes) - ->where('user_id', auth()->user()->id)->first(['transaction_journals.*']); - if ($object) { + $object = TransactionJournal + ::where('transaction_journals.id', $value) + ->with('transactionType') + ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') + ->where('user_id', auth()->user()->id)->first(['transaction_journals.*']); + if (!is_null($object)) { return $object; } } diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 665e43838f..73362cc316 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -90,7 +90,7 @@ return [ 'expenses_by_category' => 'Expenses by category', 'expenses_by_budget' => 'Expenses by budget', 'income_by_category' => 'Income by category', - + 'cannot_redirect_to_account' => 'Firefly III cannot redirect you to the correct page. Apologies.', // repeat frequencies: 'repeat_freq_yearly' => 'yearly', 'repeat_freq_monthly' => 'monthly', From dbbc85a576a4ea3b779e003d20b35e3b3302d2a5 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 22 Nov 2016 19:10:38 +0100 Subject: [PATCH 145/709] Hide some boxes when the user has no bills. --- app/Http/Controllers/HomeController.php | 14 ++++--- public/js/ff/index.js | 6 ++- resources/views/index.twig | 22 +++++----- resources/views/partials/boxes.twig | 56 ++++++++++++------------- 4 files changed, 50 insertions(+), 48 deletions(-) diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index be1fceae02..dd2e83e25e 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -19,6 +19,7 @@ use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Models\AccountType; use FireflyIII\Models\Tag; use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; +use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use Illuminate\Http\Request; use Illuminate\Support\Collection; @@ -121,7 +122,6 @@ class HomeController extends Controller */ public function index(ARI $repository) { - $types = config('firefly.accountTypesByIdentifier.asset'); $count = $repository->count($types); @@ -142,6 +142,11 @@ class HomeController extends Controller $accounts = $repository->getAccountsById($frontPage->data); $showDepositsFrontpage = Preferences::get('showDepositsFrontpage', false)->data; + // zero bills? Hide some elements from view. + /** @var BillRepositoryInterface $billRepository */ + $billRepository = app(BillRepositoryInterface::class); + $billCount = $billRepository->getBills()->count(); + foreach ($accounts as $account) { $collector = app(JournalCollectorInterface::class); $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit(10)->setPage(1); @@ -153,7 +158,7 @@ class HomeController extends Controller } return view( - 'index', compact('count', 'showTour', 'title', 'subTitle', 'mainTitleIcon', 'transactions', 'showDepositsFrontpage') + 'index', compact('count', 'showTour', 'title', 'subTitle', 'mainTitleIcon', 'transactions', 'showDepositsFrontpage', 'billCount') ); } @@ -205,10 +210,7 @@ class HomeController extends Controller * * @return bool */ - private - function startsWithAny( - array $array, string $needle - ): bool + private function startsWithAny(array $array, string $needle): bool { foreach ($array as $entry) { if ((substr($needle, 0, strlen($entry)) === $entry)) { diff --git a/public/js/ff/index.js b/public/js/ff/index.js index f03c97445f..f797dcf142 100644 --- a/public/js/ff/index.js +++ b/public/js/ff/index.js @@ -1,4 +1,4 @@ - /* globals $, columnChart,showTour, Tour, google, pieChart, stackedColumnChart */ +/* globals $, columnChart,showTour, Tour, google, pieChart, stackedColumnChart, billCount */ $(function () { "use strict"; @@ -33,7 +33,9 @@ function endTheTour() { function drawChart() { "use strict"; lineChart('chart/account/frontpage', 'accounts-chart'); - pieChart('chart/bill/frontpage', 'bills-chart'); + if (billCount > 0) { + pieChart('chart/bill/frontpage', 'bills-chart'); + } stackedColumnChart('chart/budget/frontpage', 'budgets-chart'); columnChart('chart/category/frontpage', 'categories-chart'); columnChart('chart/account/expense', 'expense-accounts-chart'); diff --git a/resources/views/index.twig b/resources/views/index.twig index 1e72dc442f..737c74873d 100644 --- a/resources/views/index.twig +++ b/resources/views/index.twig @@ -43,20 +43,21 @@
+ {% if billCount > 0 %} + +
+
+

{{ 'bills'|_ }}

- -
-
-

{{ 'bills'|_ }}

- -
-
-
-
+
+
+ +
+
-
+ {% endif %} {% for data in transactions %} @@ -129,6 +130,7 @@ {% else %} showTour = false; {% endif %} + var billCount = {{ billCount }}; diff --git a/resources/views/partials/boxes.twig b/resources/views/partials/boxes.twig index 6b3c2221cf..5ec3003595 100644 --- a/resources/views/partials/boxes.twig +++ b/resources/views/partials/boxes.twig @@ -1,6 +1,14 @@ +{# Set box sizes: #} +{% if billCount > 0 %} + {% set boxClasses = 'col-lg-3 col-md-3 col-sm-6 col-xs-12' %} +{% else %} + {# Zero bills? Remove the boxes. #} + {% set boxClasses = 'col-lg-4 col-md-4 col-sm-6 col-xs-12' %} +{% endif %} +
 
 
+
{% for piggyBank in piggyBanks %} diff --git a/resources/views/piggy-banks/index.twig b/resources/views/piggy-banks/index.twig index 6c4afac57b..364ec37f0e 100644 --- a/resources/views/piggy-banks/index.twig +++ b/resources/views/piggy-banks/index.twig @@ -9,12 +9,10 @@
diff --git a/storage/database/databasecopy.sqlite b/storage/database/databasecopy.sqlite index e1d1e20c30c7d3a2463d8a6d35efc0d40903657f..03d527000f45cc571c24ed510e1075447ad69f7d 100644 GIT binary patch literal 230400 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 M_YsVP`^c961CEtcp#T5? literal 286720 zcmeEv31C}Cnee&bGO!%Y*nNs}l}(!|Xr$*1I|eM#gyR&4o-owSZ@DYk4`awIv9 z-HRtXNe`ghQ0`L7QQAUVj&c+pc3GBVVPS#g|K`2X(_uM%Bb(B+ zQq!67d-HuW^SwE~`R4n+>FPWZj0dczBGCz7+**l>kRTw*YDH*u03qQ@l=?}KEK2=} zgtYV`Nx-r9->?bZt|zaec{<1+$?wRo$j`}7$PdX6$al#%$=Ar! z5ZHD=u(uq7YpoFM+6=)pn;_VBH3ZfT5NxDldEFHdtX>U)r38Xv(@F?Sun)mFd@s43 z^brRU@b~be_)d6O_!TIyR0>>EQiM7!4x7DZ(C!#?)D1dpl@43=;P9v~JQ9cv!pbvv zrmE6@jU^+WW6)l0gU8wVY}XX0^4SL+Rf7(@t-9KgmCs?j#+=G$8+14ZZMD^PwyLar zcH4fj2o1CP*#>P+TCT0Kb}(>u(iip*#-qM)%r_hlM#8bdxNj&F*k&q1Z5ja04Hb3R zE32J@r-Ek(!&9LU%@ez(NH5(6O|AyS_G+j^daEn#Hv2A8gnAep36c}!2-!kxq!|Am{{sIbej0xY%Q%eN@tg5=csni;{viBJ_#OZket$y> ztlLcX3%)bHpxW~Who<}^fp~2Ds!e2@WilE#6^I7H!-3eUtKo0x)b!JjMTTOlH44RmF93vZ65@*_j!DwJI5D%);16j3#Y)w)y7>E_GC?Oje-C`z-g_H?H z!B8lM*AzoleZ#|%sc?L7A`thH%gkg`@=0uqMTD&CfW{URB%=|iFkV*#HB6)3V0>mW z5G&e1phD@7ho_?Si$ev>r5GrXEJ)2>yh?yNP6S7yKDEvzbo>|N4G1{!yGbWm2W$Up zaSuEw{9fb~a2A_TWn~3jx%b)~dn;{LyTesgdh8duF%?z zs|90A*0Dp~hl26pNHBb;xKY%A_m>(3ZgG2C!v07&5DWUk*67q^&^PLx#D`28o}EVV zG#%R?iOLzx_|$kDWytj&(+H9P%FV27Dp!cHBTT(7i@Mn|m6Yr@|w?C~XCZ zK7!$RV;mpA8k#LeN!ioo?&-3+BOq;vZpSWxHDIklVm9=68>XU>A0{5cbp1DzrxAIE z{DFLr{EU19r1MWd%cn2AE>NIAf#(+m%w(+qGpw1$Fjb95$QF_U{+wRxFrcRAFa2 zy!xOdli&;9=SBCw#*DH^FO^pno6DN^3v%UxucEduecUc++=^%Y77K7J{0wRqN+z|2 z6^U&YbJ@Ce!hD7L1?af~{r|E|I@3pakru|^RPG6M(l--|_+avn2hPH*&t`s@a^W~4 zFfkd=F>%7nXaG*h&IEF-8EG%or9od)U_GH&_2U(~Zs;#l#inHoQ9v*}l01v3A}a7EWAc%1s%ikowUtb$H-ta=AK!E}UE)5FM-^H2MFJ5&E%k9$WBB$sNmt`1yoJ-n_z0k9XvlDyE+F#bVQus9%-3 z#$#E3uF4c%XT7Cz>;RZ$ZH&!cb6=eK# zvfEJe<&ib!va&K^Zm%k;RXvob!4;X}S@qc^EttKj6l@I21evIUS+zasqE+?5`kcbp z1$i|wtWFY50Gk-STs@mOXU-CO57LY&fv);zStfO}-cmZJJ<4SpMb*Cn?O^VAV2~OJ zj)bYXSpujRvpdkNS1|k+v6ew_sQlPN@U!otbQ0)Q*mVp9=^?w=erCP6l9qFyGc1z9Uoq)rNFH{=xDC*P*>Ym)oq zb0*TQkk77Hy!q7GeyH0?~?N2Y_BoTxfio6h4qwg8!%V! zC8f zY1;`$AS|EiVJj1f>i?%;|C=>^6#gtw;6*}#0{{Oa`Ot-C7ATN|0+j#9_+Js3f}rp# zP~Zhf0eKZ(0Sa_SWw`CK7%fzyv*&me9}SE|rzR$6{K4V4FCK~Rrfx-Nq!DWSy>B=& zvE4DV-EP}%vmKmiIp}qc_K)>DYpaHiw%7-1_thLZb7t&JZ(CK!H`#I|er&q6F*MO| z>huwRQ?vV6n_Mi=3LRcjg%(t$uCCqP5}9=P`y1mm4V~4F)5B+GkG95w)x(WFea(@k z)~TWPp)T*~ec>~$UjMPf`)g-U)4O>w`5pc0UG#3=7vSeFG^s#=0tK=u;9Uz(I@SAs zF`7qc9)BV`%X9VjeDzhTV&xqNRACNt4iC4ksy8IYwq%NQl8>KLsm{gR|1VVIOo?^U z9o=WDdZzL$DtJ2+jK-(nDgbt~T{+xSFcF9bhm-de1mUL2OmPm#dg$$!XTWK8JQ$t< znc^;ch3+0ZB^9z$sSx$&I0KLG3^*5OcvPDp%eEZ%v*xN#IvRF1pyAQR|J8_mkc^V6b2Pm0s6c@iDFs@| zN*MD<*n+nrp}07Qlpx0=>xXk;Z{Mz zwH4}R;j0(iP89W#pQezh@;@|)&>)We6+ZKP=|qw*aM^ygqP)eK3(tE0j;i}MQ)Per3(-nV#{$@mPJ#bgw6;W~SzBXDXsFw;8e54O~_j2(*f z*r(6*I9k2l9#3C)Pj$F!Y#`J-;T-o*gbsCd+k!_9w>tX!&e{jMApeAad|)gzUBF-EaMo%MP` zy=R7@DJ$vt7X$^tzryblq`>@vP3i{gj)R&EJRbKAkJ7u2V{j8@GPxs@ZBBdhobt+f zAEKx&@eM8Nw}jsM32jw#s27u_6lGI8tqFg1+F?oR`Ke$iu;>vC>_$Lq=M-y4X1S@cqkHMfJra3T_pkA`M$610-nRbal7y|4yQLBgJpFYMnF0>Kg4NctAw*Vg|4 z5&}7zU3j!S6gYR~X4P8duGQ)yJroRuVrsB6b75AWT@p+3q6_APcF(+SsFGUmG>O*W zL=bcqbVY`RuYG58zD^f~rhurx5N^yq6%Cxuy~d6QCl{`<+0xRfc!X8^*_9>?iB?th z3QD3uqw-Z%7^_x-3nW7pnD_c`!aqag^W+ue1SyAP;a8x*3y%V|CX2d+Z!&sd)*J~1 z{l1x4U6BU3#bWRTuqi^X0FO+?9Ylj%YZUUZFIt5)m@=asdhwhRJKsai zMJ1@u?XHZD|6*K)$fwE6Ne9`0e~cf()9|41D^Oq&3LGq^`ew*dX;2{Mjt0ZyG3#^? zPQp&X7!Lbmm1Ye^nL!lI5s+@ziW<-sgFw0fXK4eAT$UJ52(t2$g;9)WsNyy5I*1Psf=<6*b~gs%StJ&EvV(UbV-CD!=4 z)!S9&w{u`(1G4?#1Z~*hSY#-cmb7QGiD_@1Q$8_yI-5)53+~&>d3;S((*_q+XamEN z_*D88Q7INa%WG!3K3d>x92Ve!e=rH2_HyY${@4yM?b#;CWvb)VG+GZr8=3Rfw5JaC zZF)Q{ojncK7LU8(n6*4DLmHUo4>au^{d|;i6$$4ywHwSk)ou__`?uN+(vtf#yFuEU z=Y2OweyN2@6-)nMRQ|8|{}I*|{Qq2H^IxI51q$Rz0pSV}82_~b_5W!mZ$;$CY;a8x*i;DsYaO(k* zOpASPWsbdajz^kt{_sqPei?GQA>KewYhNJL8}GGEhKB9^)xF1Bo7(#(9RqIvLVLpS zp~GD^dsAO{V5Yap6Y3gko$~iiPIZMQqXRt+&W^5@Sj&Wcw10-$DtgBUx;>+wiI&-c ziT0{~M|<^vyJy-n>kl67Yqt-$hpPw1`oXZT)80N78g1`BSnZh|2zkc(r;m0wP7Jt5 z$NR@xDm{HIj`pU;@uT#8Q%~?z=Ya$2{$J=uLN|VCLEC3}x__ej8f`ynSNEfndS{8H zB`Y)cqiJuRH~Y~=kEq~yF#AT3EcYNuX-DSSAANeUGr#M1n#;Cq5$4-ex~aEtQxH8- zok=tOIi*;##9ZZkWer;vyWm_W*PvZb)~M@0 z`6GgVg51Vt#uJ@wFu1)yce>ZUL z8JQfMq^fkW7|7uN2KOkWh9Mo9! zOZ{nZER^HaA;+VBy0)JRMq_c^nF$^4-J`*ny5w6S_Jys1vp#z2x6cast;6sj<~tPt z^l++A!tcEjua%qeB|b?gyC-)65a-HXcUrWFQQe z&f|V{GhnH78lE5EI0<0=gJ~@6p~%$8Xxs`H8(OC$(U2cZF(MOIjsHIm*1~xAvNSw} zWUhU;dOg$m8+E31Q1jKQ?H*X#wU9+kW_wOKRh3=JeMqEUsuc+@u8ONQW$=tIG?m-g z<1+6Umdbf1+q#fpya=l}=iOs2+qqLXuX$2Yam1s+kr6o5%lPBE9A7k<@pAmyZA4wQcW*PiYSTp&mMit9%E4gF4Oevet%oAh z!2s#kSb&OyhLNSM)_|)UR_b=(+51K#)~*OF*mUXcqf0k#))<0qI}I}gs;q^rxNw_s zVDFgbFP-ZD)AK+0oh;}8yokGgq3JI|3Jk&xXH@_9AcB8|Ux5Pr6qvhiueog9I^q0r zb(a_Sjexn@NbEr79)Lc2PFerFPlJ3Q#$RY?R}KxJcaG$Im+md)SuO|&`QX@MisI2- zAZrkzNO&aoo7qTY;z;03Ae4E$pn@$~d$EG9|A+AH5DJ&)@c;KU@-+Djd6ImDJPNi! z50JN!d&#TG%iyjsncPgKNQ4B*Fu9TRkxtS^8psXANh--+vXg8DcjN2GYGNS-{~7-e z{uTZY@GbJs_y|2ww@!atq60>VF>vq1Pi^VGTBf6vqFDt|RU3gLgv z-w5H)=e-dAY`zJ?pUhte;lIw?A^hPyz1ZTP=Ql$5gZVWO{=a$J>%TKM3*kS^MIik9 zIX{G7pX-P4t8;M25Bk#FVFPtBD>sLYi?_~hJW5PobDrj#7PJrO+bs#BZ*cB-a?u78=GIiW{w_Fe5ow5zW3-S&K=j9C$Cgjx+zCQId%y-&6pH7>kHA!Z^Mf4AHtV!wX zP6~?NC=&(yuEF%zTH!;H$as#&>;yZ<8)ITiSQ`h`4e&x&`=Fj~)Ss2=G(Tyj=FeR0 zSnKq&uAR%GHj!nskp5|spEgrKnw?{DVQCiXWN*ksN1x_E0S5u>8rY2GJ-zVso|B0) zM=o%hR<3G%R8U(5cJw#w15axHfO`K=!T+a`iLa2SK!Fz?1?c*Z(T@@OF;2YjTE|*6 zw_(3}5_xV~wPWgs7+*LR|7X?p-y-}7!72X*IKf{Hr}MAJ$MF_;RQSCxDB!YCCwBtcD&#OE%khY)&unlB zy>pjKz3PJxwtBDKvDa3&%cb7&!3_)L*6wntSA7_V#ZYG`*S6PI6LG1xeelBCoO^B6 z5tn-D2RCfpURzbfrC$AEL0f5MYA6vZZC1O(RaN7vwC8?Q4Q<_Pt3l*ykjc%L6r9u2^0M9y=G@~KIBQjz;zTIu<+^P0*;x)Mc7$HL%EXqmOh zh2A|93+8XZY?yX_o%qcgQ!$m&xbIr^rXiGtJkM5xPpSeSV@L%qr`Tyw- zD}>*_gXaI<9T1@JUZDBEec>d8-@HKcf8&A#;on`L`M-9-3gK5S(EMM%00DaXJk9@w z^Cuzv+3&(ZuJJSRc;*g2a2 z1Lv#|zW*G}|Ij%I(1Y_d|GVc;LinzEn*W{i5`^!Vr}^)jw?gVunxpyO zICm1lH_Xxeubq=1eDxg7|H?Tlg!jy?f$(K>5TKVPX#TrYy}>&YH2?X81mU>^%|Dm0 zLMSI_{@c~_`CDX~|Ezox!ZR|>KP5{L##GI~)3O!P5t-%>%MhRt%|3P#f>D}zM1mk- zgJ8%Cfo~06|4Yb&i2R=X67K!}C-QCbH5l`sfD!*7yej+(6ev*Oc|ielNfAuZ#Pp!q zq9xvIE~W`=dXL$xCEhKHMaY6p?-EUF{D8Sgd+|;)(GvHISbg&jQBdRiH2FVF6*7MN z-=vxIS68XJITvo$)IU<%3+Z~qv6f8kGEJKQ{IM(1%|JnWm1^OFVZ7>jExb_mH%)gY zSJpH`&@^=&4b9@bXL-;MoO$q6Fc4i>ft2p7ZD*EPIj!EAJSuzfw_s3{`l*Qi{nNHdX0E!jtQkgLgB zQbGg(F8m4STE49QE z<}xku5plh`j(%8Nr^X*LUqN3G(+ACKwZ!A*%eAbJnM<|Aqv9Gh?+3)yYW#llDlO|H z=9OCFVe@5L)`!e1w8Z=JU;T-Rt^UMxzZq74+M;)uVfm*CZT%OfkT8XRc!_<&OSig- zu^QEeYF?AA=|{wAQ+BRrM=}?-Tv_s3+H^#@SFZB3Y8g>;O-GlxB(XPno_OcE!X-A= zs=iU@*QpFCPgBninXrWD1x71H1qCN-F_aHkX1jQLgUX>U{quW$seJSEnB5 zU+7o4VBZ;EFr@OATGe6;9vsaeRQZDU&l^tpVz&sS?_APt=5xthFCte>eFDuoGF_^h z{0Ix)WC;8gt4QYlC`T@iy;718nK`11;kKJ?M`N1rpo9HyOPFMW`>HJ7Ba}(lzRf2K?5;qk-XZxRSwo zP;@> zgom9i!Rg>oU*njH$2~gc>294k>h_Kv?Hial+SfDN-{%dscXw6|xW{MOyT={v6OCg7 zO)XXZeJ%E*?(yn@rgnS(m}j)TX<)S7;TeT1U#t4z3Rq8ff0d`nGu|F-btX1qMy1_c zR021ZaA))a>V#VRyL!DgPh%+D)i`~ozZ>ch?(OJ2*4oh1?WyeOwgrzIZguqcowW~i z!Ns%_omK5NxMp_DYx9K;M*ZXVMo*IssBvhrvnzgR*xh)f9a`CKt3Ew2J8;%B;cf1n zuzUJKJsH&TxCes$eeG~rZ9iZec6i1{&i0QrO!trV2BFoqqfPy`c6a}*$30RtFg60z z>K*emH2}q0>;v7yGcae{+8r&^p1%I7fxgbOaOrJupbMyD6&Q8)7SA;p?n8@qp7D6i zP|w*GPsr2a@9p)qH+hDSIAV6s#6WC#cGSCoJRuMCmWCOxefrFBQ&07PV|cc+&okj4 z^R{+Z`bXRClT8D??X&GYqfJ1T-l1b2Pq)L{9~kp^GVcGKXdON3Zkg$y@Q$_5LT6|i z2n~!4pY@FO!2iS0T);6qK6?~8k!N~xEgSG#I0n2sIt5H{`ovC4=3; z0ecv3iLf39nnr!01D7!bTR9N;CVaDz@ILV7b8H0z@8baP_MHvT`z!obFI6_2fTM0${vagNfrjZlcQdTre@SysqXo&FJ>6FabReh z4vz0_*xL>)-Sy@?;JcRAt+GAHUK#oyjGBir0jSMqKKR2~ThMIyf-e3Y6-gn&whJk0C9if9a z1n$F{uVT=(#z|7tIyC{^+}Z(-uo@!~zqO054el!$CZ7HtRA({jM|)Vqa-fW1;hD~k zbyd*|^;)_*8rCy#j*e2)>Op(2jer>UBPhie4qAVcO>i!`D1kauR^=%`X49z@K5nO@Cm$L__}b1a0vYgeF%l& zCH&m0R3j2$>(<Kaz6Sg=tn1bs0HoPp*tJ~Wv8G#488 z3W~;JxHh>f<1WdMGGOPzC}ZDSX$S6<8tE9Wi%yv=G@8ARVKh2rGSRqI(;a@wWMNT_ zcsQ^;1z!nlTEG&AQU)wz>VPtC)trX|%VQcAu*qRqD1F_kVGjqEcWYRn5)_MCt4f<& zGx0G@Ui3)~7Qh*n8ZZiSYsNmtvAk8oLM?XmRIOX!?7g4XU|~=)EHtH!{~dJv$H*T1 zZ}{!FA73tf1=jx?(C^V>D1>&PhO7BHr{1eryP+Vp-N3)*Gv2bWY)~u1JrgnNL0zip zcmpOOukn_J=1M*^I%P7^xIsLU4`s5jtj~`!tVcjp1--KYn3udLlZECAJ~TRIGSRr# z=0}+)OePvPY-sbLOcs{%{3yc~T%b%Xr3|p?c~K?{jg=3L zPMJ(JZeX$Vp-dK*&G}I#a~W*d#MdN!G-aaE$Nxpw|2n?1H1g5KNt-{_6!2iM2jnK- zLM%0Tk&oJ>Q@*E4HO#g*RP!|{Nxp0}ReWfY9V-is8zh9>lu=uf-X^yrKg#G_yB#=X zTLWxu^BHmgoUDwB#>R(6r%WaqH|+NFp-dK*efd!a{0Xo=W22MNUcM&jeI^Ud9zHZW zWirvYcjrf$Of35Nzv%kkmR%#TO;2>G;TgL`ec+@8~5Qn$hV*} z?nC)ehB0A)Ph~6*@-<1POcol64~Er@}o=^mh1DQ44bBbGLF3?>olH@p`ZF0NvqYRtU0G}!_a`H7vr%Waqef(c^{qN>0OD7*= z38BiWB&RDs@?~N1=0`rpgh5?Y#gQ`xk}}z7j`5*MQYH(H z8;)o5S^t^Lh_%Vxn;&H|m%)Y}z9#9E$wZ@%|BJ5wlYC`qmcW&Wdogvv0iEl_O^0fYBo9WjKiQ?M|}s;UjI5>gI4sQh|Sn--x|B%g> z+aCWQY*P#}6f{SxJ56^L7qW$wU^ zt+Zn5b9;LGZQf~rxUqGhse0V+2vvbbyVu*$Y4eZ!n}?dFXNP7RDu=?2o}pRQC@u~L zY{8?!mf3-^L!th$;b{*wT66TAZFh9e9Bl$Kx$f~wPZJoY^?5@cFlHMV8x4W!TxEOr z05$uw4^R`sZZE*LSGRY=`|kGX0e4H)z=UVKC2Tt|{%yk38wKib{v zJ<`=XIpu8zgR+p<-X8Kd_-#E^U44ySpZ!qlz_{1vbMywgHOs*yk*LjM_0hft_`fmK zKMUq)?gp^>0~58e)={vDbR2buCOq&#z|?KL-_bKO(AN%@ef`t@v;APY1|*s2tOVkL zS=%U>#DNVZSOrodfm@tWAd$uffOG`tBP@wNq1NhNun+ZBhRzI6Om+;}8>fAJp~HPm zXS5pJ}sA&J0Y%GAttd9p0d4qG$GK6IeBlflcT{J9Ma)vmSTv#K1&v&@&5`l6|cq zum!DzchJco-vm5^G|;koz}*{cclV9~jnDQ^v`(~7bj}_Hi^vT6(0QAVe*u3F!N0<< zK!Fzy1rpnfk^{dYpNVm7;Iu#7>hL#sI(+VdV*_KY9Yb#a=|DKt+zrOvs(o&np?KG9 zd(>lVJ=POCWN&Y9dV2bLyM}DlQ{9dJvqNLOvjfM#{MP1m42K$K2WDG4k9POQhk9H4 zx-url*0J`!)`|YUPM8e)&-TxbfIY8&945Ewc6aA&yZg`ttR!Im+z&R?4X|>wjzOMk zj{{bUZa<_Os#P1|F)%QvD@b1}OsJkwIx+hEXJGXRboged4W+hz!1%AD<9`k6Md0=G z_2dMx;veJp;xKlAcm0nGFA>~=1$`C03Nmp0Tx&|`$}I@UT%261F1~7)7E8^}d*Sy+ z=V}$!VjQ+uVW6&6+{K!u#*aPI^vD%AJ6G}+YgQPj>oV?QMX9;t{_m6=IPF})Tdb%s zP*(|Wv8zo|&w)u=uG7L>tVv;@u43L|*A+=UU($$d<}J2JVW2LNx7acwwZD*NP>)#bSklR${GK3&I8m&<4`(5VZ1)1O4f7ZTvSQ@cDly@sP{$ zckr9Q0(iUdGvQ$&3NpYyqEDfD)ckD2-(}^cQ^^{s*8Ip7v(Gd;xA4+wjlw`(n|X`1 ztd?x0|8Z{f6JF;g-eOlP4AgZsZ?VOzq}sdGvFF^#TkI-@fx0&E7HeK9ITZEVIj`a^ zcBR5VU03oJTXLD?_RQiRtYw8{{{{4@rbnFXc#B=3Fi_VOyu}um zNOnf#wY@2ujIqu5gZ-|zd%!yIndU$S#-2p_|P^jsH4MMtRvgYhs+ZQ_@Ry?KJa}p;tPh8 z?RM1mn{}M^d^j6}p%CDVgWrc?us(;n*u%vvPo)lXyd;Jkw-`;DS|t>#=!db zgThU~x<7<^IEQ~Sd(V2k`JBTvt@(|Cp=j#!gDj`2Yn|Ty{d^xvZ~p?Mj^kHoNCjj$ zz=xD{sa~|K6W{`5CKq)bb=_-q*PJiSLLP%^@$dl(mI1b z8VrwT&owo@r8@TNrDN9zZ)MvW9q&>nSw~IV>g7XL*|uugklCHbSL(<(XFz=|QGE=l zOQ80JbQ)H7T&5#eg&M}!*LLurvT*6MtMb1!^!z``|9>0!|5Ml|{1J2j?h-u0TJ#9Ne?jEZ`z_`T=s1WFe-IDn|1u0{LiA>6hK?kwn<03v=x*djCrrtk#RDU zxwk1`*;d=XQOCKQ+Z6a@wMRDSsF%1+fyUR~cooAYSU5ST9NUy^p3-jXxl*qz@Apsb zQ?k+8>bzw-TFy_F+^8(TS+i{NKZ$qxDzQq{VNFJ+LN_33c7Kpl7v0N3%l|f>EQp-d1s5Wjkn6zC=Arq%3bUZ zsru{M_M(Ni*c}Q3bv5%ATe4lUl>ujZ|96~Cyv1%;7^tg}w^++Isq!xEkfnjQ*lh{} zb-8(qEiRWT->QAv!@R|oD-6_ih__htR;ey1X(IqvOOb4I>CEIrmu$g_YU7prfv5r;eRdYMmynT!6Of z#5Fo;IhpNe6Ia5Xqo#LBmUFv2F#Fg8Y4_Ref`LZFiJN5Pd%jC6W zfNTZ}pm*aC$p3yTd=zYe4hd%P0sacU_1|@pcPcBjNlk2-8sweIYzhN)-Naq&eyOox zGo87eH}V#{UtyrG6Wqn_lbX9VdC_s+V)rQw)HT3c?CQNzj{rid*=L;nyv6QS7^v$Q zZ?Wt4NIf=9*Qt-U*gXmZb@lQVTee#|@?}s!==`~}hqu_>3Ila@^A;;!EA9ASZ41)H zTkN$81FiBVi#5s$Z>RuyZfCMUqfix^{?7_DfXF*ZFa9Hp{Q*pb`-M*Q-(dSUkoTGo zfNizQG)-0RbyT+1EE_U7qN&!Aajv4zazs-}rIl4Wa?V9R*AWe1wBJ;zV_bG=CFP}h z96EkpY1Lv%6@a#;&90+e+5!s59A2A_jFUafEvJD>FDsyKULzf0M~6%jj;pq)w`PrKX4%NlsiwVe1qI>I(A~YpIp0 zKBi636THRNDh$*W;w`qgMzW~_u~W`*-ePMM2I?B)E!JGE3t5A_#a8P=)=}PKOR6LX z$XKY@afG+nDusc%PVp9Nsg(9#r74jIc#Ex67^ut7TWqmI+OHm1pLP!O7VA(LsB4I~ zShHQKyPzr1`gn`AD-2ZI|M%1V|8e|h(Eaxae-hp!^a%vk|NbSc|3?lj*Axi4f3*V# zmvIU_I2jFwG+6@p(6xCby{4RNOPYKl11HEQZqRY&DWAwhSmV83N61;pH2Fj(&Z>?B z3}^oGiA;=+{_8Z1AfIsZiDeebCo&c)N1eA`N6o3FmnNLZ#94L1r4x0TB@@73)bu)a z?8_#Z0Oq2ytxm_gRL07|&i@8e^8eiWKZAMB1TGozPHXEVsb`ONtZ_T=$Q}1sa7q z0Qx^?d5gl=!P(9;lLI3ZM!drz-p$N|Z@?G^)lSOa4&Z{gN4+K1WW!n)Q z@6vdnDqz&w+LjMlWn1gAAp=Qei;nCDK7RUHB$YtJ>W*d|`7%f<0i*q?LVyJYSO;*0vS#wj;UD0rbnjojL-nLxvS#wj z;g?|JQAG#lHu_!Zk)&g6HRQJX^(YM1C3Bnou56d8nY2;nw)<^Y7_3X?H~c*!RlP<# z+LXC1e~%~()+O_s{w^gbfR(qFI=C=NART!*G<~RRs z(dFkdxBYKRQh=_@|K347_&4}|+=;R90ihT4e;!_v{Lgz#pR<-_9;~MGV4se2Ip;xe zid=i7S4X|X6B}@YTYIdBVG}I1eCu}36B}j%3-z>hbnErxT(r}KmF+$qqphyJOUJm4 zudXRosua4K4zG@mvw~@Y%Jd3Wc{&+Ji)9C21=G~1QZ;n+cWC&QLr9q+{{$iBQ5|*u zLdr~>RVO?;L6=!bNqJv={r?hqHR&T8@IT@EZ~*TTelEOUh=Ko)|3yzC`Qq!pOXfE6 zD?KK)jB2tAncK?mn8ILPGPjvuX`j^i3QZeD=CymX-KckYZ)jg8;Yp^bYQw&+R_%jOJqcB*PyerA$8HHWfEp^j{5tM*s-Tu!gaJRx> zU9x5d$icIfbxB8dYpOr8W(df^EOsdj)+KM}ZL#Q;c53XgQ|7h=^ePOtO4dyQjq=4C zIzgN(>sEk9!8()rKR40yzZ3XR_(9x`k?^>13cMzM0Nu2Z*XMiEF|5yroX21phc^1; z4Yuz?f34XN7>t@;pN@Um#1*h=SGJwh@h)wLNh{elxO~Ve+iqGmWFW4%QAf7C;tHT) zb;k)EIVWS0OR)+t+HX3pV_bG|1#p3NJp(%a{xPAs#KSQ5~s{<@pn>Tur8V3{a?SrU^V`~BQ^fN z0q1{%!2kam{D0mK`u`$&8odm4s4X-QxMbb<&nT<6ZdB_2S0*#kZ2*k|k17n-C2OXD zJbdDabYv@&8EM9V9DL%4!eCu8w=tl2O4_ELQy*~3+}41n6b4%*>*jz)8NLkx$|vfU zfJVUraPBWRC+B;kP}ompyt@6LQ7}K1@oJ`j9G_!Yy7w_vnbaj~rhgp$VB@f&19Kby ztsIhS>0SeDNMvs7zafRex@2zizm-0z_NQz^$!-7VQy8o!|MMgAZq@(aKir@{eExsqNs(K)JDlv5&>Ca+9KX{+;$=qNemfk}a7 z2ELlkQ#wA*DyB&+Gf-9?4KS1z3+KL97g=T?boBW(gdnrz+?eK4tW52vng8#6`2Um1 z^}y&-T{0=ve28j=|IR6MTlq~Y4Av!coB3HHQe~-TX&`gk`9%~4>yo(*{ffg<9a9XG zxh?&|3WIgY+@^l!390s#ti{~6eiI6Vb;;buex)I)>3X^tZgI-o)_x&{!MfyL-p*Az zF1a6I%P_aS-?+kHT{5@9U+I|SPIfMCi@!02!MbGKAvR6Hg=LOoXZ87y^FVFk z{FgqnI_f&7b<9q&@v zDUeadmJeBFTXflwfsFFBj%;~ll)&v&cTDQYIXhD>86{w}-xSd?F1w5pSU+|BA4A_q z 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'success'); } + /** + * Add the exact amount to fill a piggy bank + * + * @covers \FireflyIII\Http\Controllers\PiggyBankController::postAdd + */ + public function testPostAddExact() + { + // find a piggy with current amount = 0. + $piggy = PiggyBank::leftJoin('piggy_bank_repetitions', 'piggy_bank_repetitions.piggy_bank_id', '=', 'piggy_banks.id') + ->where('currentamount', 0) + ->first(['piggy_banks.id', 'targetamount']); + + + $data = ['amount' => strval($piggy->targetamount)]; + $this->be($this->user()); + $this->call('post', route('piggy-banks.add', [$piggy->id]), $data); + $this->assertResponseStatus(302); + $this->assertRedirectedToRoute('piggy-banks.index'); + $this->assertSessionHas('success'); + } + /** * @covers \FireflyIII\Http\Controllers\PiggyBankController::postRemove */ diff --git a/tests/unit/Models/TransactionTypeTest.php b/tests/unit/Models/TransactionTypeTest.php index c47a5f2d6b..51d8a1ea76 100644 --- a/tests/unit/Models/TransactionTypeTest.php +++ b/tests/unit/Models/TransactionTypeTest.php @@ -17,6 +17,9 @@ use FireflyIII\Models\TransactionType; */ class TransactionTypeTest extends TestCase { + /** + * @covers \FireflyIII\Models\TransactionType::isDeposit + */ public function testIsDeposit() { @@ -24,18 +27,27 @@ class TransactionTypeTest extends TestCase $this->assertTrue($transactionType->isDeposit()); } + /** + * @covers \FireflyIII\Models\TransactionType::isOpeningBalance + */ public function testIsOpeningBalance() { $transactionType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first(); $this->assertTrue($transactionType->isOpeningBalance()); } + /** + * @covers \FireflyIII\Models\TransactionType::isTransfer + */ public function testIsTransfer() { $transactionType = TransactionType::whereType(TransactionType::TRANSFER)->first(); $this->assertTrue($transactionType->isTransfer()); } + /** + * @covers \FireflyIII\Models\TransactionType::isWithdrawal + */ public function testIsWithdrawal() { $transactionType = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); From 540fc4f924dbdd300058a1a5309f0f4fb065b9e1 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 1 Jan 2017 17:01:29 +0100 Subject: [PATCH 462/709] Fix sort [skip ci] --- public/js/ff/piggy-banks/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/ff/piggy-banks/index.js b/public/js/ff/piggy-banks/index.js index 4b412b40bf..486f93d41e 100644 --- a/public/js/ff/piggy-banks/index.js +++ b/public/js/ff/piggy-banks/index.js @@ -73,7 +73,7 @@ function stopSorting() { "use strict"; $('.loadSpin').addClass('fa fa-refresh fa-spin'); var order = []; - $.each($('#sortable>tbody>tr'), function (i, v) { + $.each($('#sortable-piggy>tbody>tr'), function (i, v) { var holder = $(v); var id = holder.data('id'); order.push(id); From 0341a04ee3f4dee52b7533202fd90f313299d7ac Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 1 Jan 2017 20:10:30 +0100 Subject: [PATCH 463/709] Warning about locale. [skip ci] --- resources/lang/en_US/firefly.php | 1 + resources/views/preferences/index.twig | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 3a8ade8687..cf3c1515a1 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -103,6 +103,7 @@ return [ 'all_periods' => 'All periods', 'current_period' => 'Current period', 'show_the_current_period_and_overview' => 'Show the current period and overview', + 'pref_languages_locale' => 'For a language other than English to work properly, your operating system must be equipped with the correct locale-information. If these are not present, currency data, dates and amounts may be formatted wrong.', // repeat frequencies: 'repeat_freq_yearly' => 'yearly', diff --git a/resources/views/preferences/index.twig b/resources/views/preferences/index.twig index 96c0b98b3d..65080962ba 100644 --- a/resources/views/preferences/index.twig +++ b/resources/views/preferences/index.twig @@ -45,6 +45,11 @@
{% endif %} {% endfor %} + +

+
+ {{ 'pref_languages_locale'|_ }} +

From 32f8747f2e8f37d1f258968580745e6733d8fe66 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 1 Jan 2017 20:14:32 +0100 Subject: [PATCH 464/709] Update read [skip ci] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 11c105c0a6..7ffc52040b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Firefly III: A personal finances manager -[![Requires PHP7](https://img.shields.io/badge/php-7.0-red.svg)](https://secure.php.net/downloads.php#v7.0.4) [![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable)](https://packagist.org/packages/grumpydictator/firefly-iii) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/JC5/firefly-iii/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/JC5/firefly-iii/?branch=master) +[![Requires PHP7](https://img.shields.io/badge/php-7.0-red.svg)](https://secure.php.net/downloads.php#v7.0.4) [![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable)](https://packagist.org/packages/grumpydictator/firefly-iii) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/?branch=master) [![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=master)](https://travis-ci.org/JC5/firefly-iii) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA) From 3191a6c12b90aca69629e9af5dcb1c4b75812da4 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 1 Jan 2017 20:21:51 +0100 Subject: [PATCH 465/709] Update readme [skip ci] --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 7ffc52040b..5005beca67 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,11 @@ # Firefly III: A personal finances manager -[![Requires PHP7](https://img.shields.io/badge/php-7.0-red.svg)](https://secure.php.net/downloads.php#v7.0.4) [![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable)](https://packagist.org/packages/grumpydictator/firefly-iii) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/?branch=master) - -[![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=master)](https://travis-ci.org/JC5/firefly-iii) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA) +[![Requires PHP7](https://img.shields.io/badge/php-7.0-red.svg)](https://secure.php.net/downloads.php#v7.0.4) [![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable)](https://packagist.org/packages/grumpydictator/firefly-iii) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/firefly-iii/firefly-iii/?branch=master) [![Build Status](https://travis-ci.org/firefly-iii/firefly-iii.svg?branch=master)](https://travis-ci.org/firefly-iii/firefly-iii) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA) [![The index of Firefly III](https://i.nder.be/hurdhgyg/400)](https://i.nder.be/h2b37243) [![The account overview of Firefly III](https://i.nder.be/hnkfkdpr/400)](https://i.nder.be/hv70pbwc) [![The useful financial reports of Firefly III](https://i.nder.be/h7sk6nb7/400)](https://i.nder.be/ccn0u2mp) [![Even more useful reports in Firefly III](https://i.nder.be/g237hr35/400)](https://i.nder.be/gm8hbh7z) -_(You can click on the images for a better view)_ - "Firefly III" is a financial manager. It can help you keep track of expenses, income, budgets and everything in between. It even supports credit cards, shared household accounts and savings accounts! It's pretty fancy. You should use it to save and organise money. ## Try it out! From 49982d6eb113cb9df78dfe2cc2c76626d7ea88a8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 1 Jan 2017 20:32:39 +0100 Subject: [PATCH 466/709] Updated various links to reflect the new repository location [skip ci] --- CHANGELOG.md | 2 +- composer.json | 4 ++-- config/upgrade.php | 9 +-------- resources/lang/en_US/firefly.php | 2 +- resources/views/emails/error-html.twig | 2 +- resources/views/emails/error-text.twig | 2 +- resources/views/emails/registered-html.twig | 8 +------- resources/views/emails/registered-text.twig | 5 ++--- resources/views/layout/default.twig | 2 +- 9 files changed, 11 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b57b07d91a..80f72c0a53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -298,7 +298,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - Bug in the mass edit routines. -- Firefly III over a proxy will now work (see [issue #290](https://github.com/JC5/firefly-iii/issues/290)), thanks @dfiel for reporting. +- Firefly III over a proxy will now work (see [issue #290](https://github.com/firefly-iii/firefly-iii/issues/290)), thanks @dfiel for reporting. - Sneaky bug in the import routine, fixed by @Bonno ## [3.10.1] - 2016-08-25 diff --git a/composer.json b/composer.json index 27a784e1bf..af2091da01 100755 --- a/composer.json +++ b/composer.json @@ -15,13 +15,13 @@ "management" ], "license": "MIT", - "homepage": "https://github.com/JC5/firefly-iii", + "homepage": "https://github.com/firefly-iii/firefly-iii", "type": "project", "authors": [ { "name": "James Cole", "email": "thegrumpydictator@gmail.com", - "homepage": "https://github.com/JC5", + "homepage": "https://github.com/firefly-iii", "role": "Developer" } ], diff --git a/config/upgrade.php b/config/upgrade.php index a36422fe29..e88f92c76e 100644 --- a/config/upgrade.php +++ b/config/upgrade.php @@ -12,12 +12,5 @@ declare(strict_types = 1); return [ - 'text' => [ - '3.7' => 'Because of the upgrade to Laravel 5.2, several manual changes must be made to your Firefly III installation. ' . - 'Please follow the instructions on the following page: https://github.com/JC5/firefly-iii/wiki/Upgrade-to-3.7.0', - '3.8' => 'This version of Firefly III requires PHP 7.0.', - '3.10' => 'Please find the full upgrade instructions here: https://github.com/JC5/firefly-iii/wiki/Upgrade-to-3.10', - '4.0' => 'Please find the full upgrade instructions here: https://github.com/JC5/firefly-iii/wiki/Upgrade-to-4.0', - '4.1' => 'Please find the full upgrade instructions here: https://github.com/JC5/firefly-iii/wiki/Upgrade-to-4.0', - ], + 'text' => [], ]; diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index cf3c1515a1..83bd9a8185 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -84,7 +84,7 @@ return [ 'destination_accounts' => 'Destination account(s)', 'user_id_is' => 'Your user id is :user', 'field_supports_markdown' => 'This field supports Markdown.', - 'need_more_help' => 'If you need more help using Firefly III, please open a ticket on Github.', + 'need_more_help' => 'If you need more help using Firefly III, please open a ticket on Github.', 'nothing_to_display' => 'There are no transactions to show you', 'show_all_no_filter' => 'Show all transactions without grouping them by date.', 'expenses_by_category' => 'Expenses by category', diff --git a/resources/views/emails/error-html.twig b/resources/views/emails/error-html.twig index fa7a6e1829..afc890506a 100644 --- a/resources/views/emails/error-html.twig +++ b/resources/views/emails/error-html.twig @@ -34,7 +34,7 @@ This can help fix the bug you just encountered.

- If you prefer, you can also open a new issue on Github. + If you prefer, you can also open a new issue on Github.

diff --git a/resources/views/emails/error-text.twig b/resources/views/emails/error-text.twig index 9f299a377e..913d54d741 100644 --- a/resources/views/emails/error-text.twig +++ b/resources/views/emails/error-text.twig @@ -21,7 +21,7 @@ the bug you just encountered. If you prefer, you can also open a new issue here: -https://github.com/JC5/firefly-iii/issues/new +https://github.com/firefly-iii/firefly-iii/issues/new The full stacktrace is below: diff --git a/resources/views/emails/registered-html.twig b/resources/views/emails/registered-html.twig index fcc1688488..4aec81c009 100644 --- a/resources/views/emails/registered-html.twig +++ b/resources/views/emails/registered-html.twig @@ -12,13 +12,7 @@ There is a help-icon in the top right corner of each page. If you need help, click it!

  • - If you haven't already, please read the - first use guide and the - full description. -
  • -
  • - If this installation of Firefly is configured to send activation mails as well, you should get an activation - link very soon! + If you haven't already, please read the full description.
  • {% include 'emails.footer-html' %} diff --git a/resources/views/emails/registered-text.twig b/resources/views/emails/registered-text.twig index 8c2aa00143..4877bf52fd 100644 --- a/resources/views/emails/registered-text.twig +++ b/resources/views/emails/registered-text.twig @@ -14,8 +14,7 @@ Password reset: {{ address }}/password/reset Documentation: -https://github.com/JC5/firefly-iii/wiki/First-use -http://jc5.github.io/firefly-iii//description/ +https://github.com/firefly-iii/firefly-iii +https://firefly-iii.github.io/ -The registration has been created from IP {{ ip }} {% include 'emails.footer-text' %} diff --git a/resources/views/layout/default.twig b/resources/views/layout/default.twig index 58c2d5c7e6..0295b5059c 100644 --- a/resources/views/layout/default.twig +++ b/resources/views/layout/default.twig @@ -139,7 +139,7 @@ - Firefly III + Firefly III {% include('partials.control-bar') %} From b7679b5c60cd56a8df053ea325917dbfdaa682af Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 1 Jan 2017 20:33:38 +0100 Subject: [PATCH 467/709] Updated composer.lock. --- composer.lock | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/composer.lock b/composer.lock index 2c128e7a1a..cee08cb19e 100644 --- a/composer.lock +++ b/composer.lock @@ -240,16 +240,16 @@ }, { "name": "doctrine/annotations", - "version": "v1.3.0", + "version": "v1.3.1", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "30e07cf03edc3cd3ef579d0dd4dd8c58250799a5" + "reference": "bd4461328621bde0ae6b1b2675fbc6aca4ceb558" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/30e07cf03edc3cd3ef579d0dd4dd8c58250799a5", - "reference": "30e07cf03edc3cd3ef579d0dd4dd8c58250799a5", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/bd4461328621bde0ae6b1b2675fbc6aca4ceb558", + "reference": "bd4461328621bde0ae6b1b2675fbc6aca4ceb558", "shasum": "" }, "require": { @@ -304,7 +304,7 @@ "docblock", "parser" ], - "time": "2016-10-24T11:45:47+00:00" + "time": "2016-12-30T15:59:45+00:00" }, { "name": "doctrine/cache", @@ -1888,23 +1888,24 @@ }, { "name": "swiftmailer/swiftmailer", - "version": "v5.4.4", + "version": "v5.4.5", "source": { "type": "git", "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "545ce9136690cea74f98f86fbb9c92dd9ab1a756" + "reference": "cd142238a339459b10da3d8234220963f392540c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/545ce9136690cea74f98f86fbb9c92dd9ab1a756", - "reference": "545ce9136690cea74f98f86fbb9c92dd9ab1a756", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/cd142238a339459b10da3d8234220963f392540c", + "reference": "cd142238a339459b10da3d8234220963f392540c", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { - "mockery/mockery": "~0.9.1" + "mockery/mockery": "~0.9.1", + "symfony/phpunit-bridge": "~3.2" }, "type": "library", "extra": { @@ -1937,7 +1938,7 @@ "mail", "mailer" ], - "time": "2016-11-24T01:01:23+00:00" + "time": "2016-12-29T10:02:40+00:00" }, { "name": "symfony/console", @@ -3871,16 +3872,16 @@ }, { "name": "phpunit/phpunit", - "version": "5.7.4", + "version": "5.7.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "af91da3f2671006ff5d0628023de3b7ac4d1ef09" + "reference": "50fd2be8f3e23e91da825f36f08e5f9633076ffe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/af91da3f2671006ff5d0628023de3b7ac4d1ef09", - "reference": "af91da3f2671006ff5d0628023de3b7ac4d1ef09", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/50fd2be8f3e23e91da825f36f08e5f9633076ffe", + "reference": "50fd2be8f3e23e91da825f36f08e5f9633076ffe", "shasum": "" }, "require": { @@ -3949,7 +3950,7 @@ "testing", "xunit" ], - "time": "2016-12-13T16:19:44+00:00" + "time": "2016-12-28T07:18:51+00:00" }, { "name": "phpunit/phpunit-mock-objects", From 27cd9fac8a00036e180eaa22072ccc8fa50da54c Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 2 Jan 2017 08:07:28 +0100 Subject: [PATCH 468/709] This fixes #513 [skip ci] --- app/Helpers/Collector/JournalCollector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index 7ea500b656..d5d881196a 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -563,7 +563,7 @@ class JournalCollector implements JournalCollectorInterface return $set; } if ($this->joinedOpposing === false) { - Log::error('Cannot filter internal transfers because no opposing information is present.'); + Log::info('Cannot filter internal transfers because no opposing information is present.'); return $set; } From 3cd054047461f9bb144185f6162abc15b5ba1591 Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 2 Jan 2017 08:13:10 +0100 Subject: [PATCH 469/709] Missing language lines [skip ci] --- resources/lang/en_US/firefly.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 83bd9a8185..fb60f4396c 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -104,6 +104,8 @@ return [ 'current_period' => 'Current period', 'show_the_current_period_and_overview' => 'Show the current period and overview', 'pref_languages_locale' => 'For a language other than English to work properly, your operating system must be equipped with the correct locale-information. If these are not present, currency data, dates and amounts may be formatted wrong.', + 'budget_in_period' => '":name" between :start and :end', + 'budget_in_period_breadcrumb' => 'Between :start and :end', // repeat frequencies: 'repeat_freq_yearly' => 'yearly', From 533797fc9ed0cfd267e25dc54121a5b8514030d6 Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 2 Jan 2017 08:30:20 +0100 Subject: [PATCH 470/709] Update javascript code. --- public/js/ff/accounts/show.js | 7 +++---- public/js/ff/bills/show.js | 2 +- public/js/ff/budgets/index.js | 18 ++---------------- public/js/ff/budgets/show.js | 2 ++ public/js/ff/categories/show-by-date.js | 2 ++ public/js/ff/charts.js | 9 --------- public/js/ff/export/index.js | 5 ----- public/js/ff/firefly.js | 15 +++++++-------- public/js/ff/import/status.js | 16 +--------------- public/js/ff/index.js | 5 +---- public/js/ff/piggy-banks/index.js | 2 +- public/js/ff/reports/audit/all.js | 5 ----- public/js/ff/reports/budget/month.js | 1 - public/js/ff/reports/category/month.js | 1 - public/js/ff/reports/default/all.js | 3 --- public/js/ff/reports/index.js | 2 -- public/js/ff/rules/create-edit.js | 7 ------- public/js/ff/rules/create.js | 1 - public/js/ff/rules/edit.js | 2 -- public/js/ff/rules/index.js | 5 ++--- public/js/ff/split/journal/from-store.js | 19 ------------------- public/js/ff/transactions/create.js | 1 - resources/views/layout/default.twig | 4 ++-- 23 files changed, 24 insertions(+), 110 deletions(-) diff --git a/public/js/ff/accounts/show.js b/public/js/ff/accounts/show.js index 1d53f11c1b..fc41371659 100644 --- a/public/js/ff/accounts/show.js +++ b/public/js/ff/accounts/show.js @@ -8,6 +8,8 @@ * See the LICENSE file for details. */ +/** global: chartUri, incomeCategoryUri, expenseCategoryUri, expenseBudgetUri */ + var fixHelper = function (e, tr) { "use strict"; var $originals = tr.children(); @@ -53,8 +55,6 @@ $(function () { } } ).disableSelection(); - } else { - console.log('its null'); } }); @@ -62,7 +62,6 @@ $(function () { function sortStop(event, ui) { "use strict"; var current = $(ui.item); - console.log('sort stop'); var thisDate = current.data('date'); var originalBG = current.css('backgroundColor'); @@ -86,7 +85,7 @@ function sortStop(event, ui) { }); // do extra animation when done? - $.post('transactions/reorder', {items: submit, date: thisDate, _token: token}); + $.post('transactions/reorder', {items: submit, date: thisDate}); current.animate({backgroundColor: "#5cb85c"}, 200, function () { $(this).animate({backgroundColor: originalBG}, 200); diff --git a/public/js/ff/bills/show.js b/public/js/ff/bills/show.js index 3964f90dcb..f67c246ca2 100644 --- a/public/js/ff/bills/show.js +++ b/public/js/ff/bills/show.js @@ -8,7 +8,7 @@ * See the LICENSE file for details. */ -/* global comboChart, billID */ +/** global: billUri */ $(function () { "use strict"; diff --git a/public/js/ff/budgets/index.js b/public/js/ff/budgets/index.js index 95a4eca219..1f8ee67597 100644 --- a/public/js/ff/budgets/index.js +++ b/public/js/ff/budgets/index.js @@ -8,6 +8,8 @@ * See the LICENSE file for details. */ +/** global: spent, budgeted, available, currencySymbol */ + function drawSpentBar() { "use strict"; if ($('.spentBar').length > 0) { @@ -79,11 +81,6 @@ function updateBudgetedAmounts(e) { } }); } - - - console.log('Budget id is ' + id); - console.log('Difference = ' + (value - original )); - } $(function () { @@ -102,17 +99,6 @@ $(function () { */ $('input[type="number"]').on('input', updateBudgetedAmounts); - - /* - Draw the charts, if necessary: - */ - if (typeof budgetID !== 'undefined' && typeof repetitionID === 'undefined') { - columnChart('chart/budget/' + budgetID, 'budgetOverview'); - } - if (typeof budgetID !== 'undefined' && typeof repetitionID !== 'undefined') { - lineChart('chart/budget/' + budgetID + '/' + repetitionID, 'budgetOverview'); - } - }); function updateIncome() { diff --git a/public/js/ff/budgets/show.js b/public/js/ff/budgets/show.js index 1e7fb668dc..07cf18ed9e 100644 --- a/public/js/ff/budgets/show.js +++ b/public/js/ff/budgets/show.js @@ -8,6 +8,8 @@ * See the LICENSE file for details. */ +/** global: budgetChartUri */ + $(function () { "use strict"; diff --git a/public/js/ff/categories/show-by-date.js b/public/js/ff/categories/show-by-date.js index 6b5ee6b775..e97dab4db8 100644 --- a/public/js/ff/categories/show-by-date.js +++ b/public/js/ff/categories/show-by-date.js @@ -8,6 +8,8 @@ * See the LICENSE file for details. */ +/** global: specific */ + $(function () { "use strict"; columnChart(specific, 'period-specific-period'); diff --git a/public/js/ff/charts.js b/public/js/ff/charts.js index de470ba9e4..82977288d2 100644 --- a/public/js/ff/charts.js +++ b/public/js/ff/charts.js @@ -125,7 +125,6 @@ function doubleYChart(URI, container) { ]; options.stacked = true; options.scales.xAxes[0].stacked = true; - // console.log(options); var chartType = 'bar'; @@ -240,7 +239,6 @@ function pieChart(URI, container) { */ function drawAChart(URI, container, chartType, options, colorData) { if ($('#' + container).length === 0) { - console.log('No container called ' + container + ' was found.'); return; } @@ -249,7 +247,6 @@ function drawAChart(URI, container, chartType, options, colorData) { if (data.labels.length === 0) { - console.log(chartType + " chart in " + container + " has no data."); // remove the chart container + parent var holder = $('#' + container).parent().parent(); if (holder.hasClass('box') || holder.hasClass('box-body')) { @@ -261,8 +258,6 @@ function drawAChart(URI, container, chartType, options, colorData) { boxBody = holder; } boxBody.empty().append($('

    ').append($('').text(noDataForChart))); - } else { - console.log("Want to add text but holder has classes: " + holder.attr("class")); } return; } @@ -273,13 +268,11 @@ function drawAChart(URI, container, chartType, options, colorData) { } if (allCharts.hasOwnProperty(container)) { - console.log('Will draw updated ' + chartType + ' chart'); allCharts[container].data.datasets = data.datasets; allCharts[container].data.labels = data.labels; allCharts[container].update(); } else { // new chart! - console.log('Will draw new ' + chartType + 'chart'); var ctx = document.getElementById(container).getContext("2d"); allCharts[container] = new Chart(ctx, { type: chartType, @@ -289,8 +282,6 @@ function drawAChart(URI, container, chartType, options, colorData) { } }).fail(function () { - console.log('Failed to draw ' + chartType + ' in container ' + container); $('#' + container).addClass('general-chart-error'); }); - console.log('URL for ' + chartType + ' chart : ' + URI); } diff --git a/public/js/ff/export/index.js b/public/js/ff/export/index.js index eeed44981f..e1699c3c8d 100644 --- a/public/js/ff/export/index.js +++ b/public/js/ff/export/index.js @@ -24,7 +24,6 @@ $(function () { function startExport() { "use strict"; - console.log('Start export...'); hideForm(); showLoading(); hideError(); @@ -75,15 +74,12 @@ function showError(text) { function callExport() { "use strict"; - console.log('Start callExport()...') var data = $('#export').serialize(); // call status, keep calling it until response is "finished"? intervalId = window.setInterval(checkStatus, 500); $.post('export/submit', data).done(function (data) { - console.log('Export hath succeeded!'); - // stop polling: window.clearTimeout(intervalId); @@ -114,7 +110,6 @@ function callExport() { function checkStatus() { "use strict"; - console.log('get status...'); $.getJSON('export/status/' + jobKey).done(function (data) { putStatusText(data.status); }); diff --git a/public/js/ff/firefly.js b/public/js/ff/firefly.js index 2b105d4386..c35bd4fbe8 100644 --- a/public/js/ff/firefly.js +++ b/public/js/ff/firefly.js @@ -11,6 +11,12 @@ $(function () { "use strict"; + $.ajaxSetup({ + headers: { + 'X-CSRF-Token': $('meta[name="_token"]').attr('content') + } + }); + // when you click on a currency, this happens: $('.currency-option').click(currencySelect); @@ -49,18 +55,12 @@ $(function () { $.post(dateRangeConfig.URL, { start: start.format('YYYY-MM-DD'), end: end.format('YYYY-MM-DD'), - label: label, - _token: token + label: label }).done(function () { - console.log('Succesfully sent new date range [' + start.format('YYYY-MM-DD') + '-' + end.format('YYYY-MM-DD') + '].'); window.location.reload(true); }).fail(function () { - console.log('Could not send new date range.'); alert('Could not change date range'); - }); - - //alert('A date range was chosen: ' + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD')); } ); @@ -133,7 +133,6 @@ function triggerList(e) { "use strict"; var link = $(e.target); var table = link.parent().parent().parent().parent(); - console.log('data-hidden = ' + table.attr('data-hidden')); if (table.attr('data-hidden') === 'no') { // hide all elements, return false. table.find('.overListLength').hide(); diff --git a/public/js/ff/import/status.js b/public/js/ff/import/status.js index 0b24c0c7f5..2b15cac1b6 100644 --- a/public/js/ff/import/status.js +++ b/public/js/ff/import/status.js @@ -28,20 +28,17 @@ $(function () { function checkImportStatus() { "use strict"; - console.log('checkImportStatus()'); $.getJSON(jobImportUrl).done(reportOnJobImport).fail(failedJobImport); } function importComplete(data) { "use strict"; - console.log('importComplete()'); var bar = $('#import-status-bar'); bar.removeClass('active'); } function updateBar(data) { "use strict"; - console.log('updateBar()'); var bar = $('#import-status-bar'); if (data.showPercentage) { bar.addClass('progress-bar-success').removeClass('progress-bar-info'); @@ -63,7 +60,6 @@ function updateBar(data) { function reportErrors(data) { "use strict"; - console.log('reportErrors()'); if (data.errors.length == 1) { $('#import-status-error-intro').text(langImportSingleError); //'An error has occured during the import. The import can continue, however.' @@ -83,21 +79,18 @@ function reportErrors(data) { function reportStatus(data) { "use strict"; - console.log('reportStatus()'); $('#import-status-txt').removeClass('text-danger').text(data.statusText); } function kickStartJob() { "use strict"; - console.log('kickStartJob()'); - $.post(jobStartUrl, {_token: token}); + $.post(jobStartUrl); startedTheImport(); startedImport = true; } function updateTimeout(data) { "use strict"; - console.log('updateTimeout()'); if (data.stepsDone != stepCount) { stepCount = data.stepsDone; currentLimit = 0; @@ -105,12 +98,10 @@ function updateTimeout(data) { } currentLimit = currentLimit + interval; - // console.log("stepCount: " + stepCount + ", stepsDone: " + data.stepsDone + ", currentLimit: " + currentLimit); } function timeoutError() { "use strict"; - console.log('timeoutError()'); // set status $('#import-status-txt').addClass('text-danger').text(langImportTimeOutError); @@ -121,13 +112,11 @@ function timeoutError() { function importJobFinished(data) { "use strict"; - console.log('importJobFinished() = ' + data.finished); return data.finished; } function finishedJob(data) { "use strict"; - console.log('finishedJob()'); // "There was an error during the import routine. Please check the log files. The error seems to be: '" $('#import-status-txt').removeClass('text-danger').addClass('text-success').text(langImportFinished); @@ -142,7 +131,6 @@ function finishedJob(data) { function reportOnJobImport(data) { "use strict"; - console.log('reportOnJobImport()'); updateBar(data); reportErrors(data); reportStatus(data); @@ -173,13 +161,11 @@ function reportOnJobImport(data) { function startedTheImport() { "use strict"; - console.log('startedTheImport()'); setTimeout(checkImportStatus, interval); } function failedJobImport(jqxhr, textStatus, error) { "use strict"; - console.log('failedJobImport()'); // set status // "There was an error during the import routine. Please check the log files. The error seems to be: '" $('#import-status-txt').addClass('text-danger').text(langImportFatalError + ' ' + textStatus + ' ' + error); diff --git a/public/js/ff/index.js b/public/js/ff/index.js index 27d69d5bc0..04a17e9133 100644 --- a/public/js/ff/index.js +++ b/public/js/ff/index.js @@ -24,8 +24,6 @@ $(function () { tour.init(); // Start the tour tour.start(); - }).fail(function () { - console.log('Already had tour.'); }); } @@ -34,7 +32,7 @@ $(function () { function endTheTour() { "use strict"; - $.post('json/end-tour', {_token: token}); + $.post('json/end-tour'); } @@ -69,5 +67,4 @@ function putData(data) { function failData() { "use strict"; - console.log('Failed to get box!'); } \ No newline at end of file diff --git a/public/js/ff/piggy-banks/index.js b/public/js/ff/piggy-banks/index.js index 486f93d41e..70630ad6d5 100644 --- a/public/js/ff/piggy-banks/index.js +++ b/public/js/ff/piggy-banks/index.js @@ -78,7 +78,7 @@ function stopSorting() { var id = holder.data('id'); order.push(id); }); - $.post('piggy-banks/sort', {_token: token, order: order}).done(function () { + $.post('piggy-banks/sort', {order: order}).done(function () { $('.loadSpin').removeClass('fa fa-refresh fa-spin'); }); } \ No newline at end of file diff --git a/public/js/ff/reports/audit/all.js b/public/js/ff/reports/audit/all.js index 2c58e0ac54..dc28051a29 100644 --- a/public/js/ff/reports/audit/all.js +++ b/public/js/ff/reports/audit/all.js @@ -18,7 +18,6 @@ $(function () { arr.forEach(function (val) { $('input[type="checkbox"][value="' + val + '"]').prop('checked', true); }); - console.log('arr from cookie is ' + arr) } else { // no cookie? read list, store in array 'arr' // all account ids: @@ -45,7 +44,6 @@ function clickColumnOption() { function storeCheckboxes(checkboxes) { "use strict"; // store new cookie with those options: - console.log('Store new cookie with those options: ' + checkboxes); createCookie('audit-option-checkbox', checkboxes, 365); } @@ -59,7 +57,6 @@ function readCheckboxes() { checkboxes.push(c.val()); } }); - console.log('arr is now (default): ' + checkboxes); return checkboxes; } @@ -69,11 +66,9 @@ function showOnlyColumns(checkboxes) { for (var i = 0; i < hideable.length; i++) { var opt = hideable[i]; if(checkboxes.indexOf(opt) > -1) { - console.log(opt + ' is in checkboxes'); $('td.hide-' + opt).show(); $('th.hide-' + opt).show(); } else { - console.log(opt + ' is NOT in checkboxes'); $('th.hide-' + opt).hide(); $('td.hide-' + opt).hide(); } diff --git a/public/js/ff/reports/budget/month.js b/public/js/ff/reports/budget/month.js index c103e00dff..c7aad72832 100644 --- a/public/js/ff/reports/budget/month.js +++ b/public/js/ff/reports/budget/month.js @@ -47,7 +47,6 @@ function redrawPieChart(container, uri) { others = '1'; } uri = uri.replace('OTHERS', others); - console.log('URI for ' + container + ' is ' + uri); pieChart(uri, container); diff --git a/public/js/ff/reports/category/month.js b/public/js/ff/reports/category/month.js index 607d635205..d8ba767acd 100644 --- a/public/js/ff/reports/category/month.js +++ b/public/js/ff/reports/category/month.js @@ -57,7 +57,6 @@ function redrawPieChart(container, uri) { others = '1'; } uri = uri.replace('OTHERS', others); - console.log('URI for ' + container + ' is ' + uri); pieChart(uri, container); diff --git a/public/js/ff/reports/default/all.js b/public/js/ff/reports/default/all.js index b5e4bf970e..351b4b30bf 100644 --- a/public/js/ff/reports/default/all.js +++ b/public/js/ff/reports/default/all.js @@ -62,7 +62,6 @@ function respondInfoButton(data) { function loadAjaxPartial(holder, uri) { "use strict"; - console.log('Going to grab URI ' + uri); $.get(uri).done(function (data) { displayAjaxPartial(data, holder); }).fail(function () { @@ -72,7 +71,6 @@ function loadAjaxPartial(holder, uri) { function displayAjaxPartial(data, holder) { "use strict"; - console.log('Display stuff in ' + holder); var obj = $('#' + holder); obj.removeClass('loading').html(data); @@ -98,7 +96,6 @@ function displayAjaxPartial(data, holder) { function failAjaxPartial(uri, holder) { "use strict"; - console.log('Failed to load: ' + uri); $('#' + holder).removeClass('loading').addClass('general-chart-error'); } diff --git a/public/js/ff/reports/index.js b/public/js/ff/reports/index.js index f146d78bf3..5033aecef0 100644 --- a/public/js/ff/reports/index.js +++ b/public/js/ff/reports/index.js @@ -26,7 +26,6 @@ $(function () { // set values from cookies, if any: if (!(readCookie('report-type') === null)) { - console.log(readCookie('report-type')); $('select[name="report_type"]').val(readCookie('report-type')); } @@ -61,7 +60,6 @@ function getReportOptions() { var reportType = $('select[name="report_type"]').val(); $('#extra-options').empty(); $('#extra-options').addClass('loading'); - console.log('Changed report type to ' + reportType); $.getJSON('reports/options/' + reportType, function (data) { $('#extra-options').removeClass('loading').html(data.html); diff --git a/public/js/ff/rules/create-edit.js b/public/js/ff/rules/create-edit.js index 98b77e9b4d..889777d7bf 100644 --- a/public/js/ff/rules/create-edit.js +++ b/public/js/ff/rules/create-edit.js @@ -11,12 +11,6 @@ var triggerCount = 0; var actionCount = 0; -$(function () { - "use strict"; - console.log('edit-create'); -}); - - function addNewTrigger() { "use strict"; triggerCount++; @@ -41,7 +35,6 @@ function addNewAction() { actionCount++; $.getJSON('json/action', {count: actionCount}).done(function (data) { - //console.log(data.html); $('tbody.rule-action-tbody').append(data.html); // add action things. diff --git a/public/js/ff/rules/create.js b/public/js/ff/rules/create.js index 1ad133f9d7..739762d228 100644 --- a/public/js/ff/rules/create.js +++ b/public/js/ff/rules/create.js @@ -10,7 +10,6 @@ $(function () { "use strict"; - console.log("create"); if (triggerCount === 0) { addNewTrigger(); } diff --git a/public/js/ff/rules/edit.js b/public/js/ff/rules/edit.js index 16191e2ae8..7ff822a186 100644 --- a/public/js/ff/rules/edit.js +++ b/public/js/ff/rules/edit.js @@ -10,8 +10,6 @@ $(function () { "use strict"; - console.log("edit"); - if (triggerCount === 0) { addNewTrigger(); } diff --git a/public/js/ff/rules/index.js b/public/js/ff/rules/index.js index c8642209e2..b9b4045b5c 100644 --- a/public/js/ff/rules/index.js +++ b/public/js/ff/rules/index.js @@ -48,7 +48,6 @@ function sortStop(event, ui) { var ruleId = current.parent().data('id'); var entries = []; // who am i? - console.log('Rule: #' + current.parent().data('id')); $.each(parent.children(), function (i, v) { var trigger = $(v); @@ -58,11 +57,11 @@ function sortStop(event, ui) { }); if (parent.hasClass('rule-triggers')) { - $.post('rules/trigger/order/' + ruleId, {_token: token, triggers: entries}).fail(function () { + $.post('rules/trigger/order/' + ruleId, {triggers: entries}).fail(function () { alert('Could not re-order rule triggers. Please refresh the page.'); }); } else { - $.post('rules/action/order/' + ruleId, {_token: token, actions: entries}).fail(function () { + $.post('rules/action/order/' + ruleId, {actions: entries}).fail(function () { alert('Could not re-order rule actions. Please refresh the page.'); }); diff --git a/public/js/ff/split/journal/from-store.js b/public/js/ff/split/journal/from-store.js index ef3480f827..03ceb49ba5 100644 --- a/public/js/ff/split/journal/from-store.js +++ b/public/js/ff/split/journal/from-store.js @@ -18,19 +18,16 @@ $(function () { $.getJSON('json/expense-accounts').done(function (data) { destAccounts = data; - console.log('destAccounts length is now ' + destAccounts.length); $('input[name$="destination_account_name]"]').typeahead({source: destAccounts}); }); $.getJSON('json/revenue-accounts').done(function (data) { srcAccounts = data; - console.log('srcAccounts length is now ' + srcAccounts.length); $('input[name$="source_account_name]"]').typeahead({source: srcAccounts}); }); $.getJSON('json/categories').done(function (data) { categories = data; - console.log('categories length is now ' + categories.length); $('input[name$="category]"]').typeahead({source: categories}); }); @@ -45,12 +42,10 @@ function removeRow(e) { "use strict"; var rows = $('table.split-table tbody tr'); if (rows.length === 1) { - console.log('Will not remove last split'); return false; } var row = $(e.target); var index = row.data('split'); - console.log('Trying to remove row with split ' + index); $('table.split-table tbody tr[data-split="' + index + '"]').remove(); @@ -70,16 +65,13 @@ function cloneRow() { source.find('input[name$="][amount]"]').val("").on('input', calculateSum); if (destAccounts.length > 0) { - console.log('Will be able to extend dest-accounts.'); source.find('input[name$="destination_account_name]"]').typeahead({source: destAccounts}); } if (destAccounts.length > 0) { - console.log('Will be able to extend src-accounts.'); source.find('input[name$="source_account_name]"]').typeahead({source: srcAccounts}); } if (categories.length > 0) { - console.log('Will be able to extend categories.'); source.find('input[name$="category]"]').typeahead({source: categories}); } @@ -103,7 +95,6 @@ function resetSplits() { $.each($('table.split-table tbody tr'), function (i, v) { var row = $(v); row.attr('data-split', i); - console.log('Row is now ' + row.data('split')); }); // loop each remove button, update the index @@ -111,7 +102,6 @@ function resetSplits() { var button = $(v); button.attr('data-split', i); button.find('i').attr('data-split', i); - console.log('Remove button index is now ' + button.data('split')); }); @@ -120,7 +110,6 @@ function resetSplits() { var cell = $(v); var index = i + 1; cell.text('#' + index); - console.log('Cell is now ' + cell.text()); }); // loop each possible field. @@ -129,37 +118,31 @@ function resetSplits() { $.each($('input[name$="][description]"]'), function (i, v) { var input = $(v); input.attr('name', 'transactions[' + i + '][description]'); - console.log('description is now ' + input.attr('name')); }); // ends with ][destination_account_name] $.each($('input[name$="][destination_account_name]"]'), function (i, v) { var input = $(v); input.attr('name', 'transactions[' + i + '][destination_account_name]'); - console.log('destination_account_name is now ' + input.attr('name')); }); // ends with ][source_account_name] $.each($('input[name$="][source_account_name]"]'), function (i, v) { var input = $(v); input.attr('name', 'transactions[' + i + '][source_account_name]'); - console.log('source_account_name is now ' + input.attr('name')); }); // ends with ][amount] $.each($('input[name$="][amount]"]'), function (i, v) { var input = $(v); input.attr('name', 'transactions[' + i + '][amount]'); - console.log('amount is now ' + input.attr('name')); }); // ends with ][budget_id] $.each($('select[name$="][budget_id]"]'), function (i, v) { var input = $(v); input.attr('name', 'transactions[' + i + '][budget_id]'); - console.log('budget_id is now ' + input.attr('name')); }); // ends with ][category] $.each($('input[name$="][category]"]'), function (i, v) { var input = $(v); input.attr('name', 'transactions[' + i + '][category]'); - console.log('category is now ' + input.attr('name')); }); } @@ -173,10 +156,8 @@ function calculateSum() { } sum = Math.round(sum * 100) / 100; - console.log("Sum is now " + sum); $('.amount-warning').remove(); if (sum != originalSum) { - console.log(sum + ' does not match ' + originalSum); var holder = $('#journal_amount_holder'); var par = holder.find('p.form-control-static'); var amount = $('').text(' (' + accounting.formatMoney(sum) + ')').addClass('text-danger amount-warning').appendTo(par); diff --git a/public/js/ff/transactions/create.js b/public/js/ff/transactions/create.js index e059fdf127..c2e38f6b6d 100644 --- a/public/js/ff/transactions/create.js +++ b/public/js/ff/transactions/create.js @@ -111,7 +111,6 @@ function updateButtons() { if (button.data('what') == what) { button.removeClass('btn-default').addClass('btn-info').html(' ' + txt[button.data('what')]); - console.log('Now displaying form for ' + what); } else { button.removeClass('btn-info').addClass('btn-default').text(txt[button.data('what')]); } diff --git a/resources/views/layout/default.twig b/resources/views/layout/default.twig index 0295b5059c..aff6f9491d 100644 --- a/resources/views/layout/default.twig +++ b/resources/views/layout/default.twig @@ -3,6 +3,7 @@ + {% if subTitle %} @@ -197,8 +198,7 @@ toLabel: '{{ 'to'|_|escape }}', ranges: {{ dpRanges|json_encode|raw }} }; - - var token = "{{ csrf_token() }}"; + var language = "{{ language|escape }}"; var currencyCode = '{{ getCurrencyCode()|escape('js') }}'; var currencySymbol = '{{ getCurrencySymbol()|escape('js') }}'; From b2030a72a09ebfc00590741806cce0b564b089fc Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 2 Jan 2017 08:46:45 +0100 Subject: [PATCH 471/709] Fix some javascript things [skip ci] --- public/js/ff/accounts/show.js | 1 + public/js/ff/budgets/index.js | 2 +- public/js/ff/categories/show.js | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/public/js/ff/accounts/show.js b/public/js/ff/accounts/show.js index fc41371659..7cff7dc249 100644 --- a/public/js/ff/accounts/show.js +++ b/public/js/ff/accounts/show.js @@ -70,6 +70,7 @@ function sortStop(event, ui) { // animate something with color: current.animate({backgroundColor: "#d9534f"}, 200, function () { $(this).animate({backgroundColor: originalBG}, 200); + return undefined; }); return false; diff --git a/public/js/ff/budgets/index.js b/public/js/ff/budgets/index.js index 1f8ee67597..936ae07b7f 100644 --- a/public/js/ff/budgets/index.js +++ b/public/js/ff/budgets/index.js @@ -72,7 +72,7 @@ function updateBudgetedAmounts(e) { drawBudgetedBar(); // send a post to Firefly to update the amount: - $.post('budgets/amount/' + id, {amount: value, _token: token}).done(function (data) { + $.post('budgets/amount/' + id, {amount: value}).done(function (data) { // update the link if relevant: if (data.repetition > 0) { $('.budget-link[data-id="' + id + '"]').attr('href', 'budgets/show/' + id + '/' + data.repetition); diff --git a/public/js/ff/categories/show.js b/public/js/ff/categories/show.js index a53a5b3d94..e1d5905d24 100644 --- a/public/js/ff/categories/show.js +++ b/public/js/ff/categories/show.js @@ -8,6 +8,8 @@ * See the LICENSE file for details. */ +/** global: all, current, specific */ + $(function () { "use strict"; columnChart(all, 'all'); From f7d61e5a9b832e7fa8bd674d4b01c0cc4d8ecdbe Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 2 Jan 2017 09:02:58 +0100 Subject: [PATCH 472/709] Update scrutinizer config. --- .scrutinizer.yml | 49 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/.scrutinizer.yml b/.scrutinizer.yml index b2245b4a46..20a1c3551b 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -2,7 +2,48 @@ tools: external_code_coverage: false filter: - excluded_paths: - - app/Support/Migration/* - - app/database/migrations/* - - database/migrations/* + paths: + - app/* + excluded_paths: + - "database/migrations/*" + - "bootstrap/*" + - "config/*" + - "docker/*" + - "public/js/lib/*" + - "public/lib/adminlte/js/*" + - "public/lib/bootstrap/js/*" + - "resources/*" + - "routes/*" + - "storage/*" +checks: + php: + use_self_instead_of_fqcn: true + uppercase_constants: true + return_doc_comments: true + return_doc_comment_if_not_inferrable: true + remove_extra_empty_lines: true + parameter_doc_comments: true + optional_parameters_at_the_end: true + no_short_variable_names: + minimum: '3' + no_short_method_names: + minimum: '3' + no_long_variable_names: + maximum: '20' + no_goto: true + newline_at_end_of_file: true + encourage_single_quotes: true + avoid_todo_comments: true + avoid_perl_style_comments: true + avoid_fixme_comments: true + avoid_multiple_statements_on_same_line: true + align_assignments: true + javascript: true + +coding_style: + php: + spaces: + around_operators: + concatenation: true + other: + after_type_cast: false \ No newline at end of file From 87316cf7c14a2651a224d20a6485a3517c1e4c49 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 2 Jan 2017 09:09:37 +0100 Subject: [PATCH 473/709] Include local JS directory. [skip ci] --- .scrutinizer.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 20a1c3551b..02bb0d0f4a 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -4,6 +4,7 @@ tools: filter: paths: - app/* + - public/js/ff/* excluded_paths: - "database/migrations/*" - "bootstrap/*" From 7c5bed2bb593707fcefae8446dae5163634d9bae Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 2 Jan 2017 09:23:24 +0100 Subject: [PATCH 474/709] Fix export controller tests. --- .../ExportJob/ExportJobRepository.php | 2 +- .../Controllers/ExportControllerTest.php | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/app/Repositories/ExportJob/ExportJobRepository.php b/app/Repositories/ExportJob/ExportJobRepository.php index 66458230ec..b83818284a 100644 --- a/app/Repositories/ExportJob/ExportJobRepository.php +++ b/app/Repositories/ExportJob/ExportJobRepository.php @@ -124,7 +124,7 @@ class ExportJobRepository implements ExportJobRepositoryInterface /** * @param string $key * - * @return ExportJob|null + * @return ExportJob */ public function findByKey(string $key): ExportJob { diff --git a/tests/acceptance/Controllers/ExportControllerTest.php b/tests/acceptance/Controllers/ExportControllerTest.php index f0eb8d5716..39a7bd4ebc 100644 --- a/tests/acceptance/Controllers/ExportControllerTest.php +++ b/tests/acceptance/Controllers/ExportControllerTest.php @@ -8,9 +8,13 @@ * * See the LICENSE file for details. */ +use Carbon\Carbon; use FireflyIII\Export\Processor; +use FireflyIII\Export\ProcessorInterface; use FireflyIII\Models\ExportJob; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface; +use Illuminate\Support\Collection; /** * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:41. @@ -70,6 +74,11 @@ class ExportControllerTest extends TestCase */ public function testPostIndex() { + $this->session( + ['first' => new Carbon('2014-01-01')] + ); + + $data = [ 'export_start_range' => '2015-01-01', @@ -78,7 +87,11 @@ class ExportControllerTest extends TestCase 'accounts' => [1], 'job' => 'testExport', ]; - $processor = $this->mock(Processor::class); + + $accountRepository = $this->mock(AccountRepositoryInterface::class); + $accountRepository->shouldReceive('getAccountsById')->withArgs([$data['accounts']])->andReturn(new Collection); + + $processor = $this->mock(ProcessorInterface::class); $processor->shouldReceive('collectJournals')->once(); $processor->shouldReceive('convertJournals')->once(); $processor->shouldReceive('exportJournals')->once(); @@ -88,8 +101,8 @@ class ExportControllerTest extends TestCase $repository->shouldReceive('changeStatus')->andReturn(true); $repository->shouldReceive('findByKey')->andReturn(new ExportJob); - $this->be($this->user()); + $this->call('post', route('export.export'), $data); $this->assertResponseStatus(200); $this->see('ok'); From 050d7e8f00359be2cbc19eb16cfb0e7f0d8215eb Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 2 Jan 2017 09:53:22 +0100 Subject: [PATCH 475/709] Remove specific commands from composer installation routine. --- composer.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/composer.json b/composer.json index af2091da01..7c6b72dab5 100755 --- a/composer.json +++ b/composer.json @@ -72,9 +72,6 @@ ], "post-install-cmd": [ "Illuminate\\Foundation\\ComposerScripts::postInstall", - "php artisan firefly:upgrade-instructions", - "php artisan firefly:upgrade-database", - "php artisan firefly:verify", "php artisan firefly:github-move", "php artisan optimize" ], From 36f1b6a834280e8f079f9102bf1c290ffda689f7 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 2 Jan 2017 10:05:02 +0100 Subject: [PATCH 476/709] New account tests. --- app/Models/Account.php | 20 +++++--- tests/unit/Models/AccountTest.php | 83 +++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 6 deletions(-) create mode 100644 tests/unit/Models/AccountTest.php diff --git a/app/Models/Account.php b/app/Models/Account.php index a4a2813cb9..4810c6006e 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -69,10 +69,14 @@ class Account extends Model /** * @param array $fields * - * @return Account|null + * @return Account + * @throws FireflyException */ public static function firstOrCreateEncrypted(array $fields) { + if (!isset($fields['user_id'])) { + throw new FireflyException('Missing required field "user_id".'); + } // everything but the name: $query = self::orderBy('id'); $search = $fields; @@ -81,17 +85,21 @@ class Account extends Model foreach ($search as $name => $value) { $query->where($name, $value); } - $set = $query->get(['accounts.*']); + $set = $query->get(['accounts.*']); + + // account must have a name. If not set, use IBAN. + if (!isset($fields['name'])) { + $fields['name'] = $fields['iban']; + } + + + /** @var Account $account */ foreach ($set as $account) { if ($account->name == $fields['name']) { return $account; } } - // account must have a name. If not set, use IBAN. - if (!isset($fields['name'])) { - $fields['name'] = $fields['iban']; - } // create it! $account = self::create($fields); diff --git a/tests/unit/Models/AccountTest.php b/tests/unit/Models/AccountTest.php new file mode 100644 index 0000000000..6be272929b --- /dev/null +++ b/tests/unit/Models/AccountTest.php @@ -0,0 +1,83 @@ +<?php +/** + * AccountTest.php + * Copyright (c) 2016 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +declare(strict_types = 1); + +use FireflyIII\Models\Account; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; + +class AccountTest extends TestCase +{ + + /** + * @covers \FireflyIII\Models\Account::firstOrCreateEncrypted + */ + public function testEncrypted() + { + $data = [ + 'user_id' => 1, + 'name' => 'Test account #' . rand(1000, 9999), + ]; + $account = Account::firstOrCreateEncrypted($data); + + $this->assertEquals($account->user_id, $data['user_id']); + $this->assertEquals($account->name, $data['name']); + } + + /** + * @covers \FireflyIII\Models\Account::firstOrCreateEncrypted + */ + public function testEncryptedIban() + { + $data = [ + 'user_id' => 1, + 'iban' => 'NL64RABO0133183395', + ]; + $account = Account::firstOrCreateEncrypted($data); + + $this->assertEquals($account->user_id, $data['user_id']); + $this->assertEquals($account->name, $data['iban']); + } + + /** + * @covers \FireflyIII\Models\Account::firstOrCreateEncrypted + * @expectedException \FireflyIII\Exceptions\FireflyException + */ + public function testEncryptedNoId() + { + $data = [ + 'name' => 'Test account', + ]; + $account = Account::firstOrCreateEncrypted($data); + } + + /** + * @covers \FireflyIII\Models\Account::routeBinder + */ + public function testRouteBinder() + { + // not logged in? + $this->be($this->user()); + $this->call('get', route('accounts.show', [1])); + + } + + /** + * One that belongs to another user. + * + * @covers \FireflyIII\Models\Account::routeBinder + */ + public function testRouteBinderError() + { + $account = Account::whereUserId(3)->first(); + $this->be($this->user()); + $this->call('get', route('accounts.show', [$account->id])); + $this->assertResponseStatus(404); + } +} \ No newline at end of file From f8268a864b59250dfd18f8e663e62fb97d7e73fb Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 2 Jan 2017 10:34:01 +0100 Subject: [PATCH 477/709] This should fix most Javascript errors. --- public/js/ff/charts.defaults.js | 4 +- public/js/ff/charts.js | 6 +-- public/js/ff/export/index.js | 4 +- public/js/ff/firefly.js | 2 + public/js/ff/import/status.js | 4 +- public/js/ff/index.js | 7 ++- public/js/ff/piggy-banks/show.js | 1 + public/js/ff/reports/audit/all.js | 12 +++-- public/js/ff/reports/budget/month.js | 1 + public/js/ff/reports/category/month.js | 1 + public/js/ff/reports/default/all.js | 4 +- public/js/ff/reports/default/month.js | 2 + public/js/ff/reports/default/multi-year.js | 2 + public/js/ff/reports/default/year.js | 2 + public/js/ff/reports/index.js | 12 +++-- public/js/ff/rules/create-edit.js | 51 +++++++++++----------- public/js/ff/rules/create.js | 3 ++ public/js/ff/rules/edit.js | 2 + public/js/ff/rules/index.js | 1 - public/js/ff/split/journal/from-store.js | 2 + public/js/ff/tags/create.js | 6 ++- public/js/ff/tags/edit.js | 5 ++- public/js/ff/tags/index.js | 6 ++- public/js/ff/transactions/create-edit.js | 6 +-- public/js/ff/transactions/create.js | 5 ++- public/js/ff/transactions/list.js | 2 + 26 files changed, 102 insertions(+), 51 deletions(-) diff --git a/public/js/ff/charts.defaults.js b/public/js/ff/charts.defaults.js index bbcce69ccb..c670667dd1 100644 --- a/public/js/ff/charts.defaults.js +++ b/public/js/ff/charts.defaults.js @@ -8,6 +8,8 @@ * See the LICENSE file for details. */ +/** global: accounting */ + var defaultChartOptions = { scales: { xAxes: [ @@ -20,7 +22,7 @@ var defaultChartOptions = { yAxes: [{ display: true, ticks: { - callback: function (tickValue, index, ticks) { + callback: function (tickValue) { "use strict"; return accounting.formatMoney(tickValue); diff --git a/public/js/ff/charts.js b/public/js/ff/charts.js index 82977288d2..8fc2915fc5 100644 --- a/public/js/ff/charts.js +++ b/public/js/ff/charts.js @@ -7,7 +7,7 @@ * * See the LICENSE file for details. */ - +/** global: Chart, defaultChartOptions, accounting, defaultPieOptions, noDataForChart */ var allCharts = {}; /* @@ -97,7 +97,7 @@ function doubleYChart(URI, container) { { display: true, ticks: { - callback: function (tickValue, index, ticks) { + callback: function (tickValue) { "use strict"; return accounting.formatMoney(tickValue); @@ -111,7 +111,7 @@ function doubleYChart(URI, container) { { display: true, ticks: { - callback: function (tickValue, index, ticks) { + callback: function (tickValue) { "use strict"; return accounting.formatMoney(tickValue); diff --git a/public/js/ff/export/index.js b/public/js/ff/export/index.js index e1699c3c8d..006afca9ba 100644 --- a/public/js/ff/export/index.js +++ b/public/js/ff/export/index.js @@ -8,6 +8,8 @@ * See the LICENSE file for details. */ +/** global: jobKey */ + var intervalId = 0; $(function () { @@ -79,7 +81,7 @@ function callExport() { // call status, keep calling it until response is "finished"? intervalId = window.setInterval(checkStatus, 500); - $.post('export/submit', data).done(function (data) { + $.post('export/submit', data).done(function () { // stop polling: window.clearTimeout(intervalId); diff --git a/public/js/ff/firefly.js b/public/js/ff/firefly.js index c35bd4fbe8..c3b1b46bd7 100644 --- a/public/js/ff/firefly.js +++ b/public/js/ff/firefly.js @@ -7,6 +7,8 @@ * * See the LICENSE file for details. */ +/** global: moment, dateRangeConfig, accounting, currencySymbol, mon_decimal_point, frac_digits, showFullList, showOnlyTop */ + $(function () { "use strict"; diff --git a/public/js/ff/import/status.js b/public/js/ff/import/status.js index 2b15cac1b6..1a48091eaf 100644 --- a/public/js/ff/import/status.js +++ b/public/js/ff/import/status.js @@ -8,6 +8,8 @@ * See the LICENSE file for details. */ +/** global: jobImportUrl, langImportSingleError, langImportMultiError, jobStartUrl, langImportTimeOutError, langImportFinished, langImportFatalError */ + var startedImport = false; var startInterval = 2000; var interval = 500; @@ -31,7 +33,7 @@ function checkImportStatus() { $.getJSON(jobImportUrl).done(reportOnJobImport).fail(failedJobImport); } -function importComplete(data) { +function importComplete() { "use strict"; var bar = $('#import-status-bar'); bar.removeClass('active'); diff --git a/public/js/ff/index.js b/public/js/ff/index.js index 04a17e9133..ba6c9993e1 100644 --- a/public/js/ff/index.js +++ b/public/js/ff/index.js @@ -8,11 +8,13 @@ * See the LICENSE file for details. */ +/** global: Tour, showTour, accountFrontpageUri, billCount, accountExpenseUri, accountRevenueUri */ + $(function () { "use strict"; // do chart JS stuff. drawChart(); - if (showTour) { + if (showTour == true) { $.getJSON('json/tour').done(function (data) { var tour = new Tour( { @@ -55,6 +57,9 @@ function getBoxAmounts() { "use strict"; var boxes = ['in', 'out', 'bills-unpaid', 'bills-paid']; for (var x in boxes) { + if (!boxes.hasOwnProperty(x)) { + continue; + } var box = boxes[x]; $.getJSON('json/box/' + box).done(putData).fail(failData); } diff --git a/public/js/ff/piggy-banks/show.js b/public/js/ff/piggy-banks/show.js index 4c02df137c..be5093f6ba 100644 --- a/public/js/ff/piggy-banks/show.js +++ b/public/js/ff/piggy-banks/show.js @@ -7,6 +7,7 @@ * * See the LICENSE file for details. */ +/** global: piggyBankID, lineChart */ $(function () { "use strict"; diff --git a/public/js/ff/reports/audit/all.js b/public/js/ff/reports/audit/all.js index dc28051a29..1724789bf4 100644 --- a/public/js/ff/reports/audit/all.js +++ b/public/js/ff/reports/audit/all.js @@ -8,6 +8,8 @@ * See the LICENSE file for details. */ +/** global: hideable */ + $(function () { "use strict"; @@ -65,7 +67,7 @@ function showOnlyColumns(checkboxes) { for (var i = 0; i < hideable.length; i++) { var opt = hideable[i]; - if(checkboxes.indexOf(opt) > -1) { + if (checkboxes.indexOf(opt) > -1) { $('td.hide-' + opt).show(); $('th.hide-' + opt).show(); } else { @@ -96,8 +98,12 @@ function readCookie(name) { var ca = document.cookie.split(';'); for (var i = 0; i < ca.length; i++) { var c = ca[i]; - while (c.charAt(0) === ' ') c = c.substring(1, c.length); - if (c.indexOf(nameEQ) === 0) return decodeURIComponent(c.substring(nameEQ.length, c.length)); + while (c.charAt(0) === ' ') { + c = c.substring(1, c.length); + } + if (c.indexOf(nameEQ) === 0) { + return decodeURIComponent(c.substring(nameEQ.length, c.length)); + } } return null; } diff --git a/public/js/ff/reports/budget/month.js b/public/js/ff/reports/budget/month.js index c7aad72832..14d845fb6e 100644 --- a/public/js/ff/reports/budget/month.js +++ b/public/js/ff/reports/budget/month.js @@ -8,6 +8,7 @@ * See the LICENSE file for details. */ +/** global: budgetExpenseUri, accountExpenseUri, mainUri */ $(function () { "use strict"; diff --git a/public/js/ff/reports/category/month.js b/public/js/ff/reports/category/month.js index d8ba767acd..84f3444d9b 100644 --- a/public/js/ff/reports/category/month.js +++ b/public/js/ff/reports/category/month.js @@ -8,6 +8,7 @@ * See the LICENSE file for details. */ +/** global: categoryIncomeUri, categoryExpenseUri, accountIncomeUri, accountExpenseUri, mainUri */ $(function () { "use strict"; diff --git a/public/js/ff/reports/default/all.js b/public/js/ff/reports/default/all.js index 351b4b30bf..06e374dcee 100644 --- a/public/js/ff/reports/default/all.js +++ b/public/js/ff/reports/default/all.js @@ -8,6 +8,8 @@ * See the LICENSE file for details. */ +/** global: accountReportUri, incomeReportUri, expenseReportUri, incExpReportUri, startDate, endDate, accountIds */ + $(function () { "use strict"; @@ -45,7 +47,7 @@ function clickInfoButton(e) { $.getJSON('popup/general', {attributes: attributes}).done(respondInfoButton).fail(errorInfoButton); } -function errorInfoButton(data) { +function errorInfoButton() { "use strict"; // remove wait cursor $('body').removeClass('waiting'); diff --git a/public/js/ff/reports/default/month.js b/public/js/ff/reports/default/month.js index 2052997a9b..1c2f08be63 100644 --- a/public/js/ff/reports/default/month.js +++ b/public/js/ff/reports/default/month.js @@ -8,6 +8,8 @@ * See the LICENSE file for details. */ +/** global: categoryReportUri, budgetReportUri, balanceReportUri, accountChartUri */ + $(function () { "use strict"; drawChart(); diff --git a/public/js/ff/reports/default/multi-year.js b/public/js/ff/reports/default/multi-year.js index a6a63a1d3d..1bd49a294d 100644 --- a/public/js/ff/reports/default/multi-year.js +++ b/public/js/ff/reports/default/multi-year.js @@ -8,6 +8,8 @@ * See the LICENSE file for details. */ +/** global: budgetPeriodReportUri, categoryExpenseUri, categoryIncomeUri, netWorthUri, opChartUri, sumChartUri */ + $(function () { "use strict"; drawChart(); diff --git a/public/js/ff/reports/default/year.js b/public/js/ff/reports/default/year.js index 9e521ab4fb..c1eb1070f3 100644 --- a/public/js/ff/reports/default/year.js +++ b/public/js/ff/reports/default/year.js @@ -8,6 +8,8 @@ * See the LICENSE file for details. */ +/** global: budgetPeriodReportUri, categoryExpenseUri, categoryIncomeUri, netWorthUri, opChartUri, sumChartUri */ + $(function () { "use strict"; drawChart(); diff --git a/public/js/ff/reports/index.js b/public/js/ff/reports/index.js index 5033aecef0..67be970157 100644 --- a/public/js/ff/reports/index.js +++ b/public/js/ff/reports/index.js @@ -8,12 +8,14 @@ * See the LICENSE file for details. */ +/** global: minDate */ + $(function () { "use strict"; if ($('#inputDateRange').length > 0) { - picker = $('#inputDateRange').daterangepicker( + var picker = $('#inputDateRange').daterangepicker( { locale: { format: 'YYYY-MM-DD', @@ -168,8 +170,12 @@ function readCookie(name) { var ca = document.cookie.split(';'); for (var i = 0; i < ca.length; i++) { var c = ca[i]; - while (c.charAt(0) === ' ') c = c.substring(1, c.length); - if (c.indexOf(nameEQ) === 0) return decodeURIComponent(c.substring(nameEQ.length, c.length)); + while (c.charAt(0) === ' ') { + c = c.substring(1, c.length); + } + if (c.indexOf(nameEQ) === 0) { + return decodeURIComponent(c.substring(nameEQ.length, c.length)); + } } return null; } diff --git a/public/js/ff/rules/create-edit.js b/public/js/ff/rules/create-edit.js index 889777d7bf..cd36a7b1ad 100644 --- a/public/js/ff/rules/create-edit.js +++ b/public/js/ff/rules/create-edit.js @@ -52,14 +52,14 @@ function addNewAction() { function removeTrigger(e) { "use strict"; var target = $(e.target); - if(target.prop("tagName") == "I") { + if (target.prop("tagName") == "I") { target = target.parent(); } // remove grand parent: target.parent().parent().remove(); // if now at zero, immediatly add one again: - if($('.rule-trigger-tbody tr').length == 0) { + if ($('.rule-trigger-tbody tr').length == 0) { addNewTrigger(); } } @@ -67,42 +67,41 @@ function removeTrigger(e) { function removeAction(e) { "use strict"; var target = $(e.target); - if(target.prop("tagName") == "I") { + if (target.prop("tagName") == "I") { target = target.parent(); } // remove grand parent: target.parent().parent().remove(); // if now at zero, immediatly add one again: - if($('.rule-action-tbody tr').length == 0) { + if ($('.rule-action-tbody tr').length == 0) { addNewAction(); } } function testRuleTriggers() { - "use strict"; - - // Serialize all trigger data - var triggerData = $( ".rule-trigger-tbody" ).find( "input[type=text], input[type=checkbox], select" ).serializeArray(); - - // Find a list of existing transactions that match these triggers + "use strict"; + + // Serialize all trigger data + var triggerData = $(".rule-trigger-tbody").find("input[type=text], input[type=checkbox], select").serializeArray(); + + // Find a list of existing transactions that match these triggers $.get('rules/test', triggerData).done(function (data) { - var modal = $( "#testTriggerModal" ); - var numTriggers = $( ".rule-trigger-body > tr" ).length; - - // Set title and body - modal.find( ".transactions-list" ).html(data.html); - - // Show warning if appropriate - if( data.warning ) { - modal.find( ".transaction-warning .warning-contents" ).text(data.warning); - modal.find( ".transaction-warning" ).show(); - } else { - modal.find( ".transaction-warning" ).hide(); - } - - // Show the modal dialog - $( "#testTriggerModal" ).modal(); + var modal = $("#testTriggerModal"); + + // Set title and body + modal.find(".transactions-list").html(data.html); + + // Show warning if appropriate + if (data.warning) { + modal.find(".transaction-warning .warning-contents").text(data.warning); + modal.find(".transaction-warning").show(); + } else { + modal.find(".transaction-warning").hide(); + } + + // Show the modal dialog + $("#testTriggerModal").modal(); }).fail(function () { alert('Cannot get transactions for given triggers.'); }); diff --git a/public/js/ff/rules/create.js b/public/js/ff/rules/create.js index 739762d228..ffa65be20e 100644 --- a/public/js/ff/rules/create.js +++ b/public/js/ff/rules/create.js @@ -8,6 +8,9 @@ * See the LICENSE file for details. */ +/** global: triggerCount, actionCount */ + + $(function () { "use strict"; if (triggerCount === 0) { diff --git a/public/js/ff/rules/edit.js b/public/js/ff/rules/edit.js index 7ff822a186..893feff138 100644 --- a/public/js/ff/rules/edit.js +++ b/public/js/ff/rules/edit.js @@ -8,6 +8,8 @@ * See the LICENSE file for details. */ +/** global: triggerCount, actionCount */ + $(function () { "use strict"; if (triggerCount === 0) { diff --git a/public/js/ff/rules/index.js b/public/js/ff/rules/index.js index b9b4045b5c..0fc0ef7371 100644 --- a/public/js/ff/rules/index.js +++ b/public/js/ff/rules/index.js @@ -52,7 +52,6 @@ function sortStop(event, ui) { $.each(parent.children(), function (i, v) { var trigger = $(v); var id = trigger.data('id'); - var order = i + 1; entries.push(id); }); diff --git a/public/js/ff/split/journal/from-store.js b/public/js/ff/split/journal/from-store.js index 03ceb49ba5..a614d77d4e 100644 --- a/public/js/ff/split/journal/from-store.js +++ b/public/js/ff/split/journal/from-store.js @@ -8,6 +8,8 @@ * See the LICENSE file for details. */ +/** global: originalSum, amount, accounting */ + var destAccounts = {}; var srcAccounts = {}; var categories = {}; diff --git a/public/js/ff/tags/create.js b/public/js/ff/tags/create.js index 77e8224238..b252d00ae9 100644 --- a/public/js/ff/tags/create.js +++ b/public/js/ff/tags/create.js @@ -8,6 +8,8 @@ * See the LICENSE file for details. */ +/** global: zoomLevel, latitude, longitude, google */ + $(function () { "use strict"; @@ -60,12 +62,12 @@ function initialize() { Respond to zoom event. */ google.maps.event.addListener(map, 'zoom_changed', function () { - saveZoomLevel(event); + saveZoomLevel(); }); /* Maybe place marker? */ - if(doPlaceMarker) { + if(doPlaceMarker == true) { var myLatlng = new google.maps.LatLng(latitude,longitude); var fakeEvent = {}; fakeEvent.latLng = myLatlng; diff --git a/public/js/ff/tags/edit.js b/public/js/ff/tags/edit.js index c4f9cd0cfb..53b66933c4 100644 --- a/public/js/ff/tags/edit.js +++ b/public/js/ff/tags/edit.js @@ -7,6 +7,7 @@ * * See the LICENSE file for details. */ +/** global: zoomLevel, latitude, longitude, google */ $(function () { "use strict"; @@ -60,12 +61,12 @@ function initialize() { Respond to zoom event. */ google.maps.event.addListener(map, 'zoom_changed', function () { - saveZoomLevel(event); + saveZoomLevel(); }); /* Maybe place marker? */ - if(doPlaceMarker) { + if(doPlaceMarker == true) { var myLatlng = new google.maps.LatLng(latitude,longitude); var fakeEvent = {}; fakeEvent.latLng = myLatlng; diff --git a/public/js/ff/tags/index.js b/public/js/ff/tags/index.js index a24180b4e0..1234a027ba 100644 --- a/public/js/ff/tags/index.js +++ b/public/js/ff/tags/index.js @@ -8,6 +8,8 @@ * See the LICENSE file for details. */ +/** global: zoomLevel, latitude, longitude, google */ + $(function () { "use strict"; @@ -60,12 +62,12 @@ function initialize() { Respond to zoom event. */ google.maps.event.addListener(map, 'zoom_changed', function () { - saveZoomLevel(event); + saveZoomLevel(); }); /* Maybe place marker? */ - if(doPlaceMarker) { + if(doPlaceMarker == true) { var myLatlng = new google.maps.LatLng(latitude,longitude); var fakeEvent = {}; fakeEvent.latLng = myLatlng; diff --git a/public/js/ff/transactions/create-edit.js b/public/js/ff/transactions/create-edit.js index ae827d8983..44796c50bb 100644 --- a/public/js/ff/transactions/create-edit.js +++ b/public/js/ff/transactions/create-edit.js @@ -58,19 +58,19 @@ $(document).ready(function () { } - if ($('input[name="description"]').length > 0 && what !== undefined) { + if ($('input[name="description"]').length > 0 && !(typeof what === "undefined")) { $.getJSON('json/transaction-journals/' + what).done(function (data) { $('input[name="description"]').typeahead({source: data}); }); } // also for multi input: - if ($('input[name="description[]"]').length > 0 && what !== undefined) { + if ($('input[name="description[]"]').length > 0 && !(typeof what === "undefined")) { $.getJSON('json/transaction-journals/' + what).done(function (data) { $('input[name="description[]"]').typeahead({source: data}); }); } // and for the (rare) journal_description: - if ($('input[name="journal_description"]').length > 0 && what !== undefined) { + if ($('input[name="journal_description"]').length > 0 && !(typeof what === "undefined")) { $.getJSON('json/transaction-journals/' + what).done(function (data) { $('input[name="journal_description"]').typeahead({source: data}); }); diff --git a/public/js/ff/transactions/create.js b/public/js/ff/transactions/create.js index c2e38f6b6d..0440006594 100644 --- a/public/js/ff/transactions/create.js +++ b/public/js/ff/transactions/create.js @@ -8,12 +8,14 @@ * See the LICENSE file for details. */ +/** global: what, title, breadcrumbs, middleCrumbName, button, piggiesLength, txt */ + $(document).ready(function () { "use strict"; // respond to switch buttons when // creating stuff: - if (doSwitch) { + if (doSwitch == true) { updateButtons(); updateForm(); updateLayout(); @@ -35,6 +37,7 @@ function updateForm() { $('input[name="what"]').val(what); switch (what) { + default: case 'withdrawal': // show source_id and dest_name: $('#source_account_id_holder').show(); diff --git a/public/js/ff/transactions/list.js b/public/js/ff/transactions/list.js index 7e87446a48..74dc53f24d 100644 --- a/public/js/ff/transactions/list.js +++ b/public/js/ff/transactions/list.js @@ -8,6 +8,8 @@ * See the LICENSE file for details. */ +/** global: edit_selected_txt, delete_selected_txt */ + $(document).ready(function () { "use strict"; $('.mass_edit_all').show(); From 479a4dac7b2f6a61a9268b98d1f63ff9337dc919 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 2 Jan 2017 11:41:14 +0100 Subject: [PATCH 478/709] Missing translations [skip ci] #517 --- resources/views/rules/index.twig | 2 +- resources/views/tags/index.twig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/views/rules/index.twig b/resources/views/rules/index.twig index 6500bc827d..08b84c0bff 100644 --- a/resources/views/rules/index.twig +++ b/resources/views/rules/index.twig @@ -28,7 +28,7 @@ {% if ruleGroup.active %} {{ ruleGroup.title }} {% else %} - <s>{{ ruleGroup.title }}</s> (inactive) + <s>{{ ruleGroup.title }}</s> ({{ 'inactive'|_|lower }}) {% endif %} </h3> diff --git a/resources/views/tags/index.twig b/resources/views/tags/index.twig index 78512e056d..1baaace293 100644 --- a/resources/views/tags/index.twig +++ b/resources/views/tags/index.twig @@ -9,7 +9,7 @@ <div class="col-lg-12"> <div class="box"> <div class="box-header with-border"> - <h3 class="box-title">Tags</h3> + <h3 class="box-title">{{ 'tags'|_ }}</h3> </div> <div class="box-body"> <p> From 9352ee3e2575828d5d3132f0c7253d701c3d7757 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 2 Jan 2017 12:09:46 +0100 Subject: [PATCH 479/709] Various Javascript fixes. --- app/Http/Controllers/HomeController.php | 1 - app/Http/Controllers/TagController.php | 6 +- public/js/ff/accounts/show.js | 1 + public/js/ff/charts.js | 4 +- public/js/ff/firefly.js | 2 +- public/js/ff/import/status.js | 2 +- public/js/ff/reports/index.js | 2 +- public/js/ff/split/journal/from-store.js | 5 +- public/js/ff/tags/{edit.js => create-edit.js} | 2 +- public/js/ff/tags/create.js | 115 ------------------ public/js/ff/tags/index.js | 100 --------------- public/js/ff/transactions/create-edit.js | 2 + public/js/ff/transactions/create.js | 4 +- resources/views/tags/create.twig | 6 +- resources/views/tags/edit.twig | 17 +-- 15 files changed, 31 insertions(+), 238 deletions(-) rename public/js/ff/tags/{edit.js => create-edit.js} (97%) delete mode 100644 public/js/ff/tags/create.js diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index bfa7f86356..9263a25937 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -63,7 +63,6 @@ class HomeController extends Controller // a possible problem with the budgets. if ($label === strval(trans('firefly.everything')) || $label === strval(trans('firefly.customRange'))) { $isCustomRange = true; - //Preferences::set('viewRange', 'custom'); Log::debug('Range is now marked as "custom".'); } diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index f404a08e26..c000fd6fd5 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -76,6 +76,7 @@ class TagController extends Controller { $subTitle = trans('firefly.new_tag'); $subTitleIcon = 'fa-tag'; + $apiKey = env('GOOGLE_MAPS_API_KEY', ''); $preFilled = [ 'tagMode' => 'nothing', @@ -91,7 +92,7 @@ class TagController extends Controller Session::flash('gaEventCategory', 'tags'); Session::flash('gaEventAction', 'create'); - return view('tags.create', compact('subTitle', 'subTitleIcon')); + return view('tags.create', compact('subTitle', 'subTitleIcon', 'apiKey')); } /** @@ -138,6 +139,7 @@ class TagController extends Controller { $subTitle = trans('firefly.edit_tag', ['tag' => $tag->tag]); $subTitleIcon = 'fa-tag'; + $apiKey = env('GOOGLE_MAPS_API_KEY', ''); /* * Default tag options (again) @@ -167,7 +169,7 @@ class TagController extends Controller Session::flash('gaEventCategory', 'tags'); Session::flash('gaEventAction', 'edit'); - return view('tags.edit', compact('tag', 'subTitle', 'subTitleIcon', 'tagOptions')); + return view('tags.edit', compact('tag', 'subTitle', 'subTitleIcon', 'tagOptions', 'apiKey')); } /** diff --git a/public/js/ff/accounts/show.js b/public/js/ff/accounts/show.js index 7cff7dc249..12851e0d75 100644 --- a/public/js/ff/accounts/show.js +++ b/public/js/ff/accounts/show.js @@ -90,5 +90,6 @@ function sortStop(event, ui) { current.animate({backgroundColor: "#5cb85c"}, 200, function () { $(this).animate({backgroundColor: originalBG}, 200); + return undefined; }); } diff --git a/public/js/ff/charts.js b/public/js/ff/charts.js index 8fc2915fc5..0e9ff49d21 100644 --- a/public/js/ff/charts.js +++ b/public/js/ff/charts.js @@ -147,7 +147,7 @@ function doubleYNonStackedChart(URI, container) { { display: true, ticks: { - callback: function (tickValue, index, ticks) { + callback: function (tickValue) { "use strict"; return accounting.formatMoney(tickValue); @@ -161,7 +161,7 @@ function doubleYNonStackedChart(URI, container) { { display: true, ticks: { - callback: function (tickValue, index, ticks) { + callback: function (tickValue) { "use strict"; return accounting.formatMoney(tickValue); diff --git a/public/js/ff/firefly.js b/public/js/ff/firefly.js index c3b1b46bd7..82a22f16c1 100644 --- a/public/js/ff/firefly.js +++ b/public/js/ff/firefly.js @@ -7,7 +7,7 @@ * * See the LICENSE file for details. */ -/** global: moment, dateRangeConfig, accounting, currencySymbol, mon_decimal_point, frac_digits, showFullList, showOnlyTop */ +/** global: moment, dateRangeConfig, accounting, currencySymbol, mon_decimal_point, frac_digits, showFullList, showOnlyTop, mon_thousands_sep */ $(function () { diff --git a/public/js/ff/import/status.js b/public/js/ff/import/status.js index 1a48091eaf..cf97ab9935 100644 --- a/public/js/ff/import/status.js +++ b/public/js/ff/import/status.js @@ -49,7 +49,7 @@ function updateBar(data) { $('#import-status-bar').text(data.stepsDone + '/' + data.steps); if (data.percentage >= 100) { - importComplete(data); + importComplete(); return; } return; diff --git a/public/js/ff/reports/index.js b/public/js/ff/reports/index.js index 67be970157..d59c0b3d30 100644 --- a/public/js/ff/reports/index.js +++ b/public/js/ff/reports/index.js @@ -15,7 +15,7 @@ $(function () { if ($('#inputDateRange').length > 0) { - var picker = $('#inputDateRange').daterangepicker( + $('#inputDateRange').daterangepicker( { locale: { format: 'YYYY-MM-DD', diff --git a/public/js/ff/split/journal/from-store.js b/public/js/ff/split/journal/from-store.js index a614d77d4e..a7323a12a6 100644 --- a/public/js/ff/split/journal/from-store.js +++ b/public/js/ff/split/journal/from-store.js @@ -8,7 +8,7 @@ * See the LICENSE file for details. */ -/** global: originalSum, amount, accounting */ +/** global: originalSum, accounting */ var destAccounts = {}; var srcAccounts = {}; @@ -61,7 +61,6 @@ function cloneRow() { "use strict"; var source = $('.table.split-table tbody tr').last().clone(); var count = $('.split-table tbody tr').length + 1; - var index = count - 1; source.removeClass('initial-row'); source.find('.count').text('#' + count); @@ -162,6 +161,6 @@ function calculateSum() { if (sum != originalSum) { var holder = $('#journal_amount_holder'); var par = holder.find('p.form-control-static'); - var amount = $('<span>').text(' (' + accounting.formatMoney(sum) + ')').addClass('text-danger amount-warning').appendTo(par); + $('<span>').text(' (' + accounting.formatMoney(sum) + ')').addClass('text-danger amount-warning').appendTo(par); } } \ No newline at end of file diff --git a/public/js/ff/tags/edit.js b/public/js/ff/tags/create-edit.js similarity index 97% rename from public/js/ff/tags/edit.js rename to public/js/ff/tags/create-edit.js index 53b66933c4..9670302e30 100644 --- a/public/js/ff/tags/edit.js +++ b/public/js/ff/tags/create-edit.js @@ -7,7 +7,7 @@ * * See the LICENSE file for details. */ -/** global: zoomLevel, latitude, longitude, google */ +/** global: zoomLevel, latitude, longitude, google, apiKey */ $(function () { "use strict"; diff --git a/public/js/ff/tags/create.js b/public/js/ff/tags/create.js deleted file mode 100644 index b252d00ae9..0000000000 --- a/public/js/ff/tags/create.js +++ /dev/null @@ -1,115 +0,0 @@ -/* - * create.js - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -/** global: zoomLevel, latitude, longitude, google */ - -$(function () { - "use strict"; - - $('#clearLocation').click(clearLocation); - -}); - -/* - Some vars as prep for the map: - */ -var map; -var markers = []; -var setTag = false; - -var mapOptions = { - zoom: zoomLevel, - center: new google.maps.LatLng(latitude, longitude), - disableDefaultUI: true -}; - -/* - Clear location and reset zoomLevel. - */ -function clearLocation() { - "use strict"; - deleteMarkers(); - $('input[name="latitude"]').val(""); - $('input[name="longitude"]').val(""); - $('input[name="zoomLevel"]').val("6"); - setTag = false; - $('input[name="setTag"]').val('false'); - return false; -} - -function initialize() { - "use strict"; - /* - Create new map: - */ - map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions); - - /* - Respond to click event. - */ - google.maps.event.addListener(map, 'rightclick', function (event) { - placeMarker(event); - }); - - /* - Respond to zoom event. - */ - google.maps.event.addListener(map, 'zoom_changed', function () { - saveZoomLevel(); - }); - /* - Maybe place marker? - */ - if(doPlaceMarker == true) { - var myLatlng = new google.maps.LatLng(latitude,longitude); - var fakeEvent = {}; - fakeEvent.latLng = myLatlng; - placeMarker(fakeEvent); - - } -} - -/** - * save zoom level of map into hidden input. - */ -function saveZoomLevel() { - "use strict"; - $('input[name="zoomLevel"]').val(map.getZoom()); -} - -/** - * Place marker on map. - * @param event - */ -function placeMarker(event) { - "use strict"; - deleteMarkers(); - var marker = new google.maps.Marker({position: event.latLng, map: map}); - $('input[name="latitude"]').val(event.latLng.lat()); - $('input[name="longitude"]').val(event.latLng.lng()); - markers.push(marker); - setTag = true; - $('input[name="setTag"]').val('true'); -} - - -/** - * Deletes all markers in the array by removing references to them. - */ -function deleteMarkers() { - "use strict"; - for (var i = 0; i < markers.length; i++) { - markers[i].setMap(null); - } - markers = []; -} - - -google.maps.event.addDomListener(window, 'load', initialize); \ No newline at end of file diff --git a/public/js/ff/tags/index.js b/public/js/ff/tags/index.js index 1234a027ba..990b265895 100644 --- a/public/js/ff/tags/index.js +++ b/public/js/ff/tags/index.js @@ -12,104 +12,4 @@ $(function () { "use strict"; - - $('#clearLocation').click(clearLocation); - }); - -/* - Some vars as prep for the map: - */ -var map; -var markers = []; -var setTag = false; - -var mapOptions = { - zoom: zoomLevel, - center: new google.maps.LatLng(latitude, longitude), - disableDefaultUI: true -}; - -/* - Clear location and reset zoomLevel. - */ -function clearLocation() { - "use strict"; - deleteMarkers(); - $('input[name="latitude"]').val(""); - $('input[name="longitude"]').val(""); - $('input[name="zoomLevel"]').val("6"); - setTag = false; - $('input[name="setTag"]').val('false'); - return false; -} - -function initialize() { - "use strict"; - /* - Create new map: - */ - map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions); - - /* - Respond to click event. - */ - google.maps.event.addListener(map, 'rightclick', function (event) { - placeMarker(event); - }); - - /* - Respond to zoom event. - */ - google.maps.event.addListener(map, 'zoom_changed', function () { - saveZoomLevel(); - }); - /* - Maybe place marker? - */ - if(doPlaceMarker == true) { - var myLatlng = new google.maps.LatLng(latitude,longitude); - var fakeEvent = {}; - fakeEvent.latLng = myLatlng; - placeMarker(fakeEvent); - - } -} - -/** - * save zoom level of map into hidden input. - */ -function saveZoomLevel() { - "use strict"; - $('input[name="zoomLevel"]').val(map.getZoom()); -} - -/** - * Place marker on map. - * @param event - */ -function placeMarker(event) { - "use strict"; - deleteMarkers(); - var marker = new google.maps.Marker({position: event.latLng, map: map}); - $('input[name="latitude"]').val(event.latLng.lat()); - $('input[name="longitude"]').val(event.latLng.lng()); - markers.push(marker); - setTag = true; - $('input[name="setTag"]').val('true'); -} - - -/** - * Deletes all markers in the array by removing references to them. - */ -function deleteMarkers() { - "use strict"; - for (var i = 0; i < markers.length; i++) { - markers[i].setMap(null); - } - markers = []; -} - - -google.maps.event.addDomListener(window, 'load', initialize); \ No newline at end of file diff --git a/public/js/ff/transactions/create-edit.js b/public/js/ff/transactions/create-edit.js index 44796c50bb..9719b76d6b 100644 --- a/public/js/ff/transactions/create-edit.js +++ b/public/js/ff/transactions/create-edit.js @@ -8,6 +8,8 @@ * See the LICENSE file for details. */ +/** global: what */ + $(document).ready(function () { "use strict"; diff --git a/public/js/ff/transactions/create.js b/public/js/ff/transactions/create.js index 0440006594..9c006e305e 100644 --- a/public/js/ff/transactions/create.js +++ b/public/js/ff/transactions/create.js @@ -8,7 +8,7 @@ * See the LICENSE file for details. */ -/** global: what, title, breadcrumbs, middleCrumbName, button, piggiesLength, txt */ +/** global: what, title, breadcrumbs, middleCrumbName, button, piggiesLength, txt, doSwitch, middleCrumbUrl */ $(document).ready(function () { "use strict"; @@ -37,7 +37,6 @@ function updateForm() { $('input[name="what"]').val(what); switch (what) { - default: case 'withdrawal': // show source_id and dest_name: $('#source_account_id_holder').show(); @@ -80,6 +79,7 @@ function updateForm() { } break; + default: case 'transfer': // show source_id and dest_id: $('#source_account_id_holder').show(); diff --git a/resources/views/tags/create.twig b/resources/views/tags/create.twig index 95b99f7d9d..1864f85843 100644 --- a/resources/views/tags/create.twig +++ b/resources/views/tags/create.twig @@ -80,7 +80,9 @@ var zoomLevel = 6; {% endif %} + var apiKey = "{{ apiKey }}"; + </script> - <script src="https://maps.googleapis.com/maps/api/js?v=3"></script> - <script src="js/ff/tags/create.js"></script> + <script src="https://maps.googleapis.com/maps/api/js?v=3&key={{ apiKey }}"></script> + <script src="js/ff/tags/create-edit.js"></script> {% endblock %} diff --git a/resources/views/tags/edit.twig b/resources/views/tags/edit.twig index e6a089d269..d427e05273 100644 --- a/resources/views/tags/edit.twig +++ b/resources/views/tags/edit.twig @@ -59,12 +59,13 @@ {% block scripts %} <script type="text/javascript"> {% if Input.old('latitude') %} - var latitude = "{{ Input.old('latitude') }}"; + var latitude = {{ Input.old('latitude') }}; {% else %} - var latitude = "52.3167"; + var latitude = {{ tag.latitude }}; {% endif %} - {% if Input.old('latitude') and Input.old('longitude') and Input.old('zoomLevel') %} + {% if (Input.old('latitude') and Input.old('longitude') and Input.old('zoomLevel')) + or (tag.latitude and tag.longitude and tag.zoomLevel) %} var doPlaceMarker = true; {% else %} var doPlaceMarker = false; @@ -73,16 +74,18 @@ {% if Input.old('longitude') %} var longitude = "{{ Input.old('longitude') }}"; {% else %} - var longitude = "5.5500"; + var longitude = {{ tag.longitude }}; {% endif %} {% if Input.old('zoomLevel') %} var zoomLevel = {{ Input.old('zoomLevel') }}; {% else %} - var zoomLevel = 6; + var zoomLevel = {{ tag.zoomLevel }}; {% endif %} + var apiKey = "{{ apiKey }}"; + </script> - <script src="https://maps.googleapis.com/maps/api/js?v=3"></script> - <script src="js/ff/tags/edit.js"></script> + <script src="https://maps.googleapis.com/maps/api/js?v=3&key={{ apiKey }}"></script> + <script src="js/ff/tags/create-edit.js"></script> {% endblock %} From f797344106df55e032bc7a35866cc2d1f029231c Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 2 Jan 2017 12:15:33 +0100 Subject: [PATCH 480/709] Default values [skip ci] --- resources/views/tags/edit.twig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/views/tags/edit.twig b/resources/views/tags/edit.twig index d427e05273..99366b5546 100644 --- a/resources/views/tags/edit.twig +++ b/resources/views/tags/edit.twig @@ -61,7 +61,7 @@ {% if Input.old('latitude') %} var latitude = {{ Input.old('latitude') }}; {% else %} - var latitude = {{ tag.latitude }}; + var latitude = {{ tag.latitude|default("52.3167") }}; {% endif %} {% if (Input.old('latitude') and Input.old('longitude') and Input.old('zoomLevel')) @@ -74,13 +74,13 @@ {% if Input.old('longitude') %} var longitude = "{{ Input.old('longitude') }}"; {% else %} - var longitude = {{ tag.longitude }}; + var longitude = {{ tag.longitude|default("5.5500") }}; {% endif %} {% if Input.old('zoomLevel') %} var zoomLevel = {{ Input.old('zoomLevel') }}; {% else %} - var zoomLevel = {{ tag.zoomLevel }}; + var zoomLevel = {{ tag.zoomLevel|default("6") }}; {% endif %} var apiKey = "{{ apiKey }}"; From 800478d437b50a1bdd8212dcf4bf0e7434f7ef18 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 2 Jan 2017 12:18:18 +0100 Subject: [PATCH 481/709] Add support for Polish #517 [skip ci] --- config/firefly.php | 1 + 1 file changed, 1 insertion(+) diff --git a/config/firefly.php b/config/firefly.php index 1cba0ac243..7b4632912e 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -118,6 +118,7 @@ return [ 'fr_FR' => ['name_locale' => 'Français', 'name_english' => 'French', 'complete' => false], 'hr_HR' => ['name_locale' => 'hrvatski', 'name_english' => 'Croatian', 'complete' => false], 'nl_NL' => ['name_locale' => 'Nederlands', 'name_english' => 'Dutch', 'complete' => true], + 'pl_PL' => ['name_locale' => 'Polski', 'name_english' => 'Polish ', 'complete' => false], 'pt_BR' => ['name_locale' => 'Português do Brasil', 'name_english' => 'Portuguese (Brazil)', 'complete' => true], 'ru-RU' => ['name_locale' => 'Russian', 'name_english' => 'Russian', 'complete' => false], 'zh-HK' => ['name_locale' => '繁體中文(香港)', 'name_english' => 'Chinese Traditional, Hong Kong', 'complete' => false], From dcd89d38e759337da4f8b1a81062a287daef480d Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 2 Jan 2017 12:18:29 +0100 Subject: [PATCH 482/709] Small JS fixes [skip ci] --- public/js/ff/tags/create-edit.js | 10 ++++------ public/js/ff/transactions/create.js | 4 +++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/public/js/ff/tags/create-edit.js b/public/js/ff/tags/create-edit.js index 9670302e30..397f49ccef 100644 --- a/public/js/ff/tags/create-edit.js +++ b/public/js/ff/tags/create-edit.js @@ -1,13 +1,11 @@ /* - * edit.js - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. + * create-edit.js + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. * * See the LICENSE file for details. */ -/** global: zoomLevel, latitude, longitude, google, apiKey */ +/** global: zoomLevel, latitude, longitude, google, apiKey, doPlaceMarker */ $(function () { "use strict"; diff --git a/public/js/ff/transactions/create.js b/public/js/ff/transactions/create.js index 9c006e305e..aaed65207a 100644 --- a/public/js/ff/transactions/create.js +++ b/public/js/ff/transactions/create.js @@ -79,7 +79,6 @@ function updateForm() { } break; - default: case 'transfer': // show source_id and dest_id: $('#source_account_id_holder').show(); @@ -98,6 +97,9 @@ function updateForm() { $('#piggy_bank_id_holder').show(); } break; + default: + // no action. + break; } } From 8aa2e3d2f5c21c0c19734e995f634baa4db85949 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 2 Jan 2017 15:22:30 +0100 Subject: [PATCH 483/709] Various code cleanup. --- .env.example | 4 +++- app/Validation/FireflyValidator.php | 9 +++++++++ config/firefly.php | 2 +- public/js/ff/accounts/show.js | 1 + resources/lang/en_US/demo.php | 2 +- 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index 28d87ce566..87540a4f1c 100755 --- a/.env.example +++ b/.env.example @@ -40,6 +40,7 @@ SHOW_INCOMPLETE_TRANSLATIONS=false CACHE_PREFIX=firefly +GOOGLE_MAPS_API_KEY= ANALYTICS_ID= SITE_OWNER=mail@example.com @@ -48,4 +49,5 @@ PUSHER_SECRET= PUSHER_APP_ID= DEMO_USERNAME= -DEMO_PASSWORD= \ No newline at end of file +DEMO_PASSWORD= + diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index b5bdf896a2..778996bb88 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -53,6 +53,7 @@ class FireflyValidator extends Validator } /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @param $attribute * @param $value * @@ -71,6 +72,7 @@ class FireflyValidator extends Validator } /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @param $attribute * @param $value * @param $parameters @@ -94,6 +96,7 @@ class FireflyValidator extends Validator } /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @param $attribute * @param $value * @@ -115,6 +118,7 @@ class FireflyValidator extends Validator } /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @param $attribute * @param $value * @@ -144,6 +148,7 @@ class FireflyValidator extends Validator } /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @param $attribute * @param $value * @param $parameters @@ -256,6 +261,7 @@ class FireflyValidator extends Validator } /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @param $attribute * @param $value * @param $parameters @@ -286,6 +292,7 @@ class FireflyValidator extends Validator } /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @param $attribute * @param $value * @param $parameters @@ -319,6 +326,7 @@ class FireflyValidator extends Validator } /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * Validate an object and its unicity. Checks for encryption / encrypted values as well. * * parameter 0: the table @@ -356,6 +364,7 @@ class FireflyValidator extends Validator } /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @param $attribute * @param $value * @param $parameters diff --git a/config/firefly.php b/config/firefly.php index 7b4632912e..444a786cd6 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -112,7 +112,7 @@ return [ 'Cash account' => 'cash', ], 'languages' => [ - 'de_DE' => ['name_locale' => 'Deutsch', 'name_english' => 'German', 'complete' => false], + 'de_DE' => ['name_locale' => 'Deutsch', 'name_english' => 'German', 'complete' => true], 'en_US' => ['name_locale' => 'English', 'name_english' => 'English', 'complete' => true], 'es_ES' => ['name_locale' => 'Español', 'name_english' => 'Spanish', 'complete' => false], 'fr_FR' => ['name_locale' => 'Français', 'name_english' => 'French', 'complete' => false], diff --git a/public/js/ff/accounts/show.js b/public/js/ff/accounts/show.js index 12851e0d75..a1db23e92a 100644 --- a/public/js/ff/accounts/show.js +++ b/public/js/ff/accounts/show.js @@ -92,4 +92,5 @@ function sortStop(event, ui) { $(this).animate({backgroundColor: originalBG}, 200); return undefined; }); + return undefined; } diff --git a/resources/lang/en_US/demo.php b/resources/lang/en_US/demo.php index 6cd5476b57..e7f8ea934d 100644 --- a/resources/lang/en_US/demo.php +++ b/resources/lang/en_US/demo.php @@ -11,7 +11,7 @@ return [ 'no_demo_text' => 'Sorry, there is no extra demo-explanation text for <abbr title=":route">this page</abbr>.', 'see_help_icon' => 'However, the <i class="fa fa-question-circle"></i>-icon in the top right corner may tell you more.', 'index' => 'Welcome to <strong>Firefly III</strong>! On this page you get a quick overview of your finances. For more information, check out Accounts → <a href=":asset">Asset Accounts</a> and of course the <a href=":budgets">Budgets</a> and <a href=":reports">Reports</a> pages. Or just take a look around and see where you end up.', - 'accounts-index' => 'Asset accounts are your personal bank ac counts. Expense accounts are the accounts you spend money at, such as stores and friends. Revenue accounts are accounts you receive money from, such as your job, the government or other sources of income. On this page you can edit or remove them.', + 'accounts-index' => 'Asset accounts are your personal bank accounts. Expense accounts are the accounts you spend money at, such as stores and friends. Revenue accounts are accounts you receive money from, such as your job, the government or other sources of income. On this page you can edit or remove them.', 'budgets-index' => 'This page shows you an overview of your budgets. The top bar shows the amount that is available to be budgeted. This can be customized for any period by clicking the amount on the right. The amount you\'ve actually spent is shown in the bar below. Below that are the expenses per budget and what you\'ve budgeted for them.', 'reports-index-start' => 'Firefly III supports four types of reports. Read about them by clicking on the <i class="fa fa-question-circle"></i>-icon in the top right corner.', 'reports-index-examples' => 'Be sure to check out these examples: <a href=":one">a monthly financial overview</a>, <a href=":two">a yearly financial overview</a> and <a href=":three">a budget overview</a>.', From e323f5a2d56eb259c697d49d434a24c1e4fe7b11 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 2 Jan 2017 19:27:27 +0100 Subject: [PATCH 484/709] This should fix most amounts for #511 --- resources/views/list/accounts.twig | 13 ++++++++++--- resources/views/list/bills.twig | 8 ++++++-- resources/views/list/journals-tasker.twig | 7 ++++--- resources/views/list/piggy-bank-events.twig | 2 +- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/resources/views/list/accounts.twig b/resources/views/list/accounts.twig index 12b6111c8e..dbf3d7436c 100644 --- a/resources/views/list/accounts.twig +++ b/resources/views/list/accounts.twig @@ -10,7 +10,8 @@ <th data-defaultsign="_19">{{ trans('list.currentBalance') }}</th> <th class="hidden-sm hidden-xs">{{ trans('list.active') }}</th> <th data-defaultsign="month" class="hidden-sm hidden-xs">{{ trans('list.lastActivity') }}</th> - <th data-defaultsign="_19" class="hidden-sm hidden-xs">{{ trans('list.balanceDiff', {'start' : Session.get('start').formatLocalized(monthAndDayFormat),'end' : Session.get('end').formatLocalized(monthAndDayFormat)}) }}</th> + <th data-defaultsign="_19" + class="hidden-sm hidden-xs">{{ trans('list.balanceDiff', {'start' : Session.get('start').formatLocalized(monthAndDayFormat),'end' : Session.get('end').formatLocalized(monthAndDayFormat)}) }}</th> </tr> </thead> <tbody> @@ -33,7 +34,11 @@ </td> {% endif %} <td class="hidden-sm hidden-xs">{{ account.iban }}</td> - <td data-value="{{ account.endBalance }}">{{ account.endBalance|formatAmount }}</td> + <td data-value="{{ account.endBalance }}" style="text-align: right;"> + <span style="margin-right:5px;"> + {{ account.endBalance|formatAmount }} + </span> + </td> <td class="hidden-sm hidden-xs" data-value="{{ account.active }}"> {% if account.active %} <i class="fa fa-fw fa-check"></i> @@ -50,8 +55,10 @@ <em>{{ 'never'|_ }}</em> </td> {% endif %} - <td class="hidden-sm hidden-xs" data-value="{{ account.difference }}"> + <td class="hidden-sm hidden-xs" data-value="{{ account.difference }}" style="text-align: right;"> + <span style="margin-right:5px;"> {{ (account.difference)|formatAmount }} + </span> </td> </tr> diff --git a/resources/views/list/bills.twig b/resources/views/list/bills.twig index 2bf6f5b128..7e6d1af0b1 100644 --- a/resources/views/list/bills.twig +++ b/resources/views/list/bills.twig @@ -29,11 +29,15 @@ <span class="label label-info">{{ match }}</span> {% endfor %} </td> - <td data-value="{{ entry.amount_min }}"> + <td data-value="{{ entry.amount_min }}" style="text-align: right;"> + <span style="margin-right:5px;"> {{ entry.amount_min|formatAmount }} + </span> </td> - <td data-value="{{ entry.amount_max }}"> + <td data-value="{{ entry.amount_max }}" style="text-align: right;"> + <span style="margin-right:5px;"> {{ entry.amount_max|formatAmount }} + </span> </td> {% if entry.paidDates.count() == 0 and entry.payDates.count() == 0 and entry.active %} diff --git a/resources/views/list/journals-tasker.twig b/resources/views/list/journals-tasker.twig index 37cbcd1b72..a52df725be 100644 --- a/resources/views/list/journals-tasker.twig +++ b/resources/views/list/journals-tasker.twig @@ -1,6 +1,6 @@ {{ journals.render|raw }} -<table class="table table-hover table-compressed {% if sorting %}sortable-table{% endif %}"> +<table class="table table-hover table-condensed {% if sorting %}sortable-table{% endif %}"> <thead> <tr class="ignore"> <th class="hidden-xs no_select_boxes" colspan="2"> </th> @@ -62,12 +62,13 @@ {% endif %} </td> - <td> + <td style="text-align: right;"> + <span style="margin-right:5px;"> <!-- format amount of transaction --> {{ formatByCode(transaction.transaction_currency_code, transaction.transaction_amount) }} <!-- and then amount of journal itself. --> {{ optionalJournalAmount(transaction.journal_id, transaction.transaction_amount, transaction.transaction_currency_code, transaction.transaction_type_type) }} - + </span> </td> <td class="hidden-sm hidden-xs"> diff --git a/resources/views/list/piggy-bank-events.twig b/resources/views/list/piggy-bank-events.twig index 69caeb6247..df71625b69 100644 --- a/resources/views/list/piggy-bank-events.twig +++ b/resources/views/list/piggy-bank-events.twig @@ -22,7 +22,7 @@ {% endif %} </td> - <td> + <td style="text-align: right;"> {% if event.amount < 0 %} <span class="text-danger">{{ trans('firefly.removed_amount', {amount: (event.amount)|formatAmountPlain})|raw }}</span> {% else %} From ad116d19595103e15361506577033d8463d0760f Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 2 Jan 2017 20:42:29 +0100 Subject: [PATCH 485/709] Various code cleanup [skip ci] --- .github/CONTRIBUTING | 1 + app/Export/Collector/CollectorInterface.php | 2 + app/Export/Exporter/ExporterInterface.php | 2 + app/Export/ProcessorInterface.php | 1 + .../Chart/Basic/ChartJsGenerator.php | 1 + .../Controllers/Chart/BudgetController.php | 142 +++++++++--------- .../Controllers/Chart/CategoryController.php | 7 +- 7 files changed, 88 insertions(+), 68 deletions(-) create mode 100644 .github/CONTRIBUTING diff --git a/.github/CONTRIBUTING b/.github/CONTRIBUTING new file mode 100644 index 0000000000..2a1338d94d --- /dev/null +++ b/.github/CONTRIBUTING @@ -0,0 +1 @@ +If you are requesting a new feature, please check out the list of [often requested features](https://firefly-iii.github.io/requested-features/) first. Thanks! \ No newline at end of file diff --git a/app/Export/Collector/CollectorInterface.php b/app/Export/Collector/CollectorInterface.php index 6fc24233fe..f1778e9adc 100644 --- a/app/Export/Collector/CollectorInterface.php +++ b/app/Export/Collector/CollectorInterface.php @@ -35,6 +35,8 @@ interface CollectorInterface /** * @param Collection $entries * + * @return void + * */ public function setEntries(Collection $entries); diff --git a/app/Export/Exporter/ExporterInterface.php b/app/Export/Exporter/ExporterInterface.php index 9b8a4e4ed9..3559d89cec 100644 --- a/app/Export/Exporter/ExporterInterface.php +++ b/app/Export/Exporter/ExporterInterface.php @@ -40,6 +40,8 @@ interface ExporterInterface /** * @param Collection $entries * + * @return void + * */ public function setEntries(Collection $entries); diff --git a/app/Export/ProcessorInterface.php b/app/Export/ProcessorInterface.php index 459cbf6276..614d748304 100644 --- a/app/Export/ProcessorInterface.php +++ b/app/Export/ProcessorInterface.php @@ -27,6 +27,7 @@ interface ProcessorInterface * Processor constructor. * * @param array $settings + * */ public function __construct(array $settings); diff --git a/app/Generator/Chart/Basic/ChartJsGenerator.php b/app/Generator/Chart/Basic/ChartJsGenerator.php index b651770829..85cff4d4f6 100644 --- a/app/Generator/Chart/Basic/ChartJsGenerator.php +++ b/app/Generator/Chart/Basic/ChartJsGenerator.php @@ -49,6 +49,7 @@ class ChartJsGenerator implements GeneratorInterface * ] * ] * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five. * * @param array $data * diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index 80fb6142aa..0a8fe543cf 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -42,26 +42,35 @@ class BudgetController extends Controller /** @var GeneratorInterface */ protected $generator; + /** @var BudgetRepositoryInterface */ + protected $repository; + /** * BudgetController constructor. */ public function __construct() { parent::__construct(); - $this->generator = app(GeneratorInterface::class); + + $this->middleware( + function ($request, $next) { + $this->generator = app(GeneratorInterface::class); + $this->repository = app(BudgetRepositoryInterface::class); + + return $next($request); + } + ); } /** - * checked * - * @param BudgetRepositoryInterface $repository - * @param Budget $budget + * @param Budget $budget * * @return \Symfony\Component\HttpFoundation\Response */ - public function budget(BudgetRepositoryInterface $repository, Budget $budget) + public function budget(Budget $budget) { - $first = $repository->firstUseDate($budget); + $first = $this->repository->firstUseDate($budget); $range = Preferences::get('viewRange', '1M')->data; $last = session('end', new Carbon); @@ -86,7 +95,7 @@ class BudgetController extends Controller $currentEnd = Navigation::endOfPeriod($first, $range); // sub another day because reasons. $currentEnd->subDay(); - $spent = $repository->spentInPeriod($budgetCollection, new Collection, $currentStart, $currentEnd); + $spent = $this->repository->spentInPeriod($budgetCollection, new Collection, $currentStart, $currentEnd); $format = Navigation::periodShow($first, $range); $entries[$format] = bcmul($spent, '-1'); $first = Navigation::addPeriod($first, $range, 0); @@ -103,13 +112,13 @@ class BudgetController extends Controller * Shows the amount left in a specific budget limit. * * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five. - * @param BudgetRepositoryInterface $repository - * @param Budget $budget - * @param BudgetLimit $budgetLimit + * @param Budget $budget + * @param BudgetLimit $budgetLimit * * @return \Symfony\Component\HttpFoundation\Response + * @throws FireflyException */ - public function budgetLimit(BudgetRepositoryInterface $repository, Budget $budget, BudgetLimit $budgetLimit) + public function budgetLimit(Budget $budget, BudgetLimit $budgetLimit) { if ($budgetLimit->budget->id != $budget->id) { throw new FireflyException('This budget limit is not part of this budget.'); @@ -131,7 +140,7 @@ class BudgetController extends Controller $amount = $budgetLimit->amount; $budgetCollection = new Collection([$budget]); while ($start <= $end) { - $spent = $repository->spentInPeriod($budgetCollection, new Collection, $start, $start); + $spent = $this->repository->spentInPeriod($budgetCollection, new Collection, $start, $start); $amount = bcadd($amount, $spent); $format = $start->formatLocalized(strval(trans('config.month_and_day'))); $entries[$format] = $amount; @@ -149,11 +158,9 @@ class BudgetController extends Controller * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five. * @SuppressWarnings(PHPMD.ExcessiveMethodLength) // 46 lines, I'm fine with this. * - * @param BudgetRepositoryInterface $repository - * * @return \Symfony\Component\HttpFoundation\Response */ - public function frontpage(BudgetRepositoryInterface $repository) + public function frontpage() { $start = session('start', Carbon::now()->startOfMonth()); $end = session('end', Carbon::now()->endOfMonth()); @@ -165,7 +172,7 @@ class BudgetController extends Controller if ($cache->has()) { return Response::json($cache->get()); } - $budgets = $repository->getActiveBudgets(); + $budgets = $this->repository->getActiveBudgets(); $chartData = [ ['label' => strval(trans('firefly.spent_in_budget')), 'entries' => [], 'type' => 'bar',], ['label' => strval(trans('firefly.left_to_spend')), 'entries' => [], 'type' => 'bar',], @@ -176,7 +183,7 @@ class BudgetController extends Controller /** @var Budget $budget */ foreach ($budgets as $budget) { // get relevant repetitions: - $limits = $repository->getBudgetLimits($budget, $start, $end); + $limits = $this->repository->getBudgetLimits($budget, $start, $end); $expenses = $this->getExpensesForBudget($limits, $budget, $start, $end); foreach ($expenses as $name => $row) { $chartData[0]['entries'][$name] = $row['spent']; @@ -201,15 +208,16 @@ class BudgetController extends Controller /** - * @param BudgetRepositoryInterface $repository - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts + * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five. + * + * @param Budget $budget + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts * * @return \Illuminate\Http\JsonResponse */ - public function period(BudgetRepositoryInterface $repository, Budget $budget, Collection $accounts, Carbon $start, Carbon $end) + public function period(Budget $budget, Collection $accounts, Carbon $start, Carbon $end) { // chart properties for cache: $cache = new CacheProperties(); @@ -221,37 +229,14 @@ class BudgetController extends Controller if ($cache->has()) { return Response::json($cache->get()); } - - // get the expenses - $budgeted = []; $periods = Navigation::listOfPeriods($start, $end); - $entries = $repository->getBudgetPeriodReport(new Collection([$budget]), $accounts, $start, $end); - $key = Navigation::preferredCarbonFormat($start, $end); - $range = Navigation::preferredRangeFormat($start, $end); - $current = clone $start; - while ($current < $end) { - - $currentStart = Navigation::startOfPeriod($current, $range); - $currentEnd = Navigation::endOfPeriod($current, $range); - $budgetLimits = $repository->getBudgetLimits($budget, $currentStart, $currentEnd); - $index = $currentStart->format($key); - $budgeted[$index] = $budgetLimits->sum('amount'); - $currentEnd->addDay(); - $current = clone $currentEnd; - } + $entries = $this->repository->getBudgetPeriodReport(new Collection([$budget]), $accounts, $start, $end); // get the expenses + $budgeted = $this->getBudgetedInPeriod($budget, $start, $end); // join them into one set of data: $chartData = [ - [ - 'label' => strval(trans('firefly.spent')), - 'type' => 'bar', - 'entries' => [], - ], - [ - 'label' => strval(trans('firefly.budgeted')), - 'type' => 'bar', - 'entries' => [], - ], + ['label' => strval(trans('firefly.spent')), 'type' => 'bar', 'entries' => [],], + ['label' => strval(trans('firefly.budgeted')), 'type' => 'bar', 'entries' => [],], ]; foreach (array_keys($periods) as $period) { @@ -260,7 +245,6 @@ class BudgetController extends Controller $limit = isset($budgeted[$period]) ? $budgeted[$period] : 0; $chartData[0]['entries'][$label] = round(bcmul($spent, '-1'), 12); $chartData[1]['entries'][$label] = $limit; - } $data = $this->generator->multiSet($chartData); $cache->store($data); @@ -269,14 +253,13 @@ class BudgetController extends Controller } /** - * @param BudgetRepositoryInterface $repository - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end * * @return \Illuminate\Http\JsonResponse */ - public function periodNoBudget(BudgetRepositoryInterface $repository, Collection $accounts, Carbon $start, Carbon $end) + public function periodNoBudget(Collection $accounts, Carbon $start, Carbon $end) { // chart properties for cache: $cache = new CacheProperties(); @@ -290,7 +273,7 @@ class BudgetController extends Controller // the expenses: $periods = Navigation::listOfPeriods($start, $end); - $entries = $repository->getNoBudgetPeriodReport($accounts, $start, $end); + $entries = $this->repository->getNoBudgetPeriodReport($accounts, $start, $end); $chartData = []; // join them: @@ -305,6 +288,32 @@ class BudgetController extends Controller return Response::json($data); } + /** + * @param Budget $budget + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + private function getBudgetedInPeriod(Budget $budget, Carbon $start, Carbon $end): array + { + $key = Navigation::preferredCarbonFormat($start, $end); + $range = Navigation::preferredRangeFormat($start, $end); + $current = clone $start; + $budgeted = []; + while ($current < $end) { + $currentStart = Navigation::startOfPeriod($current, $range); + $currentEnd = Navigation::endOfPeriod($current, $range); + $budgetLimits = $this->repository->getBudgetLimits($budget, $currentStart, $currentEnd); + $index = $currentStart->format($key); + $budgeted[$index] = $budgetLimits->sum('amount'); + $currentEnd->addDay(); + $current = clone $currentEnd; + } + + return $budgeted; + } + /** * * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's 6 but ok. @@ -318,11 +327,9 @@ class BudgetController extends Controller */ private function getExpensesForBudget(Collection $limits, Budget $budget, Carbon $start, Carbon $end): array { - /** @var BudgetRepositoryInterface $repository */ - $repository = app(BudgetRepositoryInterface::class); - $return = []; + $return = []; if ($limits->count() === 0) { - $spent = $repository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end); + $spent = $this->repository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end); if (bccomp($spent, '0') !== 0) { $return[$budget->name]['spent'] = bcmul($spent, '-1'); $return[$budget->name]['left'] = 0; @@ -332,7 +339,7 @@ class BudgetController extends Controller return $return; } - $rows = $this->spentInPeriodMulti($repository, $budget, $limits); + $rows = $this->spentInPeriodMulti($budget, $limits); foreach ($rows as $name => $row) { if (bccomp($row['spent'], '0') !== 0 || bccomp($row['left'], '0') !== 0) { $return[$name]['spent'] = bcmul($row['spent'], '-1'); @@ -346,6 +353,8 @@ class BudgetController extends Controller } /** + * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five. + * * Returns an array with the following values: * 0 => * 'name' => name of budget + repetition @@ -354,20 +363,19 @@ class BudgetController extends Controller * 'spent' => actually spent in period for budget * 1 => (etc) * - * @param BudgetRepositoryInterface $repository - * @param Budget $budget - * @param Collection $limits + * @param Budget $budget + * @param Collection $limits * * @return array */ - private function spentInPeriodMulti(BudgetRepositoryInterface $repository, Budget $budget, Collection $limits): array + private function spentInPeriodMulti(Budget $budget, Collection $limits): array { $return = []; $format = strval(trans('config.month_and_day')); $name = $budget->name; /** @var BudgetLimit $budgetLimit */ foreach ($limits as $budgetLimit) { - $expenses = $repository->spentInPeriod(new Collection([$budget]), new Collection, $budgetLimit->start_date, $budgetLimit->end_date); + $expenses = $this->repository->spentInPeriod(new Collection([$budget]), new Collection, $budgetLimit->start_date, $budgetLimit->end_date); if ($limits->count() > 1) { $name = $budget->name . ' ' . trans( diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index c0ff85e2e3..6ba325636a 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -65,7 +65,12 @@ class CategoryController extends Controller return Response::json($cache->get()); } - $start = $repository->firstUseDate($category); + $start = $repository->firstUseDate($category); + + if ($start->year == 1900) { + $start = new Carbon; + } + $range = Preferences::get('viewRange', '1M')->data; $start = Navigation::startOfPeriod($start, $range); $end = new Carbon; From b91f6c7ce6d747ce06a79ead7b95b08f99f14606 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 2 Jan 2017 21:04:17 +0100 Subject: [PATCH 486/709] More alignment for #511 --- resources/views/accounts/show.twig | 8 ++-- resources/views/categories/show.twig | 8 ++-- resources/views/reports/budget/month.twig | 24 +++++------ resources/views/reports/category/month.twig | 36 ++++++++--------- .../views/reports/partials/accounts.twig | 18 ++++----- resources/views/reports/partials/balance.twig | 12 +++--- resources/views/reports/partials/bills.twig | 16 ++++---- .../views/reports/partials/budget-period.twig | 10 ++--- resources/views/reports/partials/budgets.twig | 40 ++++++++++++------- .../views/reports/partials/categories.twig | 8 ++-- .../reports/partials/category-period.twig | 12 +++--- .../reports/partials/income-expenses.twig | 10 ++--- .../partials/journals-audit-tasker.twig | 12 +++--- .../views/reports/partials/operations.twig | 6 +-- 14 files changed, 116 insertions(+), 104 deletions(-) diff --git a/resources/views/accounts/show.twig b/resources/views/accounts/show.twig index b68c607cde..e1f7cc7e20 100644 --- a/resources/views/accounts/show.twig +++ b/resources/views/accounts/show.twig @@ -107,14 +107,14 @@ <table class="table table-hover"> {% if entry[2] != 0 %} <tr> - <td colspan="33%">{{ 'spent'|_ }}</td> - <td colspan="67%">{{ entry[2]|formatAmount }}</td> + <td style="width:33%;">{{ 'spent'|_ }}</td> + <td style="text-align: right;">{{ entry[2]|formatAmount }}</td> </tr> {% endif %} {% if entry[3] != 0 %} <tr> - <td colspan="33%">{{ 'earned'|_ }}</td> - <td colspan="67%">{{ entry[3]|formatAmount }}</td> + <td style="width: 33%;">{{ 'earned'|_ }}</td> + <td style="text-align: right;">{{ entry[3]|formatAmount }}</td> </tr> {% endif %} </table> diff --git a/resources/views/categories/show.twig b/resources/views/categories/show.twig index 77954e0501..f824d79b75 100644 --- a/resources/views/categories/show.twig +++ b/resources/views/categories/show.twig @@ -96,14 +96,14 @@ <table class="table table-hover"> {% if entry[2] != 0 %} <tr> - <td colspan="33%">{{ 'spent'|_ }}</td> - <td colspan="67%">{{ entry[2]|formatAmount }}</td> + <td style="width:33%;">{{ 'spent'|_ }}</td> + <td style="text-align: right;">{{ entry[2]|formatAmount }}</td> </tr> {% endif %} {% if entry[3] != 0 %} <tr> - <td colspan="33%">{{ 'earned'|_ }}</td> - <td colspan="67%">{{ entry[3]|formatAmount }}</td> + <td style="width:33%;">{{ 'earned'|_ }}</td> + <td style="text-align: right;">{{ entry[3]|formatAmount }}</td> </tr> {% endif %} </table> diff --git a/resources/views/reports/budget/month.twig b/resources/views/reports/budget/month.twig index c3009fe2a0..e8de3bee8f 100644 --- a/resources/views/reports/budget/month.twig +++ b/resources/views/reports/budget/month.twig @@ -17,7 +17,7 @@ <thead> <tr> <th data-defaultsign="az">{{ 'name'|_ }}</th> - <th data-defaultsign="_19">{{ 'spent'|_ }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ 'spent'|_ }}</th> </tr> </thead> <tbody> @@ -27,9 +27,9 @@ <a href="{{ route('accounts.show', account.id) }}" title="{{ account.name }}">{{ account.name }}</a> </td> {% if accountSummary[account.id] %} - <td data-value="{{ accountSummary[account.id] }}">{{ accountSummary[account.id]|formatAmount }}</td> + <td data-value="{{ accountSummary[account.id] }}" style="text-align: right;">{{ accountSummary[account.id]|formatAmount }}</td> {% else %} - <td data-value="0">{{ 0|formatAmount }}</td> + <td data-value="0" style="text-align: right;">{{ 0|formatAmount }}</td> {% endif %} </tr> {% endfor %} @@ -47,7 +47,7 @@ <thead> <tr> <th data-defaultsign="az">{{ 'name'|_ }}</th> - <th data-defaultsign="_19">{{ 'spent'|_ }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ 'spent'|_ }}</th> </tr> </thead> <tbody> @@ -57,9 +57,9 @@ <a href="{{ route('budgets.show', budget.id) }}" title="{{ budget.name }}">{{ budget.name }}</a> </td> {% if budgetSummary[budget.id] %} - <td data-value="{{ budgetSummary[budget.id] }}">{{ budgetSummary[budget.id]|formatAmount }}</td> + <td data-value="{{ budgetSummary[budget.id] }}" style="text-align: right;">{{ budgetSummary[budget.id]|formatAmount }}</td> {% else %} - <td data-value="0">{{ 0|formatAmount }}</td> + <td data-value="0" style="text-align: right;">{{ 0|formatAmount }}</td> {% endif %} </tr> {% endfor %} @@ -133,8 +133,8 @@ <thead> <tr> <th data-defaultsign="az">{{ 'account'|_ }}</th> - <th data-defaultsign="_19">{{ 'spent_average'|_ }}</th> - <th data-defaultsign="_19">{{ 'total'|_ }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ 'spent_average'|_ }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ 'total'|_ }}</th> <th data-defaultsign="_19">{{ 'transaction_count'|_ }}</th> </tr> </thead> @@ -148,10 +148,10 @@ <td data-value="{{ row.name }}"> <a href="{{ route('accounts.show', row.id) }}">{{ row.name }}</a> </td> - <td data-value="{{ row.average }}"> + <td data-value="{{ row.average }}" style="text-align: right;"> {{ row.average|formatAmount }} </td> - <td data-value="{{ row.sum }}"> + <td data-value="{{ row.sum }}" style="text-align: right;"> {{ row.sum|formatAmount }} </td> <td data-value="{{ row.count }}"> @@ -188,7 +188,7 @@ <th data-defaultsort="disabled">{{ 'description'|_ }}</th> <th data-defaultsign="month">{{ 'date'|_ }}</th> <th data-defaultsign="az">{{ 'account'|_ }}</th> - <th data-defaultsign="_19">{{ 'amount'|_ }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ 'amount'|_ }}</th> </tr> </thead> <tbody> @@ -215,7 +215,7 @@ {{ row.opposing_account_name }} </a> </td> - <td data-value="{{ row.transaction_amount }}"> + <td data-value="{{ row.transaction_amount }}" style="text-align: right;"> {{ row.transaction_amount|formatAmount }} </td> </tr> diff --git a/resources/views/reports/category/month.twig b/resources/views/reports/category/month.twig index 76df238013..cef7514534 100644 --- a/resources/views/reports/category/month.twig +++ b/resources/views/reports/category/month.twig @@ -17,8 +17,8 @@ <thead> <tr> <th data-defaultsign="az">{{ 'name'|_ }}</th> - <th data-defaultsign="_19">{{ 'earned'|_ }}</th> - <th data-defaultsign="_19">{{ 'spent'|_ }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ 'earned'|_ }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ 'spent'|_ }}</th> </tr> </thead> <tbody> @@ -28,14 +28,14 @@ <a href="{{ route('accounts.show', account.id) }}" title="{{ account.name }}">{{ account.name }}</a> </td> {% if accountSummary[account.id] %} - <td data-value="{{ accountSummary[account.id].earned }}">{{ accountSummary[account.id].earned|formatAmount }}</td> + <td data-value="{{ accountSummary[account.id].earned }}" style="text-align: right;">{{ accountSummary[account.id].earned|formatAmount }}</td> {% else %} - <td data-value="0">{{ 0|formatAmount }}</td> + <td data-value="0" style="text-align: right;">{{ 0|formatAmount }}</td> {% endif %} {% if accountSummary[account.id] %} - <td data-value="{{ accountSummary[account.id].spent }}">{{ accountSummary[account.id].spent|formatAmount }}</td> + <td data-value="{{ accountSummary[account.id].spent }}" style="text-align: right;">{{ accountSummary[account.id].spent|formatAmount }}</td> {% else %} - <td data-value="0">{{ 0|formatAmount }}</td> + <td data-value="0" style="text-align: right;">{{ 0|formatAmount }}</td> {% endif %} </tr> {% endfor %} @@ -53,8 +53,8 @@ <thead> <tr> <th data-defaultsign="az">{{ 'name'|_ }}</th> - <th data-defaultsign="_19">{{ 'earned'|_ }}</th> - <th data-defaultsign="_19">{{ 'spent'|_ }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ 'earned'|_ }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ 'spent'|_ }}</th> </tr> </thead> <tbody> @@ -64,14 +64,14 @@ <a href="{{ route('categories.show', category.id) }}" title="{{ category.name }}">{{ category.name }}</a> </td> {% if categorySummary[category.id] %} - <td data-value="{{ categorySummary[category.id].earned }}">{{ categorySummary[category.id].earned|formatAmount }}</td> + <td data-value="{{ categorySummary[category.id].earned }}" style="text-align: right;">{{ categorySummary[category.id].earned|formatAmount }}</td> {% else %} - <td data-value="0">{{ 0|formatAmount }}</td> + <td data-value="0" style="text-align: right;">{{ 0|formatAmount }}</td> {% endif %} {% if categorySummary[category.id] %} - <td data-value="{{ categorySummary[category.id].spent }}">{{ categorySummary[category.id].spent|formatAmount }}</td> + <td data-value="{{ categorySummary[category.id].spent }}" style="text-align: right;">{{ categorySummary[category.id].spent|formatAmount }}</td> {% else %} - <td data-value="0">{{ 0|formatAmount }}</td> + <td data-value="0" style="text-align: right;">{{ 0|formatAmount }}</td> {% endif %} </tr> {% endfor %} @@ -164,8 +164,8 @@ <thead> <tr> <th data-defaultsign="az">{{ 'account'|_ }}</th> - <th data-defaultsign="_19">{{ 'spent_average'|_ }}</th> - <th data-defaultsign="_19">{{ 'total'|_ }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ 'spent_average'|_ }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ 'total'|_ }}</th> <th data-defaultsign="_19">{{ 'transaction_count'|_ }}</th> </tr> </thead> @@ -179,10 +179,10 @@ <td data-value="{{ row.name }}"> <a href="{{ route('accounts.show', row.id) }}">{{ row.name }}</a> </td> - <td data-value="{{ row.average }}"> + <td data-value="{{ row.average }}" style="text-align: right;"> {{ row.average|formatAmount }} </td> - <td data-value="{{ row.sum }}"> + <td data-value="{{ row.sum }}" style="text-align: right;"> {{ row.sum|formatAmount }} </td> <td data-value="{{ row.count }}"> @@ -219,7 +219,7 @@ <th data-defaultsort="disabled">{{ 'description'|_ }}</th> <th data-defaultsign="month">{{ 'date'|_ }}</th> <th data-defaultsign="az">{{ 'account'|_ }}</th> - <th data-defaultsign="_19">{{ 'amount'|_ }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ 'amount'|_ }}</th> </tr> </thead> <tbody> @@ -246,7 +246,7 @@ {{ row.opposing_account_name }} </a> </td> - <td data-value="{{ row.transaction_amount }}"> + <td data-value="{{ row.transaction_amount }}" style="text-align: right;"> {{ row.transaction_amount|formatAmount }} </td> </tr> diff --git a/resources/views/reports/partials/accounts.twig b/resources/views/reports/partials/accounts.twig index ef3ad787e7..3468f4c1d0 100644 --- a/resources/views/reports/partials/accounts.twig +++ b/resources/views/reports/partials/accounts.twig @@ -2,9 +2,9 @@ <thead> <tr> <th data-defaultsign="az">{{ 'name'|_ }}</th> - <th data-defaultsign="_19" class="hidden-xs">{{ 'balanceStart'|_ }}</th> - <th data-defaultsign="_19" class="hidden-xs">{{ 'balanceEnd'|_ }}</th> - <th data-defaultsign="_19">{{ 'difference'|_ }}</th> + <th data-defaultsign="_19" class="hidden-xs" style="text-align: right;">{{ 'balanceStart'|_ }}</th> + <th data-defaultsign="_19" class="hidden-xs" style="text-align: right;">{{ 'balanceEnd'|_ }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ 'difference'|_ }}</th> </tr> </thead> <tbody> @@ -13,18 +13,18 @@ <td data-value="{{ account.name }}"> <a href="{{ route('accounts.show',account.id) }}" title="{{ account.name }}">{{ account.name }}</a> </td> - <td class="hidden-xs" data-value="{{ account.startBalance }}">{{ account.startBalance|formatAmount }}</td> - <td class="hidden-xs" data-value="{{ account.endBalance }}">{{ account.endBalance|formatAmount }}</td> - <td data-value="{{ (account.endBalance - account.startBalance) }}">{{ (account.endBalance - account.startBalance)|formatAmount }}</td> + <td class="hidden-xs" data-value="{{ account.startBalance }}" style="text-align: right;">{{ account.startBalance|formatAmount }}</td> + <td class="hidden-xs" data-value="{{ account.endBalance }}" style="text-align: right;">{{ account.endBalance|formatAmount }}</td> + <td style="text-align: right;" data-value="{{ (account.endBalance - account.startBalance) }}">{{ (account.endBalance - account.startBalance)|formatAmount }}</td> </tr> {% endfor %} </tbody> <tfoot> <tr> <td><em>{{ 'sumOfSums'|_ }}</em></td> - <td class="hidden-xs">{{ accountReport.getStart|formatAmount }}</td> - <td class="hidden-xs">{{ accountReport.getEnd|formatAmount }}</td> - <td>{{ accountReport.getDifference|formatAmount }}</td> + <td class="hidden-xs" style="text-align: right;">{{ accountReport.getStart|formatAmount }}</td> + <td class="hidden-xs" style="text-align: right;">{{ accountReport.getEnd|formatAmount }}</td> + <td style="text-align: right;">{{ accountReport.getDifference|formatAmount }}</td> </tr> </tfoot> </table> diff --git a/resources/views/reports/partials/balance.twig b/resources/views/reports/partials/balance.twig index 58fdf45cfc..c2c6e1a323 100644 --- a/resources/views/reports/partials/balance.twig +++ b/resources/views/reports/partials/balance.twig @@ -4,9 +4,9 @@ <tr> <th colspan="2">{{ 'budgets'|_ }}</th> {% for account in balance.getBalanceHeader.getAccounts %} - <th class="hidden-xs"><a href="{{ route('accounts.show',account.id) }}">{{ account.name }}</a></th> + <th class="hidden-xs" style="text-align: right;"><a href="{{ route('accounts.show',account.id) }}">{{ account.name }}</a></th> {% endfor %} - <th> + <th style="text-align: right;"> {{ 'leftInBudget'|_ }} </th> </tr> @@ -28,7 +28,7 @@ </span> {% endif %} </td> - <td> + <td style="text-align: right;"> {% if(balanceLine.getBudget.amount) %} {{ balanceLine.getBudget.amount|formatAmount }} {% else %} @@ -40,7 +40,7 @@ {% endif %} {% for balanceEntry in balanceLine.getBalanceEntries %} - <td class="hidden-xs"> + <td class="hidden-xs" style="text-align: right;"> {% if balanceEntry.getSpent != 0 %} <span class="text-danger">{{ (balanceEntry.getSpent)|formatAmountPlain }}</span> <i class="fa fa-fw text-muted fa-info-circle firefly-info-button" data-location="balance-amount" @@ -49,11 +49,11 @@ {% endif %} {% if balanceEntry.getLeft != 0 %} - <span class="text-success">{{ (balanceEntry.getLeft)|formatAmountPlain }}</span> + <span class="text-success" style="text-align: right;">{{ (balanceEntry.getLeft)|formatAmountPlain }}</span> {% endif %} </td> {% endfor %} - <td> + <td style="text-align: right;"> {{ balanceLine.leftOfRepetition|formatAmount }} </td> </tr> diff --git a/resources/views/reports/partials/bills.twig b/resources/views/reports/partials/bills.twig index fe234655ef..05f7f02839 100644 --- a/resources/views/reports/partials/bills.twig +++ b/resources/views/reports/partials/bills.twig @@ -7,10 +7,10 @@ <thead> <tr> <th data-defaultsign="az">{{ trans('form.name') }}</th> - <th data-defaultsign="_19" class="hidden-xs">{{ trans('form.amount_min') }}</th> - <th data-defaultsign="_19" class="hidden-xs">{{ trans('form.amount_max') }}</th> - <th data-defaultsign="_19">{{ trans('form.amount') }}</th> - <th data-defaultsign="_19">{{ trans('form.under') }}</th> + <th data-defaultsign="_19" class="hidden-xs" style="text-align: right;">{{ trans('form.amount_min') }}</th> + <th data-defaultsign="_19" class="hidden-xs" style="text-align: right;">{{ trans('form.amount_max') }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ trans('form.amount') }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ trans('form.under') }}</th> </tr> </thead> <tbody> @@ -19,10 +19,10 @@ <td data-value="{{ line.getBill.name }}"> <a href="{{ route('bills.show',line.getBill.id) }}">{{ line.getBill.name }}</a> </td> - <td class="hidden-xs" data-value="{{ line.getMin }}">{{ line.getMin|formatAmount }}</td> - <td class="hidden-xs" data-value="{{ line.getMax }}">{{ line.getMax|formatAmount }}</td> + <td class="hidden-xs" data-value="{{ line.getMin }}" style="text-align: right;">{{ line.getMin|formatAmount }}</td> + <td class="hidden-xs" data-value="{{ line.getMax }}" style="text-align: right;">{{ line.getMax|formatAmount }}</td> {% if line.isHit %} - <td data-value="{{ line.getAmount }}"> + <td data-value="{{ line.getAmount }}" style="text-align: right;"> <a href="{{ route('transactions.show', line.getTransactionJournalId) }}"> {{ line.getAmount|formatAmount }} </a> @@ -34,7 +34,7 @@ {% if not line.isActive %} <td data-value="-1"> </td> {% endif %} - <td data-value="{{ (line.getMax - line.getAmount) }}"> + <td data-value="{{ (line.getMax - line.getAmount) }}" style="text-align: right;"> {% if line.isActive %} {{ (line.getMax + line.getAmount)|formatAmount }} {% endif %} diff --git a/resources/views/reports/partials/budget-period.twig b/resources/views/reports/partials/budget-period.twig index 86d5f98aed..e5586d17b0 100644 --- a/resources/views/reports/partials/budget-period.twig +++ b/resources/views/reports/partials/budget-period.twig @@ -3,9 +3,9 @@ <tr> <th data-defaultsign="az" colspan="2">{{ 'budget'|_ }}</th> {% for period in periods %} - <th data-defaultsign="_19">{{ period }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ period }}</th> {% endfor %} - <th data-defaultsign="_19">{{ 'sum'|_ }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ 'sum'|_ }}</th> </tr> </thead> <tbody> @@ -19,17 +19,17 @@ </td> {% for key, period in periods %} {% if(info.entries[key]) %} - <td data-value="{{ info.entries[key] }}"> + <td data-value="{{ info.entries[key] }}" style="text-align: right;"> {{ info.entries[key]|formatAmount }} </td> {% else %} - <td data-value="0"> + <td data-value="0" style="text-align: right;"> {{ 0|formatAmount }} </td> {% endif %} {% endfor %} - <td data-value="{{ info.sum }}"> + <td data-value="{{ info.sum }}" style="text-align: right;"> {{ info.sum|formatAmount }} </td> </tr> diff --git a/resources/views/reports/partials/budgets.twig b/resources/views/reports/partials/budgets.twig index 9deece67bb..be4b938c4b 100644 --- a/resources/views/reports/partials/budgets.twig +++ b/resources/views/reports/partials/budgets.twig @@ -3,10 +3,11 @@ <tr> <th data-defaultsign="az">{{ 'budget'|_ }}</th> <th data-defaultsign="month" class="hidden-xs">{{ 'date'|_ }}</th> - <th data-defaultsign="_19">{{ 'budgeted'|_ }}</th> - <th data-defaultsign="_19">{{ 'spent'|_ }}</th> - <th data-defaultsign="_19">{{ 'left'|_ }}</th> - <th data-defaultsign="_19">{{ 'overspent'|_ }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ 'budgeted'|_ }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ 'spent'|_ }}</th> + <th data-defaultsort="disabled"> </th> + <th data-defaultsign="_19" style="text-align: right;">{{ 'left'|_ }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ 'overspent'|_ }}</th> </tr> </thead> <tbody> @@ -40,33 +41,41 @@ {% if budgetLine.getBudgetLimit.id %} - <td data-value="{{ budgetLine.getBudgetLimit.amount }}"> + <td data-value="{{ budgetLine.getBudgetLimit.amount }}" style="text-align: right;"> {{ budgetLine.getBudgetLimit.amount|formatAmount }} </td> {% else %} - <td data-value="0"> + <td data-value="0" style="text-align: right;"> {{ 0|formatAmount }} </td> {% endif %} - <td data-value="{{ budgetLine.getSpent }}"> + <td data-value="{{ budgetLine.getSpent }}" style="text-align: right;"> {% if budgetLine.getSpent != 0 %} {{ budgetLine.getSpent|formatAmount }} - <i class="fa fa-fw text-muted fa-info-circle firefly-info-button" + <!-- <i class="fa fa-fw text-muted fa-info-circle firefly-info-button" data-location="budget-spent-amount" data-budget-id="{{ budgetLine.getBudget.id }}"></i> + --> {% endif %} {% if budgetLine.getSpent == 0 %} {{ budgetLine.getSpent|formatAmount }} {% endif %} - </td> - <td data-value="{{ budgetLine.getLeft }}"> + <td> + {% if budgetLine.getSpent != 0 %} + <i class="fa fa-fw text-muted fa-info-circle firefly-info-button" + data-location="budget-spent-amount" data-budget-id="{{ budgetLine.getBudget.id }}"></i> + + {% endif %} + </td> + + <td data-value="{{ budgetLine.getLeft }}" style="text-align: right;"> {% if(budgetLine.getOverspent == 0) %} {{ budgetLine.getLeft|formatAmount }} {% endif %} </td> - <td data-value="{{ budgetLine.getOverspent }}"> + <td data-value="{{ budgetLine.getOverspent }}" style="text-align: right;"> {% if budgetLine.getOverspent != 0 %} {{ budgetLine.getOverspent|formatAmount }} {% endif %} @@ -78,8 +87,8 @@ <tr> <td><em>{{ 'sum'|_ }}</em></td> <td class="hidden-xs"> </td> - <td>{{ budgets.getBudgeted|formatAmount }}</td> - <td> + <td style="text-align: right;">{{ budgets.getBudgeted|formatAmount }}</td> + <td style="text-align: right;"> {% if budgets.getSpent != 0 %} <span class="text-danger">{{ budgets.getSpent|formatAmountPlain }}</span> {% endif %} @@ -87,8 +96,9 @@ {{ budgets.getSpent|formatAmount }} {% endif %} </td> - <td>{{ budgets.getLeft|formatAmount }}</td> - <td><span class="text-danger">{{ budgets.getOverspent|formatAmountPlain }}</span></td> + <td> </td> + <td style="text-align: right;">{{ budgets.getLeft|formatAmount }}</td> + <td style="text-align: right;"><span class="text-danger">{{ budgets.getOverspent|formatAmountPlain }}</span></td> </tr> </tfoot> </table> diff --git a/resources/views/reports/partials/categories.twig b/resources/views/reports/partials/categories.twig index d5bfcb4df8..eec4ac0843 100644 --- a/resources/views/reports/partials/categories.twig +++ b/resources/views/reports/partials/categories.twig @@ -2,7 +2,8 @@ <thead> <tr> <th>{{ 'category'|_ }}</th> - <th colspan="2">{{ 'spent'|_ }}</th> + <th style="text-align: right;">{{ 'spent'|_ }}</th> + <th> </th> </tr> </thead> <tbody> @@ -17,7 +18,7 @@ <td> <a href="{{ route('categories.show',id) }}">{{ category.name }}</a> </td> - <td>{{ category.spent|formatAmount }}</td> + <td style="text-align: right;">{{ category.spent|formatAmount }}</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="{{ id }}" @@ -37,7 +38,8 @@ <tr> <td><em>{{ 'sum'|_ }}</em></td> - <td>{{ sum|formatAmount }}</td> + <td style="text-align: right;">{{ sum|formatAmount }}</td> + <td> </td> </tr> </tfoot> </table> diff --git a/resources/views/reports/partials/category-period.twig b/resources/views/reports/partials/category-period.twig index d24e0ec09f..8228327b64 100644 --- a/resources/views/reports/partials/category-period.twig +++ b/resources/views/reports/partials/category-period.twig @@ -3,9 +3,9 @@ <tr> <th data-defaultsign="az" colspan="2">{{ 'category'|_ }}</th> {% for period in periods %} - <th data-defaultsign="_19">{{ period }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ period }}</th> {% endfor %} - <th data-defaultsign="_19">{{ 'sum'|_ }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ 'sum'|_ }}</th> </tr> </thead> <tbody> @@ -20,11 +20,11 @@ {% for key, period in periods %} {# income first #} {% if(info.entries[key]) %} - <td data-value="{{ info.entries[key] }}"> + <td data-value="{{ info.entries[key] }}" style="text-align: right;"> {{ info.entries[key]|formatAmount }} </td> {% else %} - <td data-value="0"> + <td data-value="0" style="text-align: right;"> {{ 0|formatAmount }} </td> {% endif %} @@ -32,11 +32,11 @@ {# if sum of income, display: #} {% if info.sum %} - <td data-value="{{ info.sum }}"> + <td data-value="{{ info.sum }}" style="text-align: right;"> {{ info.sum|formatAmount }} </td> {% else %} - <td data-value="0"> + <td data-value="0" style="text-align: right;"> {{ 0|formatAmount }} </td> {% endif %} diff --git a/resources/views/reports/partials/income-expenses.twig b/resources/views/reports/partials/income-expenses.twig index c70f60b0d7..3dcb0f0614 100644 --- a/resources/views/reports/partials/income-expenses.twig +++ b/resources/views/reports/partials/income-expenses.twig @@ -2,8 +2,8 @@ <thead> <tr> <th data-defaultsign="az">{{ 'name'|_ }}</th> - <th data-defaultsign="_19">{{ 'total'|_ }}</th> - <th data-defaultsign="_19" class="hidden-xs">{{ 'average'|_ }}</th> + <th data-defaultsign="_19" style="text-align: right;">{{ 'total'|_ }}</th> + <th data-defaultsign="_19" class="hidden-xs" style="text-align: right;">{{ 'average'|_ }}</th> <th data-defaultsort="disabled"></th> </tr> </thead> @@ -25,10 +25,10 @@ </small> {% endif %} </td> - <td data-value="{{ entry.sum }}"> + <td data-value="{{ entry.sum }}" style="text-align: right;"> {{ (entry.sum)|formatAmount }} </td> - <td class="hidden-xs" data-value="{{ entry.average }}"> + <td class="hidden-xs" data-value="{{ entry.average }}" style="text-align: right;"> {% if entry.count > 1 %} {{ entry.average|formatAmount }} {% else %} @@ -52,7 +52,7 @@ {% endif %} <tr> <td><em>{{ 'sum'|_ }}</em></td> - <td>{{ (sum)|formatAmount }}</td> + <td style="text-align: right;">{{ (sum)|formatAmount }}</td> </tr> </tfoot> </table> diff --git a/resources/views/reports/partials/journals-audit-tasker.twig b/resources/views/reports/partials/journals-audit-tasker.twig index 824dd126bc..0c9fd25adc 100644 --- a/resources/views/reports/partials/journals-audit-tasker.twig +++ b/resources/views/reports/partials/journals-audit-tasker.twig @@ -7,9 +7,9 @@ <th class="hide-icon"> </th> <th class="hide-description">{{ trans('list.description') }}</th> - <th class="hide-balance_before">{{ trans('list.balance_before') }}</th> - <th class="hide-amount">{{ trans('list.amount') }}</th> - <th class="hide-balance_after">{{ trans('list.balance_after') }}</th> + <th class="hide-balance_before" style="text-align: right;">{{ trans('list.balance_before') }}</th> + <th class="hide-amount" style="text-align: right;">{{ trans('list.amount') }}</th> + <th class="hide-balance_after" style="text-align: right;">{{ trans('list.balance_after') }}</th> <th class="hide-date">{{ trans('list.date') }}</th> <th class="hide-book_date">{{ trans('list.book_date') }}</th> @@ -57,14 +57,14 @@ {% endif %} </a> </td> - <td class="hide-balance_before">{{ transaction.before|formatAmount }}</td> - <td class="hide-amount"> + <td class="hide-balance_before" style="text-align: right;">{{ transaction.before|formatAmount }}</td> + <td class="hide-amount" style="text-align: right;"> <!-- format amount of transaction --> {{ formatByCode(transaction.transaction_currency_code, transaction.transaction_amount) }} <!-- and then amount of journal itself. --> {{ optionalJournalAmount(transaction.journal_id, transaction.transaction_amount, transaction.transaction_currency_code, transaction.transaction_type_type) }} </td> - <td class="hide-balance_after">{{ transaction.after|formatAmount }}</td> + <td class="hide-balance_after" style="text-align: right;">{{ transaction.after|formatAmount }}</td> <td class="hide-date">{{ transaction.date.formatLocalized(monthAndDayFormat) }}</td> <td class="hide-book_date"> diff --git a/resources/views/reports/partials/operations.twig b/resources/views/reports/partials/operations.twig index 12fb1928f7..60ee342bb9 100644 --- a/resources/views/reports/partials/operations.twig +++ b/resources/views/reports/partials/operations.twig @@ -1,14 +1,14 @@ <table class="table table-hover"> <tr> <td>{{ 'in'|_ }}</td> - <td>{{ incomeSum|formatAmount }}</td> + <td style="text-align: right;">{{ incomeSum|formatAmount }}</td> </tr> <tr> <td>{{ 'out'|_ }}</td> - <td>{{ expensesSum|formatAmount }}</td> + <td style="text-align: right;">{{ expensesSum|formatAmount }}</td> </tr> <tr> <td>{{ 'difference'|_ }}</td> - <td>{{ (incomeSum + expensesSum)|formatAmount }}</td> + <td style="text-align: right;">{{ (incomeSum + expensesSum)|formatAmount }}</td> </tr> </table> From 796be319b7910b34ceff169cdf2fe1be20c07c68 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Tue, 3 Jan 2017 08:27:48 +0100 Subject: [PATCH 487/709] Piggy banks for #511 [skip ci] --- resources/views/list/piggy-banks.twig | 22 +++++++++++++++++----- resources/views/piggy-banks/index.twig | 22 +++++++++++----------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/resources/views/list/piggy-banks.twig b/resources/views/list/piggy-banks.twig index 6fe3af9bff..7fa3188f14 100644 --- a/resources/views/list/piggy-banks.twig +++ b/resources/views/list/piggy-banks.twig @@ -1,4 +1,14 @@ <table class="table table-hover table-condensed" id="sortable-piggy"> + <thead> + <tr> + <th colspan="2"> </th> + <th>{{ 'piggy_bank'|_ }}</th> + <th style="text-align: right;">{{ 'saved_so_far'|_ }}</th> + <th colspan="3"> </th> + <th style="text-align: right;">{{ 'target_amount'|_ }}</th> + <th style="text-align: right;">{{ 'left_to_save'|_ }}</th> + </tr> + </thead> <tbody> {% for piggyBank in piggyBanks %} <tr data-id="{{ piggyBank.id }}"> @@ -21,8 +31,8 @@ <td> <a href="{{ route('piggy-banks.show', piggyBank.id) }}" title="{{ piggyBank.order }}">{{ piggyBank.name }}</a> </td> - <td> - <span title="Saved so far">{{ piggyBank.savedSoFar|formatAmountPlain }}</span> + <td style="text-align: right;"> + <span title="Saved so far" style="text-align:right;">{{ piggyBank.savedSoFar|formatAmount }}</span> </td> <td class="hidden-sm hidden-xs" style="text-align:right;width:40px;"> {% if piggyBank.savedSoFar > 0 %} @@ -55,10 +65,12 @@ <i data-id="{{ piggyBank.id }}" class="fa fa-plus"></i></a> {% endif %} </td> - <td class="hidden-sm hidden-xs" style="width:200px;"> - <span title="Target amount">{{ piggyBank.targetamount|formatAmount }}</span> + <td class="hidden-sm hidden-xs" style="width:200px;text-align:right;"> + <span title="{{ 'target_amount'|_ }}">{{ piggyBank.targetamount|formatAmount }}</span> + </td> + <td class="hidden-sm hidden-xs" style="width:200px;text-align:right;"> {% if piggyBank.leftToSave > 0 %} - <span title="Left to save">({{ piggyBank.leftToSave|formatAmount }})</span> + <span title="{{ 'left_to_save'|_ }}">{{ piggyBank.leftToSave|formatAmount }}</span> {% endif %} </td> </tr> diff --git a/resources/views/piggy-banks/index.twig b/resources/views/piggy-banks/index.twig index 364ec37f0e..821b731c9a 100644 --- a/resources/views/piggy-banks/index.twig +++ b/resources/views/piggy-banks/index.twig @@ -11,7 +11,7 @@ <div class="box-header with-border"> <h3 class="box-title">{{ 'piggyBanks'|_ }}</h3> </div> - <div class="box-body"> + <div class="box-body no-padding"> {% include 'list/piggy-banks' %} </div> </div> @@ -29,22 +29,22 @@ <thead> <tr> <th>{{ 'account'|_ }}</th> - <th class="hidden-sm hidden-xs">{{ 'balance'|_ }}</th> - <th>{{ 'left_for_piggy_banks'|_ }}</th> - <th class="hidden-sm hidden-xs">{{ 'sum_of_piggy_banks'|_ }}</th> - <th class="hidden-sm hidden-xs">{{ 'saved_so_far'|_ }}</th> - <th class="hidden-sm hidden-xs">{{ 'left_to_save'|_ }}</th> + <th style="text-align:right;" class="hidden-sm hidden-xs">{{ 'balance'|_ }}</th> + <th style="text-align:right;">{{ 'left_for_piggy_banks'|_ }}</th> + <th style="text-align:right;" class="hidden-sm hidden-xs">{{ 'sum_of_piggy_banks'|_ }}</th> + <th style="text-align:right;" class="hidden-sm hidden-xs">{{ 'saved_so_far'|_ }}</th> + <th style="text-align:right;" class="hidden-sm hidden-xs">{{ 'left_to_save'|_ }}</th> </tr> </thead> <tbody> {% for id,info in accounts %} <tr> <td><a href="{{ route('accounts.show',id) }}" title="{{ info.name }}">{{ info.name }}</a></td> - <td class="hidden-sm hidden-xs">{{ info.balance|formatAmount }}</td> - <td>{{ info.leftForPiggyBanks|formatAmount }}</td> - <td class="hidden-sm hidden-xs">{{ info.sumOfTargets|formatAmount }}</td> - <td class="hidden-sm hidden-xs">{{ info.sumOfSaved|formatAmount }}</td> - <td class="hidden-sm hidden-xs">{{ info.leftToSave|formatAmount }}</td> + <td style="text-align:right;" class="hidden-sm hidden-xs">{{ info.balance|formatAmount }}</td> + <td style="text-align:right;">{{ info.leftForPiggyBanks|formatAmount }}</td> + <td style="text-align:right;" class="hidden-sm hidden-xs">{{ info.sumOfTargets|formatAmount }}</td> + <td style="text-align:right;" class="hidden-sm hidden-xs">{{ info.sumOfSaved|formatAmount }}</td> + <td style="text-align:right;" class="hidden-sm hidden-xs">{{ info.leftToSave|formatAmount }}</td> </tr> {% endfor %} </tbody> From c34fb7f03772db58fa8e8e17f909e1a88070ad0f Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Tue, 3 Jan 2017 17:02:17 +0100 Subject: [PATCH 488/709] Cleaned up the category and budget pie charts. --- app/Generator/Report/Support.php | 38 +-- app/Helpers/Chart/MetaPieChart.php | 278 ++++++++++++++++++ app/Helpers/Chart/MetaPieChartInterface.php | 82 ++++++ .../Chart/BudgetReportController.php | 108 ++----- .../Chart/CategoryReportController.php | 208 +++---------- app/Providers/FireflyServiceProvider.php | 3 + resources/views/reports/budget/month.twig | 4 +- resources/views/reports/category/month.twig | 8 +- 8 files changed, 447 insertions(+), 282 deletions(-) create mode 100644 app/Helpers/Chart/MetaPieChart.php create mode 100644 app/Helpers/Chart/MetaPieChartInterface.php diff --git a/app/Generator/Report/Support.php b/app/Generator/Report/Support.php index 1f71ad182a..573129ef97 100644 --- a/app/Generator/Report/Support.php +++ b/app/Generator/Report/Support.php @@ -34,27 +34,7 @@ class Support */ public static function filterExpenses(Collection $collection, array $accounts): Collection { - $result = $collection->filter( - function (Transaction $transaction) use ($accounts) { - $opposing = $transaction->opposing_account_id; - // remove internal transfer - if (in_array($opposing, $accounts)) { - Log::debug(sprintf('Filtered #%d because its opposite is in accounts.', $transaction->id)); - - return null; - } - // remove positive amount - if (bccomp($transaction->transaction_amount, '0') === 1) { - Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount)); - - return null; - } - - return $transaction; - } - ); - - return $result; + return self::filterTransactions($collection, $accounts, 1); } /** @@ -64,9 +44,21 @@ class Support * @return Collection */ public static function filterIncome(Collection $collection, array $accounts): Collection + { + return self::filterTransactions($collection, $accounts, -1); + } + + /** + * @param Collection $collection + * @param array $accounts + * @param int $modifier + * + * @return Collection + */ + public static function filterTransactions(Collection $collection, array $accounts, int $modifier): Collection { $result = $collection->filter( - function (Transaction $transaction) use ($accounts) { + function (Transaction $transaction) use ($accounts, $modifier) { $opposing = $transaction->opposing_account_id; // remove internal transfer if (in_array($opposing, $accounts)) { @@ -75,7 +67,7 @@ class Support return null; } // remove positive amount - if (bccomp($transaction->transaction_amount, '0') === -1) { + if (bccomp($transaction->transaction_amount, '0') === $modifier) { Log::debug(sprintf('Filtered #%d because amount is %f.', $transaction->id, $transaction->transaction_amount)); return null; diff --git a/app/Helpers/Chart/MetaPieChart.php b/app/Helpers/Chart/MetaPieChart.php new file mode 100644 index 0000000000..fbeb70cc25 --- /dev/null +++ b/app/Helpers/Chart/MetaPieChart.php @@ -0,0 +1,278 @@ +<?php +/** + * MetaPieChart.php + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +declare(strict_types = 1); + +namespace FireflyIII\Helpers\Chart; + +use Carbon\Carbon; +use FireflyIII\Generator\Report\Support; +use FireflyIII\Helpers\Collector\JournalCollectorInterface; +use FireflyIII\Models\Transaction; +use FireflyIII\Models\TransactionType; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\User; +use Illuminate\Support\Collection; + +/** + * Class MetaPieChart + * + * @package FireflyIII\Helpers\Chart + */ +class MetaPieChart implements MetaPieChartInterface +{ + /** @var Collection */ + protected $accounts; + /** @var Collection */ + protected $budgets; + /** @var Collection */ + protected $categories; + /** @var bool */ + protected $collectOtherObjects = false; + /** @var Carbon */ + protected $end; + /** @var array */ + protected $grouping + = [ + 'account' => ['opposing_account_id'], + 'budget' => ['transaction_journal_budget_id', 'transaction_budget_id'], + 'category' => ['transaction_journal_category_id', 'transaction_category_id'], + ]; + + /** @var array */ + protected $repositories + = [ + 'account' => AccountRepositoryInterface::class, + 'budget' => BudgetRepositoryInterface::class, + 'category' => CategoryRepositoryInterface::class, + ]; + + + /** @var Carbon */ + protected $start; + /** @var string */ + protected $total = '0'; + /** @var User */ + protected $user; + + public function __construct() + { + $this->accounts = new Collection; + $this->budgets = new Collection; + $this->categories = new Collection; + } + + /** + * @param string $direction + * @param string $group + * + * @return array + */ + public function generate(string $direction, string $group): array + { + $transactions = $this->getTransactions($direction); + $grouped = $this->groupByFields($transactions, $this->grouping[$group]); + $chartData = $this->organizeByType($group, $grouped); + + // also collect all other transactions + if ($this->collectOtherObjects && $direction === 'expense') { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class, [$this->user]); + $collector->setAccounts($this->accounts)->setRange($this->start, $this->end)->setTypes([TransactionType::WITHDRAWAL]); + $journals = $collector->getJournals(); + $sum = strval($journals->sum('transaction_amount')); + $sum = bcmul($sum, '-1'); + $sum = bcsub($sum, $this->total); + $chartData[strval(trans('firefly.everything_else'))] = $sum; + } + + if ($this->collectOtherObjects && $direction === 'income') { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector->setAccounts($this->accounts)->setRange($this->start, $this->end)->setTypes([TransactionType::DEPOSIT]); + $journals = $collector->getJournals(); + $sum = strval($journals->sum('transaction_amount')); + $sum = bcsub($sum, $this->total); + $chartData[strval(trans('firefly.everything_else'))] = $sum; + } + + return $chartData; + + } + + /** + * @param Collection $accounts + * + * @return MetaPieChartInterface + */ + public function setAccounts(Collection $accounts): MetaPieChartInterface + { + $this->accounts = $accounts; + + return $this; + } + + /** + * @param Collection $budgets + * + * @return MetaPieChartInterface + */ + public function setBudgets(Collection $budgets): MetaPieChartInterface + { + $this->budgets = $budgets; + + return $this; + } + + /** + * @param Collection $categories + * + * @return MetaPieChartInterface + */ + public function setCategories(Collection $categories): MetaPieChartInterface + { + $this->categories = $categories; + + return $this; + } + + /** + * @param bool $collectOtherObjects + * + * @return MetaPieChartInterface + */ + public function setCollectOtherObjects(bool $collectOtherObjects): MetaPieChartInterface + { + $this->collectOtherObjects = $collectOtherObjects; + + return $this; + } + + /** + * @param Carbon $end + * + * @return MetaPieChartInterface + */ + public function setEnd(Carbon $end): MetaPieChartInterface + { + $this->end = $end; + + return $this; + } + + /** + * @param Carbon $start + * + * @return MetaPieChartInterface + */ + public function setStart(Carbon $start): MetaPieChartInterface + { + $this->start = $start; + + return $this; + } + + /** + * @param User $user + * + * @return MetaPieChartInterface + */ + public function setUser(User $user): MetaPieChartInterface + { + $this->user = $user; + + return $this; + } + + protected function getTransactions(string $direction) + { + $types = [TransactionType::DEPOSIT, TransactionType::TRANSFER]; + $modifier = -1; + if ($direction === 'expense') { + $types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]; + $modifier = 1; + } + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector->setAccounts($this->accounts); + $collector->setRange($this->start, $this->end); + $collector->setTypes($types); + $collector->withOpposingAccount(); + + if ($direction === 'income') { + $collector->disableFilter(); + } + + if ($this->budgets->count() > 0) { + $collector->setBudgets($this->budgets); + } + if ($this->categories->count() > 0) { + $collector->setCategories($this->categories); + } + + $accountIds = $this->accounts->pluck('id')->toArray(); + $transactions = $collector->getJournals(); + $set = Support::filterTransactions($transactions, $accountIds, $modifier); + + return $set; + } + + /** + * @param Collection $set + * @param array $fields + * + * @return array + */ + protected function groupByFields(Collection $set, array $fields) + { + $grouped = []; + /** @var Transaction $transaction */ + foreach ($set as $transaction) { + $values = []; + foreach ($fields as $field) { + $values[] = intval($transaction->$field); + } + $value = max($values); + $grouped[$value] = $grouped[$value] ?? '0'; + $grouped[$value] = bcadd($transaction->transaction_amount, $grouped[$value]); + } + + return $grouped; + } + + /** + * @param string $type + * @param array $array + * + * @return array + */ + protected function organizeByType(string $type, array $array): array + { + $chartData = []; + $names = []; + $repository = app($this->repositories[$type], [$this->user]); + foreach ($array as $objectId => $amount) { + if (!isset($names[$objectId])) { + $object = $repository->find(intval($objectId)); + $names[$objectId] = $object->name; + } + if (bccomp($amount, '0') === -1) { + $amount = bcmul($amount, '-1'); + } + + $this->total = bcadd($this->total, $amount); + $chartData[$names[$objectId]] = $amount; + } + + return $chartData; + + } +} \ No newline at end of file diff --git a/app/Helpers/Chart/MetaPieChartInterface.php b/app/Helpers/Chart/MetaPieChartInterface.php new file mode 100644 index 0000000000..a466db8a38 --- /dev/null +++ b/app/Helpers/Chart/MetaPieChartInterface.php @@ -0,0 +1,82 @@ +<?php +/** + * MetaPieChartInterface.php + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +declare(strict_types = 1); + +namespace FireflyIII\Helpers\Chart; + +use Carbon\Carbon; +use FireflyIII\User; +use Illuminate\Support\Collection; + +/** + * Interface MetaPieChartInterface + * + * @package FireflyIII\Helpers\Chart + */ +interface MetaPieChartInterface +{ + /** + * @param string $direction + * @param string $group + * + * @return array + */ + public function generate(string $direction, string $group): array; + + /** + * @param Collection $accounts + * + * @return MetaPieChartInterface + */ + public function setAccounts(Collection $accounts): MetaPieChartInterface; + + /** + * @param Collection $budgets + * + * @return MetaPieChartInterface + */ + public function setBudgets(Collection $budgets): MetaPieChartInterface; + + /** + * @param Collection $categories + * + * @return MetaPieChartInterface + */ + public function setCategories(Collection $categories): MetaPieChartInterface; + + /** + * @param bool $collectOtherObjects + * + * @return MetaPieChartInterface + */ + public function setCollectOtherObjects(bool $collectOtherObjects): MetaPieChartInterface; + + /** + * @param Carbon $end + * + * @return MetaPieChartInterface + */ + public function setEnd(Carbon $end): MetaPieChartInterface; + + /** + * @param Carbon $start + * + * @return MetaPieChartInterface + */ + public function setStart(Carbon $start): MetaPieChartInterface; + + /** + * @param User $user + * + * @return MetaPieChartInterface + */ + public function setUser(User $user): MetaPieChartInterface; + +} \ No newline at end of file diff --git a/app/Http/Controllers/Chart/BudgetReportController.php b/app/Http/Controllers/Chart/BudgetReportController.php index 70070ee2f3..d131f8a661 100644 --- a/app/Http/Controllers/Chart/BudgetReportController.php +++ b/app/Http/Controllers/Chart/BudgetReportController.php @@ -17,6 +17,7 @@ namespace FireflyIII\Http\Controllers\Chart; use Carbon\Carbon; use FireflyIII\Generator\Chart\Basic\GeneratorInterface; use FireflyIII\Generator\Report\Category\MonthReportGenerator; +use FireflyIII\Helpers\Chart\MetaPieChartInterface; use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Budget; @@ -75,51 +76,19 @@ class BudgetReportController extends Controller */ public function accountExpense(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end, string $others) { - /** @var bool $others */ - $others = intval($others) === 1; - $cache = new CacheProperties; - $cache->addProperty('chart.budget.report.account-expense'); - $cache->addProperty($accounts); - $cache->addProperty($budgets); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty($others); - if ($cache->has()) { - return Response::json($cache->get()); - } - - $names = []; - $set = $this->getExpenses($accounts, $budgets, $start, $end); - $grouped = $this->groupByOpposingAccount($set); - $chartData = []; - $total = '0'; - - foreach ($grouped as $accountId => $amount) { - if (!isset($names[$accountId])) { - $account = $this->accountRepository->find(intval($accountId)); - $names[$accountId] = $account->name; - } - $amount = bcmul($amount, '-1'); - $total = bcadd($total, $amount); - $chartData[$names[$accountId]] = $amount; - } - - // also collect all transactions NOT in these budgets. - if ($others) { - /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); - $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]); - $journals = $collector->getJournals(); - $sum = strval($journals->sum('transaction_amount')); - $sum = bcmul($sum, '-1'); - $sum = bcsub($sum, $total); - $chartData[strval(trans('firefly.everything_else'))] = $sum; - } - - $data = $this->generator->pieChart($chartData); - $cache->store($data); + /** @var MetaPieChartInterface $helper */ + $helper = app(MetaPieChartInterface::class); + $helper->setAccounts($accounts); + $helper->setBudgets($budgets); + $helper->setUser(auth()->user()); + $helper->setStart($start); + $helper->setEnd($end); + $helper->setCollectOtherObjects(intval($others) === 1); + $chartData = $helper->generate('expense', 'account'); + $data = $this->generator->pieChart($chartData); return Response::json($data); + } /** @@ -133,49 +102,16 @@ class BudgetReportController extends Controller */ public function budgetExpense(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end, string $others) { - /** @var bool $others */ - $others = intval($others) === 1; - $cache = new CacheProperties; - $cache->addProperty('chart.budget.report.budget-expense'); - $cache->addProperty($accounts); - $cache->addProperty($budgets); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty($others); - if ($cache->has()) { - return Response::json($cache->get()); - } - - $names = []; - $set = $this->getExpenses($accounts, $budgets, $start, $end); - $grouped = $this->groupByBudget($set); - $total = '0'; - $chartData = []; - - foreach ($grouped as $budgetId => $amount) { - if (!isset($names[$budgetId])) { - $budget = $this->budgetRepository->find(intval($budgetId)); - $names[$budgetId] = $budget->name; - } - $amount = bcmul($amount, '-1'); - $total = bcadd($total, $amount); - $chartData[$names[$budgetId]] = $amount; - } - - // also collect all transactions NOT in these budgets. - if ($others) { - /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); - $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]); - $journals = $collector->getJournals(); - $sum = strval($journals->sum('transaction_amount')); - $sum = bcmul($sum, '-1'); - $sum = bcsub($sum, $total); - $chartData[strval(trans('firefly.everything_else'))] = $sum; - } - - $data = $this->generator->pieChart($chartData); - $cache->store($data); + /** @var MetaPieChartInterface $helper */ + $helper = app(MetaPieChartInterface::class); + $helper->setAccounts($accounts); + $helper->setBudgets($budgets); + $helper->setUser(auth()->user()); + $helper->setStart($start); + $helper->setEnd($end); + $helper->setCollectOtherObjects(intval($others) === 1); + $chartData = $helper->generate('expense', 'budget'); + $data = $this->generator->pieChart($chartData); return Response::json($data); } diff --git a/app/Http/Controllers/Chart/CategoryReportController.php b/app/Http/Controllers/Chart/CategoryReportController.php index fb66e25a1d..e156becd7e 100644 --- a/app/Http/Controllers/Chart/CategoryReportController.php +++ b/app/Http/Controllers/Chart/CategoryReportController.php @@ -17,6 +17,7 @@ namespace FireflyIII\Http\Controllers\Chart; use Carbon\Carbon; use FireflyIII\Generator\Chart\Basic\GeneratorInterface; use FireflyIII\Generator\Report\Category\MonthReportGenerator; +use FireflyIII\Helpers\Chart\MetaPieChartInterface; use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Category; @@ -75,49 +76,16 @@ class CategoryReportController extends Controller */ public function accountExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others) { - /** @var bool $others */ - $others = intval($others) === 1; - $cache = new CacheProperties; - $cache->addProperty('chart.category.report.account-expense'); - $cache->addProperty($accounts); - $cache->addProperty($categories); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty($others); - if ($cache->has()) { - return Response::json($cache->get()); - } - - $names = []; - $set = $this->getExpenses($accounts, $categories, $start, $end); - $grouped = $this->groupByOpposingAccount($set); - $chartData = []; - $total = '0'; - - foreach ($grouped as $accountId => $amount) { - if (!isset($names[$accountId])) { - $account = $this->accountRepository->find(intval($accountId)); - $names[$accountId] = $account->name; - } - $amount = bcmul($amount, '-1'); - $total = bcadd($total, $amount); - $chartData[$names[$accountId]] = $amount; - } - - // also collect all transactions NOT in these categories. - if ($others) { - /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); - $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]); - $journals = $collector->getJournals(); - $sum = strval($journals->sum('transaction_amount')); - $sum = bcmul($sum, '-1'); - $sum = bcsub($sum, $total); - $chartData[strval(trans('firefly.everything_else'))] = $sum; - } - - $data = $this->generator->pieChart($chartData); - $cache->store($data); + /** @var MetaPieChartInterface $helper */ + $helper = app(MetaPieChartInterface::class); + $helper->setAccounts($accounts); + $helper->setCategories($categories); + $helper->setUser(auth()->user()); + $helper->setStart($start); + $helper->setEnd($end); + $helper->setCollectOtherObjects(intval($others) === 1); + $chartData = $helper->generate('expense', 'account'); + $data = $this->generator->pieChart($chartData); return Response::json($data); } @@ -133,48 +101,16 @@ class CategoryReportController extends Controller */ public function accountIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others) { - /** @var bool $others */ - $others = intval($others) === 1; - $cache = new CacheProperties; - $cache->addProperty('chart.category.report.account-income'); - $cache->addProperty($accounts); - $cache->addProperty($categories); - $cache->addProperty($start); - $cache->addProperty($others); - $cache->addProperty($end); - if ($cache->has()) { - return Response::json($cache->get()); - } - - - $names = []; - $set = $this->getIncome($accounts, $categories, $start, $end); - $grouped = $this->groupByOpposingAccount($set); - $chartData = []; - $total = '0'; - - foreach ($grouped as $accountId => $amount) { - if (!isset($names[$accountId])) { - $account = $this->accountRepository->find(intval($accountId)); - $names[$accountId] = $account->name; - } - $total = bcadd($total, $amount); - $chartData[$names[$accountId]] = $amount; - } - - // also collect others? - if ($others) { - /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); - $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]); - $journals = $collector->getJournals(); - $sum = strval($journals->sum('transaction_amount')); - $sum = bcsub($sum, $total); - $chartData[strval(trans('firefly.everything_else'))] = $sum; - } - - $data = $this->generator->pieChart($chartData); - $cache->store($data); + /** @var MetaPieChartInterface $helper */ + $helper = app(MetaPieChartInterface::class); + $helper->setAccounts($accounts); + $helper->setCategories($categories); + $helper->setUser(auth()->user()); + $helper->setStart($start); + $helper->setEnd($end); + $helper->setCollectOtherObjects(intval($others) === 1); + $chartData = $helper->generate('income', 'account'); + $data = $this->generator->pieChart($chartData); return Response::json($data); } @@ -190,49 +126,16 @@ class CategoryReportController extends Controller */ public function categoryExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others) { - /** @var bool $others */ - $others = intval($others) === 1; - $cache = new CacheProperties; - $cache->addProperty('chart.category.report.category-expense'); - $cache->addProperty($accounts); - $cache->addProperty($categories); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty($others); - if ($cache->has()) { - return Response::json($cache->get()); - } - - $names = []; - $set = $this->getExpenses($accounts, $categories, $start, $end); - $grouped = $this->groupByCategory($set); - $total = '0'; - $chartData = []; - - foreach ($grouped as $categoryId => $amount) { - if (!isset($names[$categoryId])) { - $category = $this->categoryRepository->find(intval($categoryId)); - $names[$categoryId] = $category->name; - } - $amount = bcmul($amount, '-1'); - $total = bcadd($total, $amount); - $chartData[$names[$categoryId]] = $amount; - } - - // also collect all transactions NOT in these categories. - if ($others) { - /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); - $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]); - $journals = $collector->getJournals(); - $sum = strval($journals->sum('transaction_amount')); - $sum = bcmul($sum, '-1'); - $sum = bcsub($sum, $total); - $chartData[strval(trans('firefly.everything_else'))] = $sum; - } - - $data = $this->generator->pieChart($chartData); - $cache->store($data); + /** @var MetaPieChartInterface $helper */ + $helper = app(MetaPieChartInterface::class); + $helper->setAccounts($accounts); + $helper->setCategories($categories); + $helper->setUser(auth()->user()); + $helper->setStart($start); + $helper->setEnd($end); + $helper->setCollectOtherObjects(intval($others) === 1); + $chartData = $helper->generate('expense', 'category'); + $data = $this->generator->pieChart($chartData); return Response::json($data); } @@ -248,46 +151,17 @@ class CategoryReportController extends Controller */ public function categoryIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others) { - /** @var bool $others */ - $others = intval($others) === 1; - $cache = new CacheProperties; - $cache->addProperty('chart.category.report.category-income'); - $cache->addProperty($accounts); - $cache->addProperty($categories); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty($others); - if ($cache->has()) { - return Response::json($cache->get()); - } - $names = []; - $set = $this->getIncome($accounts, $categories, $start, $end); - $grouped = $this->groupByCategory($set); - $total = '0'; - $chartData = []; - - foreach ($grouped as $categoryId => $amount) { - if (!isset($names[$categoryId])) { - $category = $this->categoryRepository->find(intval($categoryId)); - $names[$categoryId] = $category->name; - } - $total = bcadd($total, $amount); - $chartData[$names[$categoryId]] = $amount; - } - - if ($others) { - /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); - $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]); - $journals = $collector->getJournals(); - $sum = strval($journals->sum('transaction_amount')); - $sum = bcsub($sum, $total); - $chartData[strval(trans('firefly.everything_else'))] = $sum; - } - - $data = $this->generator->pieChart($chartData); - $cache->store($data); + /** @var MetaPieChartInterface $helper */ + $helper = app(MetaPieChartInterface::class); + $helper->setAccounts($accounts); + $helper->setCategories($categories); + $helper->setUser(auth()->user()); + $helper->setStart($start); + $helper->setEnd($end); + $helper->setCollectOtherObjects(intval($others) === 1); + $chartData = $helper->generate('income', 'category'); + $data = $this->generator->pieChart($chartData); return Response::json($data); } diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php index 2a49d5b15e..b798f531e0 100644 --- a/app/Providers/FireflyServiceProvider.php +++ b/app/Providers/FireflyServiceProvider.php @@ -96,6 +96,9 @@ class FireflyServiceProvider extends ServiceProvider // chart generator: $this->app->bind('FireflyIII\Generator\Chart\Basic\GeneratorInterface', 'FireflyIII\Generator\Chart\Basic\ChartJsGenerator'); + // chart builder + $this->app->bind('FireflyIII\Helpers\Chart\MetaPieChartInterface', 'FireflyIII\Helpers\Chart\MetaPieChart'); + // other generators $this->app->bind('FireflyIII\Export\ProcessorInterface', 'FireflyIII\Export\Processor'); $this->app->bind('FireflyIII\Import\ImportProcedureInterface', 'FireflyIII\Import\ImportProcedure'); diff --git a/resources/views/reports/budget/month.twig b/resources/views/reports/budget/month.twig index e8de3bee8f..d5efaae2f7 100644 --- a/resources/views/reports/budget/month.twig +++ b/resources/views/reports/budget/month.twig @@ -80,7 +80,7 @@ </div> <label style="font-weight:normal;"> <input type="checkbox" id="budgets-out-pie-chart-checked"> - <small>{{ 'include_not_in_budget'|_ }}</small> + <small>{{ 'include_expense_not_in_budget'|_ }}</small> </label> </div> </div> @@ -97,7 +97,7 @@ </div> <label style="font-weight:normal;"> <input type="checkbox" id="accounts-out-pie-chart-checked"> - <small>{{ 'include_not_in_budget'|_ }}</small> + <small>{{ 'include_expense_not_in_account'|_ }}</small> </label> </div> </div> diff --git a/resources/views/reports/category/month.twig b/resources/views/reports/category/month.twig index cef7514534..b50d8b4aed 100644 --- a/resources/views/reports/category/month.twig +++ b/resources/views/reports/category/month.twig @@ -90,7 +90,7 @@ <canvas id="categories-in-pie-chart" style="margin:0 auto;" height="150" width="150"></canvas> <label style="font-weight:normal;"> <input type="checkbox" id="categories-in-pie-chart-checked"> - <small>{{ 'include_not_in_category'|_ }}</small> + <small>{{ 'include_income_not_in_category'|_ }}</small> </label> </div> </div> @@ -104,7 +104,7 @@ <canvas id="categories-out-pie-chart" style="margin:0 auto;" height="150" width="150"></canvas> <label style="font-weight:normal;"> <input type="checkbox" id="categories-out-pie-chart-checked"> - <small>{{ 'include_not_in_category'|_ }}</small> + <small>{{ 'include_expense_not_in_category'|_ }}</small> </label> </div> </div> @@ -119,7 +119,7 @@ <canvas id="accounts-in-pie-chart" style="margin:0 auto;" height="150" width="150"></canvas> <label style="font-weight:normal;"> <input type="checkbox" id="accounts-in-pie-chart-checked"> - <small>{{ 'include_not_in_category'|_ }}</small> + <small>{{ 'include_income_not_in_account'|_ }}</small> </label> </div> </div> @@ -133,7 +133,7 @@ <canvas id="accounts-out-pie-chart" style="margin:0 auto;" height="150" width="150"></canvas> <label style="font-weight:normal;"> <input type="checkbox" id="accounts-out-pie-chart-checked"> - <small>{{ 'include_not_in_category'|_ }}</small> + <small>{{ 'include_expenses_not_in_account'|_ }}</small> </label> </div> </div> From 0c6d21329661cf1b724c70d081b011207b543029 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Tue, 3 Jan 2017 17:26:31 +0100 Subject: [PATCH 489/709] Update scrutiniser configuration. --- .scrutinizer.yml | 1 + app/Http/Controllers/PreferencesController.php | 1 + 2 files changed, 2 insertions(+) diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 02bb0d0f4a..ac754cc957 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -39,6 +39,7 @@ checks: avoid_fixme_comments: true avoid_multiple_statements_on_same_line: true align_assignments: true + duplication: false javascript: true coding_style: diff --git a/app/Http/Controllers/PreferencesController.php b/app/Http/Controllers/PreferencesController.php index 93efecf2a8..f4bd94c4b5 100644 --- a/app/Http/Controllers/PreferencesController.php +++ b/app/Http/Controllers/PreferencesController.php @@ -113,6 +113,7 @@ class PreferencesController extends Controller * @param TokenFormRequest $request * * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @SuppressWarnings(PHPMD.UnusedFormalParameter) // it's unused but the class does some validation. */ public function postCode(TokenFormRequest $request) { From 2d59b6718d4ac6c852f896f870fd6465d5db8a9f Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Tue, 3 Jan 2017 17:46:08 +0100 Subject: [PATCH 490/709] Various code style fixes. --- app/Export/Processor.php | 7 ++++--- .../Controllers/Transaction/ConvertController.php | 12 ++++++------ app/Rules/Triggers/AbstractTrigger.php | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/app/Export/Processor.php b/app/Export/Processor.php index 15960d63f6..0d0421a460 100644 --- a/app/Export/Processor.php +++ b/app/Export/Processor.php @@ -155,7 +155,7 @@ class Processor implements ProcessorInterface $zip->close(); // delete the files: - $this->deleteFiles($disk); + $this->deleteFiles(); return true; } @@ -183,10 +183,11 @@ class Processor implements ProcessorInterface } /** - * @param FilesystemAdapter $disk + * */ - private function deleteFiles(FilesystemAdapter $disk) + private function deleteFiles() { + $disk = Storage::disk('export'); foreach ($this->getFiles() as $file) { $disk->delete($file); } diff --git a/app/Http/Controllers/Transaction/ConvertController.php b/app/Http/Controllers/Transaction/ConvertController.php index ebeec3b1d3..45f5583b9a 100644 --- a/app/Http/Controllers/Transaction/ConvertController.php +++ b/app/Http/Controllers/Transaction/ConvertController.php @@ -217,8 +217,8 @@ class ConvertController extends Controller switch ($joined) { default: throw new FireflyException('Cannot handle ' . $joined); - case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT: # one - case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT: #six + case TransactionType::WITHDRAWAL . '-' . TransactionType::DEPOSIT: // one + case TransactionType::TRANSFER . '-' . TransactionType::DEPOSIT: // six $data = [ 'name' => $data['source_account_revenue'], 'accountType' => 'revenue', @@ -228,14 +228,14 @@ class ConvertController extends Controller ]; $source = $accountRepository->store($data); break; - case TransactionType::WITHDRAWAL . '-' . TransactionType::TRANSFER: # two - case TransactionType::TRANSFER . '-' . TransactionType::WITHDRAWAL: #five + case TransactionType::WITHDRAWAL . '-' . TransactionType::TRANSFER: // two + case TransactionType::TRANSFER . '-' . TransactionType::WITHDRAWAL: // five $source = $sourceAccount; break; - case TransactionType::DEPOSIT . '-' . TransactionType::WITHDRAWAL: # three + case TransactionType::DEPOSIT . '-' . TransactionType::WITHDRAWAL: // three $source = $destinationAccount; break; - case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER: # four + case TransactionType::DEPOSIT . '-' . TransactionType::TRANSFER: // four $source = $accountRepository->find(intval($data['source_account_asset'])); break; } diff --git a/app/Rules/Triggers/AbstractTrigger.php b/app/Rules/Triggers/AbstractTrigger.php index f3be138f8a..a2492e1a13 100644 --- a/app/Rules/Triggers/AbstractTrigger.php +++ b/app/Rules/Triggers/AbstractTrigger.php @@ -23,7 +23,7 @@ use FireflyIII\Models\TransactionJournal; * * @package FireflyIII\Rules\Triggers */ -class AbstractTrigger +final class AbstractTrigger { /** @var bool */ public $stopProcessing; From f9f21efd36a62aa1e5259eb5b2db0ccdb70e4ab7 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Tue, 3 Jan 2017 20:02:48 +0100 Subject: [PATCH 491/709] Update config to fix #519 --- config/session.php | 187 ++++----------------------------------------- 1 file changed, 13 insertions(+), 174 deletions(-) diff --git a/config/session.php b/config/session.php index 51698cb45e..f5af7f0fdc 100644 --- a/config/session.php +++ b/config/session.php @@ -13,179 +13,18 @@ declare(strict_types = 1); return [ - - /* - |-------------------------------------------------------------------------- - | Default Session Driver - |-------------------------------------------------------------------------- - | - | This option controls the default session "driver" that will be used on - | requests. By default, we will use the lightweight native driver but - | you may specify any of the other wonderful drivers provided here. - | - | Supported: "file", "cookie", "database", "apc", - | "memcached", "redis", "array" - | - */ - - 'driver' => env('SESSION_DRIVER', 'file'), - - /* - |-------------------------------------------------------------------------- - | Session Lifetime - |-------------------------------------------------------------------------- - | - | Here you may specify the number of minutes that you wish the session - | to be allowed to remain idle before it expires. If you want them - | to immediately expire on the browser closing, set that option. - | - */ - - 'lifetime' => 120, - + 'driver' => env('SESSION_DRIVER', 'file'), + 'lifetime' => 120, 'expire_on_close' => false, - - /* - |-------------------------------------------------------------------------- - | Session Encryption - |-------------------------------------------------------------------------- - | - | This option allows you to easily specify that all of your session data - | should be encrypted before it is stored. All encryption will be run - | automatically by Laravel and you can use the Session like normal. - | - */ - - 'encrypt' => true, - - /* - |-------------------------------------------------------------------------- - | Session File Location - |-------------------------------------------------------------------------- - | - | When using the native session driver, we need a location where session - | files may be stored. A default has been set for you but a different - | location may be specified. This is only needed for file sessions. - | - */ - - 'files' => storage_path('framework/sessions'), - - /* - |-------------------------------------------------------------------------- - | Session Database Connection - |-------------------------------------------------------------------------- - | - | When using the "database" or "redis" session drivers, you may specify a - | connection that should be used to manage these sessions. This should - | correspond to a connection in your database configuration options. - | - */ - - 'connection' => null, - - /* - |-------------------------------------------------------------------------- - | Session Database Table - |-------------------------------------------------------------------------- - | - | When using the "database" session driver, you may specify the table we - | should use to manage the sessions. Of course, a sensible default is - | provided for you; however, you are free to change this as needed. - | - */ - - 'table' => 'sessions', - - /* - |-------------------------------------------------------------------------- - | Session Cache Store - |-------------------------------------------------------------------------- - | - | When using the "apc" or "memcached" session drivers, you may specify a - | cache store that should be used for these sessions. This value must - | correspond with one of the application's configured cache stores. - | - */ - - 'store' => null, - - /* - |-------------------------------------------------------------------------- - | Session Sweeping Lottery - |-------------------------------------------------------------------------- - | - | Some session drivers must manually sweep their storage location to get - | rid of old sessions from storage. Here are the chances that it will - | happen on a given request. By default, the odds are 2 out of 100. - | - */ - - 'lottery' => [2, 100], - - /* - |-------------------------------------------------------------------------- - | Session Cookie Name - |-------------------------------------------------------------------------- - | - | Here you may change the name of the cookie used to identify a session - | instance by ID. The name specified here will get used every time a - | new session cookie is created by the framework for every driver. - | - */ - - 'cookie' => 'firefly_session', - - /* - |-------------------------------------------------------------------------- - | Session Cookie Path - |-------------------------------------------------------------------------- - | - | The session cookie path determines the path for which the cookie will - | be regarded as available. Typically, this will be the root path of - | your application but you are free to change this when necessary. - | - */ - - 'path' => env('COOKIE_PATH', '/'), - - /* - |-------------------------------------------------------------------------- - | Session Cookie Domain - |-------------------------------------------------------------------------- - | - | Here you may change the domain of the cookie used to identify a session - | in your application. This will determine which domains the cookie is - | available to in your application. A sensible default has been set. - | - */ - - 'domain' => env('COOKIE_DOMAIN', null), - - /* - |-------------------------------------------------------------------------- - | HTTPS Only Cookies - |-------------------------------------------------------------------------- - | - | By setting this option to true, session cookies will only be sent back - | to the server if the browser has a HTTPS connection. This will keep - | the cookie from being sent to you if it can not be done securely. - | - */ - - 'secure' => env('COOKIE_SECURE', false), - - /* - |-------------------------------------------------------------------------- - | HTTP Access Only - |-------------------------------------------------------------------------- - | - | Setting this value to true will prevent JavaScript from accessing the - | value of the cookie and the cookie will only be accessible through - | the HTTP protocol. You are free to modify this option if needed. - | - */ - - 'http_only' => !env('COOKIE_SECURE', false), - + 'encrypt' => true, + 'files' => storage_path('framework/sessions'), + 'connection' => null, + 'table' => 'sessions', + 'store' => null, + 'lottery' => [2, 100], + 'cookie' => 'firefly_session', + 'path' => env('COOKIE_PATH', '/'), + 'domain' => env('COOKIE_DOMAIN', null), + 'secure' => env('COOKIE_SECURE', false), + 'http_only' => true, ]; From 457037ed99d39575b665f4da8094e5d0f3c06b47 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Tue, 3 Jan 2017 20:05:20 +0100 Subject: [PATCH 492/709] Should not be a final class, dummy. --- app/Rules/Triggers/AbstractTrigger.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Rules/Triggers/AbstractTrigger.php b/app/Rules/Triggers/AbstractTrigger.php index a2492e1a13..f3be138f8a 100644 --- a/app/Rules/Triggers/AbstractTrigger.php +++ b/app/Rules/Triggers/AbstractTrigger.php @@ -23,7 +23,7 @@ use FireflyIII\Models\TransactionJournal; * * @package FireflyIII\Rules\Triggers */ -final class AbstractTrigger +class AbstractTrigger { /** @var bool */ public $stopProcessing; From e2c613c422bf5d19d0522f9cf8f8f74d4ed39181 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Wed, 4 Jan 2017 04:48:58 +0100 Subject: [PATCH 493/709] New config. --- config/firefly.php | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/config/firefly.php b/config/firefly.php index 444a786cd6..45e813e6eb 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -19,17 +19,11 @@ declare(strict_types = 1); return [ 'configuration' => [ - 'single_user_mode' => true, - 'is_demo_site' => false, - 'must_confirm_account' => false, - 'mail_for_lockout' => false, - 'mail_for_blocked_domain' => false, - 'mail_for_blocked_email' => false, - 'mail_for_bad_login' => false, - 'mail_for_blocked_login' => false, + 'single_user_mode' => true, + 'is_demo_site' => false, ], 'chart' => 'chartjs', - 'version' => '4.3.0', + 'version' => '4.3.1', 'csv_import_enabled' => true, 'maxUploadSize' => 5242880, 'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'], @@ -150,7 +144,7 @@ return [ 'transaction_type' => 'FireflyIII\Models\TransactionType', 'currency' => 'FireflyIII\Models\TransactionCurrency', 'limitrepetition' => 'FireflyIII\Models\LimitRepetition', - 'budgetlimit' => 'FireflyIII\Models\BudgetLimit', + 'budgetlimit' => 'FireflyIII\Models\BudgetLimit', 'piggyBank' => 'FireflyIII\Models\PiggyBank', 'tj' => 'FireflyIII\Models\TransactionJournal', 'unfinishedJournal' => 'FireflyIII\Support\Binder\UnfinishedJournal', From 800f67908e0ad0427da1f012a8d19946841514da Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Wed, 4 Jan 2017 04:49:07 +0100 Subject: [PATCH 494/709] New change log. --- CHANGELOG.md | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80f72c0a53..7a7d24c3a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,24 +3,29 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## [4.3.1] - 2017-01-xx +## [4.3.1] - 2017-01-04 ### Added -- Support for Russian. +- Support for Russian and Polish. +- Support for a proper demo website. +- Support for custom decimal places in currencies (#506, suggested by @xpfgsyb). +- Most amounts are now right-aligned (#511, suggested by @xpfgsyb). +- German is now a "complete" language, more than 75% translated! ### Changed - **[New Github repository!](github.com/firefly-iii/firefly-iii)** - -### Deprecated -- With the realization of a proper demo site many administrative functions have become deprecated, and will be removed over the coming releases. Included but not limited to the ability to block domains for registration, get email notifications for specific actions and many more. +- Better category overview. +- #502, thanks to @zjean ### Removed -- Something +- Removed a lot of administration functions. +- Removed ability to activate users. ### Fixed -- Something +- #501, thanks to @zjean +- #513, thanks to @skibbipl ### Security -- Something +- #519, thanks to @xpfgsyb ## [4.3.0] - 2015-12-26 ### Added From d3614d3505ea9e83f4f95493d3495b05fdf05b87 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Wed, 4 Jan 2017 04:57:04 +0100 Subject: [PATCH 495/709] New strings for translation [skip ci] --- resources/lang/en_US/firefly.php | 7 +++++-- resources/views/reports/category/month.twig | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index fb60f4396c..ec41d10bf2 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -725,8 +725,11 @@ return [ 'expense_per_budget' => 'Expense per budget', 'income_per_account' => 'Income per account', 'expense_per_account' => 'Expense per account', - 'include_not_in_category' => 'Include categories not selected for this report', - 'include_not_in_budget' => 'Include budgets not selected for this report', + 'include_expense_not_in_budget' => 'Included expenses not in the selected budget(s)', + 'include_expense_not_in_account' => 'Included expenses not in the selected account(s)', + 'include_expense_not_in_category' => 'Included expenses not in the selected category(ies)', + 'include_income_not_in_category' => 'Included income not in the selected category(ies)', + 'include_income_not_in_account' => 'Included income not in the selected account(s)', 'everything_else' => 'Everything else', 'income_and_expenses' => 'Income and expenses', 'spent_average' => 'Spent (average)', diff --git a/resources/views/reports/category/month.twig b/resources/views/reports/category/month.twig index b50d8b4aed..4bf6652694 100644 --- a/resources/views/reports/category/month.twig +++ b/resources/views/reports/category/month.twig @@ -133,7 +133,7 @@ <canvas id="accounts-out-pie-chart" style="margin:0 auto;" height="150" width="150"></canvas> <label style="font-weight:normal;"> <input type="checkbox" id="accounts-out-pie-chart-checked"> - <small>{{ 'include_expenses_not_in_account'|_ }}</small> + <small>{{ 'include_expense_not_in_account'|_ }}</small> </label> </div> </div> From 1c9ebafe2b925d6d3e37d0f78a596744123e1233 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Wed, 4 Jan 2017 09:15:41 +0100 Subject: [PATCH 496/709] Fix password reset routine. --- app/Http/Controllers/Auth/ForgotPasswordController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php index 1b95f56d44..3942c0868b 100644 --- a/app/Http/Controllers/Auth/ForgotPasswordController.php +++ b/app/Http/Controllers/Auth/ForgotPasswordController.php @@ -16,6 +16,7 @@ use FireflyIII\Http\Controllers\Controller; use FireflyIII\User; use Illuminate\Foundation\Auth\SendsPasswordResetEmails; use Illuminate\Http\Request; +use Password; /** * Class ForgotPasswordController From daf3a95db0599c5d9e1a939526c99c47c882f9b5 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Wed, 4 Jan 2017 09:28:49 +0100 Subject: [PATCH 497/709] Updated laravel, removed GitHub move announcement. --- app/Console/Commands/MoveRepository.php | 86 ------------------------- app/Console/Kernel.php | 1 - app/Http/Middleware/VerifyCsrfToken.php | 24 ++++++- composer.json | 4 +- composer.lock | 53 +++++++-------- 5 files changed, 51 insertions(+), 117 deletions(-) delete mode 100644 app/Console/Commands/MoveRepository.php diff --git a/app/Console/Commands/MoveRepository.php b/app/Console/Commands/MoveRepository.php deleted file mode 100644 index 51d170d7c3..0000000000 --- a/app/Console/Commands/MoveRepository.php +++ /dev/null @@ -1,86 +0,0 @@ -<?php -/** - * MoveRepository.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -declare(strict_types = 1); - -namespace FireflyIII\Console\Commands; - -use Carbon\Carbon; -use Illuminate\Console\Command; - -/** - * Class MoveRepository - * - * @package FireflyIII\Console\Commands - */ -class MoveRepository extends Command -{ - /** - * The console command description. - * - * @var string - */ - protected $description = 'Alerts the user that the Github repository will move, if they are interested to know this.'; - /** - * The name and signature of the console command. - * - * @var string - */ - protected $signature = 'firefly:github-move'; - - /** - * Create a new command instance. - * - */ - public function __construct() - { - parent::__construct(); - } - - /** - * Execute the console command. - */ - public function handle() - { - $moveDate = new Carbon('2017-01-01'); - $final = new Carbon('2017-03-01'); - $now = new Carbon; - - // display message before 2017-01-01 - if ($moveDate > $now) { - $this->line('+------------------------------------------------------------------------------+'); - $this->line(''); - $this->line('The Github repository for Firefly III will MOVE'); - $this->line('This move will be on January 1st 2017'); - $this->line(''); - $this->error('READ THIS WIKI PAGE FOR MORE INFORMATION'); - $this->line(''); - $this->info('https://github.com/firefly-iii/help/wiki/New-Github-repository'); - $this->line(''); - $this->line('+------------------------------------------------------------------------------+'); - } - - // display message after 2017-01-01 but before 2017-03-01 - if ($moveDate <= $now && $now <= $final) { - $this->line('+------------------------------------------------------------------------------+'); - $this->line(''); - $this->line('The Github repository for Firefly III has MOVED'); - $this->line('This move was on January 1st 2017!'); - $this->line(''); - $this->error('READ THIS WIKI PAGE FOR MORE INFORMATION'); - $this->line(''); - $this->info('https://github.com/firefly-iii/help/wiki/New-Github-repository'); - $this->line(''); - $this->line('+------------------------------------------------------------------------------+'); - } - - } -} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index dc634190ca..61537ce4e8 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -63,7 +63,6 @@ class Kernel extends ConsoleKernel EncryptFile::class, ScanAttachments::class, UpgradeDatabase::class, - MoveRepository::class, ]; /** diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php index 6eb1050494..11ec0455c9 100644 --- a/app/Http/Middleware/VerifyCsrfToken.php +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -13,7 +13,8 @@ declare(strict_types = 1); namespace FireflyIII\Http\Middleware; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier; - +use Symfony\Component\HttpFoundation\Cookie; +use Carbon\Carbon; /** * Class VerifyCsrfToken * @@ -30,4 +31,25 @@ class VerifyCsrfToken extends BaseVerifier = [ // ]; + + /** + * Add the CSRF token to the response cookies. + * + * @param \Illuminate\Http\Request $request + * @param \Symfony\Component\HttpFoundation\Response $response + * @return \Symfony\Component\HttpFoundation\Response + */ + protected function addCookieToResponse($request, $response) + { + $config = config('session'); + + $response->headers->setCookie( + new Cookie( + 'XSRF-TOKEN', $request->session()->token(), Carbon::now()->getTimestamp() + 60 * $config['lifetime'], + $config['path'], $config['domain'], $config['secure'], true + ) + ); + + return $response; + } } diff --git a/composer.json b/composer.json index 7c6b72dab5..4fca5041fe 100755 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ "require": { "php": ">=7.0.0", "ext-intl": "*", - "laravel/framework": "5.3.18", + "laravel/framework": "5.3.28", "davejamesmiller/laravel-breadcrumbs": "^3.0", "watson/validating": "3.*", "doctrine/dbal": "^2.5", @@ -72,7 +72,6 @@ ], "post-install-cmd": [ "Illuminate\\Foundation\\ComposerScripts::postInstall", - "php artisan firefly:github-move", "php artisan optimize" ], "post-update-cmd": [ @@ -80,7 +79,6 @@ "php artisan firefly:upgrade-instructions", "php artisan firefly:upgrade-database", "php artisan firefly:verify", - "php artisan firefly:github-move", "php artisan optimize" ] }, diff --git a/composer.lock b/composer.lock index cee08cb19e..0caf0c8130 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "38cc0ca42a8c6c518d99045c4bfe5d19", + "content-hash": "977583861ece20e991f3f68223551012", "packages": [ { "name": "bacon/bacon-qr-code", @@ -854,16 +854,16 @@ }, { "name": "laravel/framework", - "version": "v5.3.18", + "version": "v5.3.28", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "9bee167d173857c25966c19afdaa66f127ca6784" + "reference": "a64fc4f8958091ca39623b2e8c8f173cb34fa47a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/9bee167d173857c25966c19afdaa66f127ca6784", - "reference": "9bee167d173857c25966c19afdaa66f127ca6784", + "url": "https://api.github.com/repos/laravel/framework/zipball/a64fc4f8958091ca39623b2e8c8f173cb34fa47a", + "reference": "a64fc4f8958091ca39623b2e8c8f173cb34fa47a", "shasum": "" }, "require": { @@ -878,7 +878,7 @@ "nesbot/carbon": "~1.20", "paragonie/random_compat": "~1.4|~2.0", "php": ">=5.6.4", - "psy/psysh": "0.7.*", + "psy/psysh": "0.7.*|0.8.*", "ramsey/uuid": "~3.0", "swiftmailer/swiftmailer": "~5.1", "symfony/console": "3.1.*", @@ -945,7 +945,7 @@ "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (~2.0).", "symfony/css-selector": "Required to use some of the crawler integration testing tools (3.1.*).", "symfony/dom-crawler": "Required to use most of the crawler integration testing tools (3.1.*).", - "symfony/psr-http-message-bridge": "Required to psr7 bridging features (0.2.*)." + "symfony/psr-http-message-bridge": "Required to use psr7 bridging features (0.2.*)." }, "type": "library", "extra": { @@ -978,7 +978,7 @@ "framework", "laravel" ], - "time": "2016-10-08T01:51:20+00:00" + "time": "2016-12-15T18:03:17+00:00" }, { "name": "laravelcollective/html", @@ -1414,24 +1414,24 @@ }, { "name": "nikic/php-parser", - "version": "v2.1.1", + "version": "v3.0.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "4dd659edadffdc2143e4753df655d866dbfeedf0" + "reference": "adf44419c0fc014a0f191db6f89d3e55d4211744" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4dd659edadffdc2143e4753df655d866dbfeedf0", - "reference": "4dd659edadffdc2143e4753df655d866dbfeedf0", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/adf44419c0fc014a0f191db6f89d3e55d4211744", + "reference": "adf44419c0fc014a0f191db6f89d3e55d4211744", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": ">=5.4" + "php": ">=5.5" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "~4.0|~5.0" }, "bin": [ "bin/php-parse" @@ -1439,7 +1439,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1461,7 +1461,7 @@ "parser", "php" ], - "time": "2016-09-16T12:04:44+00:00" + "time": "2016-12-06T11:30:35+00:00" }, { "name": "paragonie/random_compat", @@ -1621,37 +1621,38 @@ }, { "name": "psy/psysh", - "version": "v0.7.2", + "version": "v0.8.0", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "e64e10b20f8d229cac76399e1f3edddb57a0f280" + "reference": "4a8860e13aa68a4bbf2476c014f8a1f14f1bf991" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/e64e10b20f8d229cac76399e1f3edddb57a0f280", - "reference": "e64e10b20f8d229cac76399e1f3edddb57a0f280", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/4a8860e13aa68a4bbf2476c014f8a1f14f1bf991", + "reference": "4a8860e13aa68a4bbf2476c014f8a1f14f1bf991", "shasum": "" }, "require": { "dnoegel/php-xdg-base-dir": "0.1", "jakub-onderka/php-console-highlighter": "0.3.*", - "nikic/php-parser": "^1.2.1|~2.0", + "nikic/php-parser": "~1.3|~2.0|~3.0", "php": ">=5.3.9", "symfony/console": "~2.3.10|^2.4.2|~3.0", "symfony/var-dumper": "~2.7|~3.0" }, "require-dev": { - "fabpot/php-cs-fixer": "~1.5", - "phpunit/phpunit": "~3.7|~4.0|~5.0", - "squizlabs/php_codesniffer": "~2.0", + "friendsofphp/php-cs-fixer": "~1.11", + "hoa/console": "~3.16|~1.14", + "phpunit/phpunit": "~4.4|~5.0", "symfony/finder": "~2.1|~3.0" }, "suggest": { "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", "ext-pdo-sqlite": "The doc command requires SQLite to work.", "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well.", - "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history." + "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history.", + "hoa/console": "A pure PHP readline implementation. You'll want this if your PHP install doesn't already support readline or libedit." }, "bin": [ "bin/psysh" @@ -1689,7 +1690,7 @@ "interactive", "shell" ], - "time": "2016-03-09T05:03:14+00:00" + "time": "2016-12-07T17:15:07+00:00" }, { "name": "ramsey/uuid", From ba957196dad6df78e7552288a17c7c9e8f64fd19 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Wed, 4 Jan 2017 13:14:06 +0100 Subject: [PATCH 498/709] Make sure correct locale is used. Debug info. #521 [skip ci] --- app/Support/Amount.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/Support/Amount.php b/app/Support/Amount.php index 1e52f19ef3..8294a53021 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -17,6 +17,7 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionJournal; use Illuminate\Support\Collection; +use Log; use Preferences as Prefs; /** @@ -50,7 +51,10 @@ class Amount */ public function formatAnything(TransactionCurrency $format, string $amount, bool $coloured = true): string { - setlocale(LC_MONETARY, 0); + $locale = explode(',', trans('config.locale')); + $locale = array_map('trim', $locale); + Log::debug('formatAnything(). Will set locale to', $locale); + setlocale(LC_MONETARY, $locale); $float = round($amount, 12); $info = localeconv(); $formatted = number_format($float, $format->decimal_places, $info['mon_decimal_point'], $info['mon_thousands_sep']); From 83f5f776a60eb654fcdd4afaec7f6db5fe32da4d Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Wed, 4 Jan 2017 17:25:28 +0100 Subject: [PATCH 499/709] Code for #522 --- app/Http/Requests/BillFormRequest.php | 4 ++-- app/Http/Requests/JournalFormRequest.php | 2 +- app/Http/Requests/PiggyBankFormRequest.php | 2 +- app/Support/ExpandedForm.php | 3 --- app/Validation/FireflyValidator.php | 14 ++++++++++++++ resources/lang/en_US/validation.php | 1 + 6 files changed, 19 insertions(+), 7 deletions(-) diff --git a/app/Http/Requests/BillFormRequest.php b/app/Http/Requests/BillFormRequest.php index c27a24377d..0b0b85e29d 100644 --- a/app/Http/Requests/BillFormRequest.php +++ b/app/Http/Requests/BillFormRequest.php @@ -67,8 +67,8 @@ class BillFormRequest extends Request $rules = [ 'name' => $nameRule, 'match' => $matchRule, - 'amount_min' => 'required|numeric|min:0.01', - 'amount_max' => 'required|numeric|min:0.01', + 'amount_min' => 'required|numeric|more:0', + 'amount_max' => 'required|numeric|more:0', 'amount_currency_id_amount_min' => 'required|exists:transaction_currencies,id', 'amount_currency_id_amount_max' => 'required|exists:transaction_currencies,id', 'date' => 'required|date', diff --git a/app/Http/Requests/JournalFormRequest.php b/app/Http/Requests/JournalFormRequest.php index 69734b26bf..2ecc96af87 100644 --- a/app/Http/Requests/JournalFormRequest.php +++ b/app/Http/Requests/JournalFormRequest.php @@ -94,7 +94,7 @@ class JournalFormRequest extends Request 'notes' => 'min:1,max:50000', // and then transaction rules: 'description' => 'required|between:1,255', - 'amount' => 'numeric|required|min:0.01', + 'amount' => 'numeric|required|more:0', 'budget_id' => 'mustExist:budgets,id|belongsToUser:budgets,id', 'category' => 'between:1,255', 'source_account_id' => 'numeric|belongsToUser:accounts,id', diff --git a/app/Http/Requests/PiggyBankFormRequest.php b/app/Http/Requests/PiggyBankFormRequest.php index 3411975166..d0e6889d62 100644 --- a/app/Http/Requests/PiggyBankFormRequest.php +++ b/app/Http/Requests/PiggyBankFormRequest.php @@ -63,7 +63,7 @@ class PiggyBankFormRequest extends Request $rules = [ 'name' => $nameRule, 'account_id' => 'required|belongsToUser:accounts', - 'targetamount' => 'required|min:0.01', + 'targetamount' => 'required|numeric|more:0', 'amount_currency_id_targetamount' => 'required|exists:transaction_currencies,id', 'startdate' => 'date', 'targetdate' => $targetDateRule, diff --git a/app/Support/ExpandedForm.php b/app/Support/ExpandedForm.php index 258da0331b..28b6972ed7 100644 --- a/app/Support/ExpandedForm.php +++ b/app/Support/ExpandedForm.php @@ -496,9 +496,6 @@ class ExpandedForm $classes = $this->getHolderClasses($name); $value = $this->fillFieldValue($name, $value); $options['step'] = 'any'; - if ($view !== 'balance') { - $options['min'] = '0.01'; - } $defaultCurrency = isset($options['currency']) ? $options['currency'] : Amt::getDefaultCurrency(); $currencies = Amt::getAllCurrencies(); unset($options['currency']); diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index 778996bb88..61590816b4 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -147,6 +147,20 @@ class FireflyValidator extends Validator return (intval($checksum) === 1); } + /** + * @param $attribute + * @param $value + * @param $parameters + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * + * @return bool + */ + public function validateMore($attribute, $value, $parameters): bool + { + $compare = $parameters[0] ?? '0'; + return bccomp($value, $compare) > 0; + } + /** * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @param $attribute diff --git a/resources/lang/en_US/validation.php b/resources/lang/en_US/validation.php index 7905a9f4c8..ea21edefe4 100644 --- a/resources/lang/en_US/validation.php +++ b/resources/lang/en_US/validation.php @@ -23,6 +23,7 @@ return [ 'belongs_to_user' => 'The value of :attribute is unknown', 'accepted' => 'The :attribute must be accepted.', 'bic' => 'This is not a valid BIC.', + 'more' => ':attribute must be larger than zero.', 'active_url' => 'The :attribute is not a valid URL.', 'after' => 'The :attribute must be a date after :date.', 'alpha' => 'The :attribute may only contain letters.', From dbebfe7c07b766fa7d3ca979defb9f80e76f94e7 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 08:32:31 +0100 Subject: [PATCH 500/709] Remove unused method. --- app/Helpers/Report/ReportHelper.php | 25 -------------------- app/Helpers/Report/ReportHelperInterface.php | 9 ------- 2 files changed, 34 deletions(-) diff --git a/app/Helpers/Report/ReportHelper.php b/app/Helpers/Report/ReportHelper.php index 71d80119a4..40b7b68eaf 100644 --- a/app/Helpers/Report/ReportHelper.php +++ b/app/Helpers/Report/ReportHelper.php @@ -103,31 +103,6 @@ class ReportHelper implements ReportHelperInterface return $collection; } - /** - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return CategoryCollection - */ - public function getCategoryReport(Collection $accounts, Carbon $start, Carbon $end): CategoryCollection - { - $object = new CategoryCollection; - /** @var CategoryRepositoryInterface $repository */ - $repository = app(CategoryRepositoryInterface::class); - $categories = $repository->getCategories(); - - /** @var Category $category */ - foreach ($categories as $category) { - $spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $end); - // CategoryCollection expects the amount in $spent: - $category->spent = $spent; - $object->addCategory($category); - } - - return $object; - } - /** * @param Carbon $date * diff --git a/app/Helpers/Report/ReportHelperInterface.php b/app/Helpers/Report/ReportHelperInterface.php index 6e7343629d..9124821593 100644 --- a/app/Helpers/Report/ReportHelperInterface.php +++ b/app/Helpers/Report/ReportHelperInterface.php @@ -42,15 +42,6 @@ interface ReportHelperInterface */ public function getBillReport(Carbon $start, Carbon $end, Collection $accounts): BillCollection; - /** - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * - * @return CategoryCollection - */ - public function getCategoryReport(Collection $accounts, Carbon $start, Carbon $end): CategoryCollection; - /** * @param Carbon $date * From b9599d3aa1c2214e896a9f20623254ae4215eb04 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 08:33:04 +0100 Subject: [PATCH 501/709] Add two methods that have a different way of collecting information #524 --- .../Category/CategoryRepository.php | 47 +++++++++++++++++++ .../Category/CategoryRepositoryInterface.php | 19 ++++++++ 2 files changed, 66 insertions(+) diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 9b881df691..a59ad1a846 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -414,6 +414,25 @@ class CategoryRepository implements CategoryRepositoryInterface return $sum; } + /** + * @param Collection $categories + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function spentInPeriodCollector(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string + { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class, [$this->user]); + $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($accounts)->setCategories($categories); + $set = $collector->getJournals(); + $sum = strval($set->sum('transaction_amount')); + + return $sum; + } + /** * @param Collection $accounts * @param Carbon $start @@ -429,6 +448,34 @@ class CategoryRepository implements CategoryRepositoryInterface return $sum; } + /** + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function spentInPeriodWithoutCategoryCollector(Collection $accounts, Carbon $start, Carbon $end): string + { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class, [$this->user]); + $collector->setRange($start, $end)->setAccounts($accounts)->setTypes([TransactionType::WITHDRAWAL])->withoutCategory(); + $set = $collector->getJournals(); + $set = $set->filter( + function (Transaction $transaction) { + if (bccomp($transaction->transaction_amount, '0') === -1) { + return $transaction; + } + + return null; + } + ); + + $sum = strval($set->sum('transaction_amount')); + + return $sum; + } + /** * @param array $data * diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index bbcd272d1e..4f07aafcf6 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -140,6 +140,16 @@ interface CategoryRepositoryInterface */ public function spentInPeriod(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string; + /** + * @param Collection $categories + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function spentInPeriodCollector(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string; + /** * @param Collection $accounts * @param Carbon $start @@ -149,6 +159,15 @@ interface CategoryRepositoryInterface */ public function spentInPeriodWithoutCategory(Collection $accounts, Carbon $start, Carbon $end): string; + /** + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function spentInPeriodWithoutCategoryCollector(Collection $accounts, Carbon $start, Carbon $end): string; + /** * @param array $data * From af466a1d752c6ab2d457c0b2626dc8230cb17a99 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 08:33:22 +0100 Subject: [PATCH 502/709] Refactor code to verify these methods work #524 --- app/Http/Controllers/Chart/CategoryController.php | 9 +++++---- app/Http/Controllers/Report/CategoryController.php | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 6ba325636a..15c807412f 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -90,7 +90,7 @@ class CategoryController extends Controller while ($start <= $end) { $currentEnd = Navigation::endOfPeriod($start, $range); - $spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $currentEnd); + $spent = $repository->spentInPeriodCollector(new Collection([$category]), $accounts, $start, $currentEnd); $earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $currentEnd); $label = Navigation::periodShow($start, $range); $chartData[0]['entries'][$label] = bcmul($spent, '-1'); @@ -143,12 +143,13 @@ class CategoryController extends Controller $accounts = $accountRepository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]); /** @var Category $category */ foreach ($categories as $category) { - $spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $end); + $spent = $repository->spentInPeriodCollector(new Collection([$category]), $accounts, $start, $end); if (bccomp($spent, '0') === -1) { $chartData[$category->name] = bcmul($spent, '-1'); } } - $chartData[strval(trans('firefly.no_category'))] = bcmul($repository->spentInPeriodWithoutCategory(new Collection, $start, $end), '-1'); + + $chartData[strval(trans('firefly.no_category'))] = bcmul($repository->spentInPeriodWithoutCategoryCollector(new Collection, $start, $end), '-1'); // sort arsort($chartData); @@ -314,7 +315,7 @@ class CategoryController extends Controller ]; while ($start <= $end) { - $spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $start); + $spent = $repository->spentInPeriodCollector(new Collection([$category]), $accounts, $start, $start); $earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $start); $label = Navigation::periodShow($start, '1D'); diff --git a/app/Http/Controllers/Report/CategoryController.php b/app/Http/Controllers/Report/CategoryController.php index 730deeaece..2426dd6ea6 100644 --- a/app/Http/Controllers/Report/CategoryController.php +++ b/app/Http/Controllers/Report/CategoryController.php @@ -123,7 +123,7 @@ class CategoryController extends Controller $report = []; /** @var Category $category */ foreach ($categories as $category) { - $spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $end); + $spent = $repository->spentInPeriodCollector(new Collection([$category]), $accounts, $start, $end); if (bccomp($spent, '0') !== 0) { $report[$category->id] = ['name' => $category->name, 'spent' => $spent]; } From d48cc698987108b84e0a95010da401ea4ce6f7ce Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 08:34:22 +0100 Subject: [PATCH 503/709] Removed old versions of methods #524 --- .../Category/CategoryRepository.php | 31 ------------------- .../Category/CategoryRepositoryInterface.php | 19 ------------ 2 files changed, 50 deletions(-) diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index a59ad1a846..0b69ad69e3 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -398,22 +398,6 @@ class CategoryRepository implements CategoryRepositoryInterface return $result; } - /** - * @param Collection $categories - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - public function spentInPeriod(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string - { - $sum = $this->sumInPeriod($categories, $accounts, TransactionType::WITHDRAWAL, $start, $end); - $sum = bcmul($sum, '-1'); - - return $sum; - } - /** * @param Collection $categories * @param Collection $accounts @@ -433,21 +417,6 @@ class CategoryRepository implements CategoryRepositoryInterface return $sum; } - /** - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - public function spentInPeriodWithoutCategory(Collection $accounts, Carbon $start, Carbon $end): string - { - $types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]; - $sum = $this->sumInPeriodWithoutCategory($accounts, $types, $start, $end); - - return $sum; - } - /** * @param Collection $accounts * @param Carbon $start diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index 4f07aafcf6..0fd3ef1399 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -130,16 +130,6 @@ interface CategoryRepositoryInterface */ public function periodIncomeNoCategory(Collection $accounts, Carbon $start, Carbon $end): array; - /** - * @param Collection $categories - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - public function spentInPeriod(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string; - /** * @param Collection $categories * @param Collection $accounts @@ -150,15 +140,6 @@ interface CategoryRepositoryInterface */ public function spentInPeriodCollector(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string; - /** - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - public function spentInPeriodWithoutCategory(Collection $accounts, Carbon $start, Carbon $end): string; - /** * @param Collection $accounts * @param Carbon $start From 4d49701203d111d0fc56309d4616da2fc7e36b1c Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 08:39:46 +0100 Subject: [PATCH 504/709] =?UTF-8?q?Add=20new=20=E2=80=9Cearned=20in=20peri?= =?UTF-8?q?od=E2=80=9D=20method.=20#524?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Category/CategoryRepository.php | 19 +++++++++++++++++++ .../Category/CategoryRepositoryInterface.php | 10 ++++++++++ 2 files changed, 29 insertions(+) diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 0b69ad69e3..dab725a385 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -74,6 +74,25 @@ class CategoryRepository implements CategoryRepositoryInterface } + /** + * @param Collection $categories + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function earnedInPeriodCollector(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string + { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class, [$this->user]); + $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($accounts)->setCategories($categories); + $set = $collector->getJournals(); + $sum = strval($set->sum('transaction_amount')); + + return $sum; + } + /** * @param Collection $accounts * @param Carbon $start diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index 0fd3ef1399..96dd7fd02b 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -41,6 +41,16 @@ interface CategoryRepositoryInterface */ public function earnedInPeriod(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string; + /** + * @param Collection $categories + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function earnedInPeriodCollector(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string; + /** * @param Collection $accounts * @param Carbon $start From 7c82f4560443ed14abebb47b8fec886e58541635 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 08:40:05 +0100 Subject: [PATCH 505/709] =?UTF-8?q?Refactor=20code=20to=20use=20new=20?= =?UTF-8?q?=E2=80=9Cearned=20in=20period=E2=80=9D=20method.=20#524?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/CategoryController.php | 5 +++-- app/Http/Controllers/Chart/CategoryController.php | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index b9146890db..44d25e47f1 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -319,6 +319,7 @@ class CategoryController extends Controller */ private function getGroupedEntries(Category $category): Collection { + /** @var CategoryRepositoryInterface $repository */ $repository = app(CategoryRepositoryInterface::class); $accountRepository = app(AccountRepositoryInterface::class); $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); @@ -344,8 +345,8 @@ class CategoryController extends Controller while ($end >= $first) { $end = Navigation::startOfPeriod($end, $range); $currentEnd = Navigation::endOfPeriod($end, $range); - $spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $end, $currentEnd); - $earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $end, $currentEnd); + $spent = $repository->spentInPeriodCollector(new Collection([$category]), $accounts, $end, $currentEnd); + $earned = $repository->earnedInPeriodCollector(new Collection([$category]), $accounts, $end, $currentEnd); $dateStr = $end->format('Y-m-d'); $dateName = Navigation::periodShow($end, $range); $entries->push([$dateStr, $dateName, $spent, $earned]); diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 15c807412f..8d7c04c7fe 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -91,7 +91,7 @@ class CategoryController extends Controller while ($start <= $end) { $currentEnd = Navigation::endOfPeriod($start, $range); $spent = $repository->spentInPeriodCollector(new Collection([$category]), $accounts, $start, $currentEnd); - $earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $currentEnd); + $earned = $repository->earnedInPeriodCollector(new Collection([$category]), $accounts, $start, $currentEnd); $label = Navigation::periodShow($start, $range); $chartData[0]['entries'][$label] = bcmul($spent, '-1'); $chartData[1]['entries'][$label] = $earned; @@ -316,7 +316,7 @@ class CategoryController extends Controller while ($start <= $end) { $spent = $repository->spentInPeriodCollector(new Collection([$category]), $accounts, $start, $start); - $earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $start); + $earned = $repository->earnedInPeriodCollector(new Collection([$category]), $accounts, $start, $start); $label = Navigation::periodShow($start, '1D'); $chartData[0]['entries'][$label] = bcmul($spent, '-1'); From cdf6e5a4879c7d3a37e5406952064cd64fa7a29d Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 08:40:26 +0100 Subject: [PATCH 506/709] Remove old method #524 --- .../Category/CategoryRepositoryInterface.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index 96dd7fd02b..28e333f5b2 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -31,16 +31,6 @@ interface CategoryRepositoryInterface */ public function destroy(Category $category): bool; - /** - * @param Collection $categories - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - public function earnedInPeriod(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string; - /** * @param Collection $categories * @param Collection $accounts From a79b2a7773463d6c489dae25f7f72cf10eeff5bf Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 08:41:11 +0100 Subject: [PATCH 507/709] Remove old method and another unused method #524 --- .../Category/CategoryRepository.php | 31 ------------------- .../Category/CategoryRepositoryInterface.php | 9 ------ 2 files changed, 40 deletions(-) diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index dab725a385..a1ada9272e 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -58,22 +58,6 @@ class CategoryRepository implements CategoryRepositoryInterface return true; } - /** - * @param Collection $categories - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - public function earnedInPeriod(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string - { - $sum = $this->sumInPeriod($categories, $accounts, TransactionType::DEPOSIT, $start, $end); - - return $sum; - - } - /** * @param Collection $categories * @param Collection $accounts @@ -93,21 +77,6 @@ class CategoryRepository implements CategoryRepositoryInterface return $sum; } - /** - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - public function earnedInPeriodWithoutCategory(Collection $accounts, Carbon $start, Carbon $end): string - { - $types = [TransactionType::DEPOSIT, TransactionType::TRANSFER]; - $sum = $this->sumInPeriodWithoutCategory($accounts, $types, $start, $end); - - return $sum; - } - /** * Find a category * diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index 28e333f5b2..61f4810f3a 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -41,15 +41,6 @@ interface CategoryRepositoryInterface */ public function earnedInPeriodCollector(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string; - /** - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - public function earnedInPeriodWithoutCategory(Collection $accounts, Carbon $start, Carbon $end): string; - /** * Find a category * From 1be49876df4413bba827cbc1e1758d7e7c03bf8c Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 08:41:37 +0100 Subject: [PATCH 508/709] Remove complicated no longer used methods #524 --- .../Category/CategoryRepository.php | 120 ------------------ 1 file changed, 120 deletions(-) diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index a1ada9272e..f415a7c40a 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -466,124 +466,4 @@ class CategoryRepository implements CategoryRepositoryInterface return $category; } - /** - * @param Collection $categories - * @param Collection $accounts - * @param string $type - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - private function sumInPeriod(Collection $categories, Collection $accounts, string $type, Carbon $start, Carbon $end): string - { - $categoryIds = $categories->pluck('id')->toArray(); - $query = $this->user - ->transactionJournals() - ->leftJoin( // join source transaction - 'transactions as source_transactions', function (JoinClause $join) { - $join->on('source_transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->where('source_transactions.amount', '<', 0); - - } - ) - ->leftJoin( // join destination transaction (slighly more complex) - 'transactions as destination_transactions', function (JoinClause $join) { - $join->on('destination_transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->where('destination_transactions.amount', '>', 0) - ->where('destination_transactions.identifier', '=', DB::raw('source_transactions.identifier')); - } - ) - // left join source category: - ->leftJoin('category_transaction as source_cat_trans', 'source_transactions.id', '=', 'source_cat_trans.transaction_id') - // left join destination category: - ->leftJoin('category_transaction as dest_cat_trans', 'source_transactions.id', '=', 'dest_cat_trans.transaction_id') - // left join journal category: - ->leftJoin('category_transaction_journal as journal_category', 'journal_category.transaction_journal_id', '=', 'transaction_journals.id') - // left join transaction type: - ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') - // where nothing is deleted: - ->whereNull('transaction_journals.deleted_at') - ->whereNull('source_transactions.deleted_at') - ->whereNull('destination_transactions.deleted_at') - // in correct date range: - ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) - ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) - // correct categories (complex) - ->where( - function ($q1) use ($categoryIds) { - $q1->where( - function ($q2) use ($categoryIds) { - // source and destination transaction have categories, journal does not. - $q2->whereIn('source_cat_trans.category_id', $categoryIds); - $q2->whereIn('dest_cat_trans.category_id', $categoryIds); - $q2->whereNull('journal_category.category_id'); - } - ); - $q1->orWhere( - function ($q3) use ($categoryIds) { - // journal has category, source and destination have not - $q3->whereNull('source_cat_trans.category_id'); - $q3->whereNull('dest_cat_trans.category_id'); - $q3->whereIn('journal_category.category_id', $categoryIds); - } - ); - } - ) - // type: - ->where('transaction_types.type', $type); - // accounts, if present: - if ($accounts->count() > 0) { - $accountIds = $accounts->pluck('id')->toArray(); - $query->where( - function ($q) use ($accountIds) { - $q->whereIn('source_transactions.account_id', $accountIds); - $q->orWhereIn('destination_transactions.account_id', $accountIds); - } - ); - } - $sum = strval($query->sum('destination_transactions.amount')); - if ($sum === '') { - $sum = '0'; - } - - - return $sum; - } - - /** - * @param Collection $accounts - * @param array $types - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - private function sumInPeriodWithoutCategory(Collection $accounts, array $types, Carbon $start, Carbon $end): string - { - $query = $this->user->transactionJournals() - ->distinct() - ->transactionTypes($types) - ->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->leftJoin( - 'transactions as t', function (JoinClause $join) { - $join->on('t.transaction_journal_id', '=', 'transaction_journals.id')->where('amount', '<', 0); - } - ) - ->leftJoin('category_transaction', 't.id', '=', 'category_transaction.transaction_id') - ->whereNull('category_transaction_journal.id') - ->whereNull('category_transaction.id') - ->before($end) - ->after($start); - - if ($accounts->count() > 0) { - $accountIds = $accounts->pluck('id')->toArray(); - - $query->whereIn('t.account_id', $accountIds); - } - $sum = strval($query->sum('t.amount')); - - return $sum; - - } } From 5f153b8a010600af8260f48e9dcc59c2d93de004 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 08:45:01 +0100 Subject: [PATCH 509/709] Fix a test --- tests/acceptance/Controllers/BillControllerTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/acceptance/Controllers/BillControllerTest.php b/tests/acceptance/Controllers/BillControllerTest.php index efce584e0b..60a8388697 100644 --- a/tests/acceptance/Controllers/BillControllerTest.php +++ b/tests/acceptance/Controllers/BillControllerTest.php @@ -124,11 +124,11 @@ class BillControllerTest extends TestCase $data = [ 'name' => 'New Bill ' . rand(1000, 9999), 'match' => 'some words', - 'amount_min' => 100, + 'amount_min' => '100', 'amount_currency_id_amount_min' => 1, 'amount_currency_id_amount_max' => 1, 'skip' => 0, - 'amount_max' => 100, + 'amount_max' => '100', 'date' => '2016-01-01', 'repeat_freq' => 'monthly', ]; @@ -154,11 +154,11 @@ class BillControllerTest extends TestCase $data = [ 'name' => 'Updated Bill ' . rand(1000, 9999), 'match' => 'some more words', - 'amount_min' => 100, + 'amount_min' => '100', 'amount_currency_id_amount_min' => 1, 'amount_currency_id_amount_max' => 1, 'skip' => 0, - 'amount_max' => 100, + 'amount_max' => '100', 'date' => '2016-01-01', 'repeat_freq' => 'monthly', ]; From 57f63ba752992058f2e5b1a24c548ce790d81174 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 08:45:10 +0100 Subject: [PATCH 510/709] Clean up class #524 --- app/Repositories/Category/CategoryRepository.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index f415a7c40a..90ccc0a30a 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -14,14 +14,12 @@ declare(strict_types = 1); namespace FireflyIII\Repositories\Category; use Carbon\Carbon; -use DB; use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Models\Category; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\User; -use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; use Log; use Navigation; From 452c14bececfe4e424e63e2d2b705734454f324a Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 08:47:09 +0100 Subject: [PATCH 511/709] Refactor method to original name #524 --- app/Http/Controllers/CategoryController.php | 2 +- app/Http/Controllers/Chart/CategoryController.php | 4 ++-- app/Repositories/Category/CategoryRepository.php | 2 +- app/Repositories/Category/CategoryRepositoryInterface.php | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index 44d25e47f1..cf1404f0dd 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -346,7 +346,7 @@ class CategoryController extends Controller $end = Navigation::startOfPeriod($end, $range); $currentEnd = Navigation::endOfPeriod($end, $range); $spent = $repository->spentInPeriodCollector(new Collection([$category]), $accounts, $end, $currentEnd); - $earned = $repository->earnedInPeriodCollector(new Collection([$category]), $accounts, $end, $currentEnd); + $earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $end, $currentEnd); $dateStr = $end->format('Y-m-d'); $dateName = Navigation::periodShow($end, $range); $entries->push([$dateStr, $dateName, $spent, $earned]); diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 8d7c04c7fe..15c807412f 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -91,7 +91,7 @@ class CategoryController extends Controller while ($start <= $end) { $currentEnd = Navigation::endOfPeriod($start, $range); $spent = $repository->spentInPeriodCollector(new Collection([$category]), $accounts, $start, $currentEnd); - $earned = $repository->earnedInPeriodCollector(new Collection([$category]), $accounts, $start, $currentEnd); + $earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $currentEnd); $label = Navigation::periodShow($start, $range); $chartData[0]['entries'][$label] = bcmul($spent, '-1'); $chartData[1]['entries'][$label] = $earned; @@ -316,7 +316,7 @@ class CategoryController extends Controller while ($start <= $end) { $spent = $repository->spentInPeriodCollector(new Collection([$category]), $accounts, $start, $start); - $earned = $repository->earnedInPeriodCollector(new Collection([$category]), $accounts, $start, $start); + $earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $start); $label = Navigation::periodShow($start, '1D'); $chartData[0]['entries'][$label] = bcmul($spent, '-1'); diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 90ccc0a30a..8d4d7ed1ec 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -64,7 +64,7 @@ class CategoryRepository implements CategoryRepositoryInterface * * @return string */ - public function earnedInPeriodCollector(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string + public function earnedInPeriod(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string { /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class, [$this->user]); diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index 61f4810f3a..66bfe14a19 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -39,7 +39,7 @@ interface CategoryRepositoryInterface * * @return string */ - public function earnedInPeriodCollector(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string; + public function earnedInPeriod(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string; /** * Find a category From 7c5ee8a67d4c844412a2839396bce7ae366c5b71 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 08:47:45 +0100 Subject: [PATCH 512/709] Refactor method to original name #524 --- app/Http/Controllers/CategoryController.php | 2 +- app/Http/Controllers/Chart/CategoryController.php | 6 +++--- app/Http/Controllers/Report/CategoryController.php | 2 +- app/Repositories/Category/CategoryRepository.php | 2 +- app/Repositories/Category/CategoryRepositoryInterface.php | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index cf1404f0dd..a8a17ea00c 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -345,7 +345,7 @@ class CategoryController extends Controller while ($end >= $first) { $end = Navigation::startOfPeriod($end, $range); $currentEnd = Navigation::endOfPeriod($end, $range); - $spent = $repository->spentInPeriodCollector(new Collection([$category]), $accounts, $end, $currentEnd); + $spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $end, $currentEnd); $earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $end, $currentEnd); $dateStr = $end->format('Y-m-d'); $dateName = Navigation::periodShow($end, $range); diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 15c807412f..5e8240e116 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -90,7 +90,7 @@ class CategoryController extends Controller while ($start <= $end) { $currentEnd = Navigation::endOfPeriod($start, $range); - $spent = $repository->spentInPeriodCollector(new Collection([$category]), $accounts, $start, $currentEnd); + $spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $currentEnd); $earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $currentEnd); $label = Navigation::periodShow($start, $range); $chartData[0]['entries'][$label] = bcmul($spent, '-1'); @@ -143,7 +143,7 @@ class CategoryController extends Controller $accounts = $accountRepository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]); /** @var Category $category */ foreach ($categories as $category) { - $spent = $repository->spentInPeriodCollector(new Collection([$category]), $accounts, $start, $end); + $spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $end); if (bccomp($spent, '0') === -1) { $chartData[$category->name] = bcmul($spent, '-1'); } @@ -315,7 +315,7 @@ class CategoryController extends Controller ]; while ($start <= $end) { - $spent = $repository->spentInPeriodCollector(new Collection([$category]), $accounts, $start, $start); + $spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $start); $earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $start, $start); $label = Navigation::periodShow($start, '1D'); diff --git a/app/Http/Controllers/Report/CategoryController.php b/app/Http/Controllers/Report/CategoryController.php index 2426dd6ea6..730deeaece 100644 --- a/app/Http/Controllers/Report/CategoryController.php +++ b/app/Http/Controllers/Report/CategoryController.php @@ -123,7 +123,7 @@ class CategoryController extends Controller $report = []; /** @var Category $category */ foreach ($categories as $category) { - $spent = $repository->spentInPeriodCollector(new Collection([$category]), $accounts, $start, $end); + $spent = $repository->spentInPeriod(new Collection([$category]), $accounts, $start, $end); if (bccomp($spent, '0') !== 0) { $report[$category->id] = ['name' => $category->name, 'spent' => $spent]; } diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 8d4d7ed1ec..637e31dccd 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -392,7 +392,7 @@ class CategoryRepository implements CategoryRepositoryInterface * * @return string */ - public function spentInPeriodCollector(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string + public function spentInPeriod(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string { /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class, [$this->user]); diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index 66bfe14a19..3bc70a9459 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -129,7 +129,7 @@ interface CategoryRepositoryInterface * * @return string */ - public function spentInPeriodCollector(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string; + public function spentInPeriod(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string; /** * @param Collection $accounts From b575b87f77a19fd614bf2cdae9e2cc14e1986207 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 08:48:16 +0100 Subject: [PATCH 513/709] Refactor method to original name #524 --- app/Http/Controllers/Chart/CategoryController.php | 2 +- app/Repositories/Category/CategoryRepository.php | 2 +- app/Repositories/Category/CategoryRepositoryInterface.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 5e8240e116..217d122bc8 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -149,7 +149,7 @@ class CategoryController extends Controller } } - $chartData[strval(trans('firefly.no_category'))] = bcmul($repository->spentInPeriodWithoutCategoryCollector(new Collection, $start, $end), '-1'); + $chartData[strval(trans('firefly.no_category'))] = bcmul($repository->spentInPeriodWithoutCategory(new Collection, $start, $end), '-1'); // sort arsort($chartData); diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 637e31dccd..016b9a8f19 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -410,7 +410,7 @@ class CategoryRepository implements CategoryRepositoryInterface * * @return string */ - public function spentInPeriodWithoutCategoryCollector(Collection $accounts, Carbon $start, Carbon $end): string + public function spentInPeriodWithoutCategory(Collection $accounts, Carbon $start, Carbon $end): string { /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class, [$this->user]); diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index 3bc70a9459..1194fbbf74 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -138,7 +138,7 @@ interface CategoryRepositoryInterface * * @return string */ - public function spentInPeriodWithoutCategoryCollector(Collection $accounts, Carbon $start, Carbon $end): string; + public function spentInPeriodWithoutCategory(Collection $accounts, Carbon $start, Carbon $end): string; /** * @param array $data From a609a471386a3853c47d88c9709332257839547c Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 08:52:45 +0100 Subject: [PATCH 514/709] Fix tests. --- .../Controllers/Transaction/SingleControllerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/acceptance/Controllers/Transaction/SingleControllerTest.php b/tests/acceptance/Controllers/Transaction/SingleControllerTest.php index 172aa7d0d4..beae215fcd 100644 --- a/tests/acceptance/Controllers/Transaction/SingleControllerTest.php +++ b/tests/acceptance/Controllers/Transaction/SingleControllerTest.php @@ -94,7 +94,7 @@ class SingleControllerTest extends TestCase $data = [ 'what' => 'withdrawal', - 'amount' => 10, + 'amount' => '10', 'amount_currency_id_amount' => 1, 'source_account_id' => 1, 'destination_account_name' => 'Some destination', @@ -119,7 +119,7 @@ class SingleControllerTest extends TestCase 'description' => 'Updated groceries', 'source_account_id' => 1, 'destination_account_name' => 'PLUS', - 'amount' => 123, + 'amount' => '123', 'amount_currency_id_amount' => 1, 'budget_id' => 1, 'category' => 'Daily groceries', From 6fe28b15dfb831f57609bb25a00ec2528c049b3b Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 08:56:14 +0100 Subject: [PATCH 515/709] Add some phpdoc [skip ci] --- app/Http/Controllers/BudgetController.php | 1 + app/Http/Controllers/CategoryController.php | 1 + 2 files changed, 2 insertions(+) diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index 77f9f5cd64..fce86e46a1 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -397,6 +397,7 @@ class BudgetController extends Controller private function collectBudgetInformation(Collection $budgets, Carbon $start, Carbon $end): array { // get account information + /** @var AccountRepositoryInterface $accountRepository */ $accountRepository = app(AccountRepositoryInterface::class); $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]); $return = []; diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index a8a17ea00c..3eac58d095 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -321,6 +321,7 @@ class CategoryController extends Controller { /** @var CategoryRepositoryInterface $repository */ $repository = app(CategoryRepositoryInterface::class); + /** @var AccountRepositoryInterface $accountRepository */ $accountRepository = app(AccountRepositoryInterface::class); $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); $first = $repository->firstUseDate($category); From 3ef569d280ffd7965dec88831abacc072776263f Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 09:04:12 +0100 Subject: [PATCH 516/709] Respond to empty account collection #524 --- .../Category/CategoryRepository.php | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 016b9a8f19..32c2eb2135 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -396,7 +396,17 @@ class CategoryRepository implements CategoryRepositoryInterface { /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class, [$this->user]); - $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($accounts)->setCategories($categories); + $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setCategories($categories); + + + if ($accounts->count() > 0) { + $collector->setAccounts($accounts); + } + if ($accounts->count() === 0) { + $collector->setAllAssetAccounts(); + } + + $set = $collector->getJournals(); $sum = strval($set->sum('transaction_amount')); @@ -414,7 +424,15 @@ class CategoryRepository implements CategoryRepositoryInterface { /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class, [$this->user]); - $collector->setRange($start, $end)->setAccounts($accounts)->setTypes([TransactionType::WITHDRAWAL])->withoutCategory(); + $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->withoutCategory(); + + if ($accounts->count() > 0) { + $collector->setAccounts($accounts); + } + if ($accounts->count() === 0) { + $collector->setAllAssetAccounts(); + } + $set = $collector->getJournals(); $set = $set->filter( function (Transaction $transaction) { From 4241ae035e696ee509a338cc7feb22455e5a39fd Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 09:07:04 +0100 Subject: [PATCH 517/709] =?UTF-8?q?Add=20two=20new=20=E2=80=9CspentInPerio?= =?UTF-8?q?d=E2=80=9D=20methods=20that=20use=20the=20collector=20and=20not?= =?UTF-8?q?=20big=20queries.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Helpers/Report/BalanceReportHelper.php | 4 +- app/Helpers/Report/BudgetReportHelper.php | 6 +- app/Http/Controllers/BudgetController.php | 6 +- .../Controllers/Chart/BudgetController.php | 8 +-- app/Repositories/Budget/BudgetRepository.php | 65 ++++++++++++++++++- .../Budget/BudgetRepositoryInterface.php | 19 ++++++ 6 files changed, 94 insertions(+), 14 deletions(-) diff --git a/app/Helpers/Report/BalanceReportHelper.php b/app/Helpers/Report/BalanceReportHelper.php index b1d53731e7..0b14e18f91 100644 --- a/app/Helpers/Report/BalanceReportHelper.php +++ b/app/Helpers/Report/BalanceReportHelper.php @@ -158,9 +158,7 @@ class BalanceReportHelper implements BalanceReportHelperInterface foreach ($accounts as $account) { $balanceEntry = new BalanceEntry; $balanceEntry->setAccount($account); - $spent = $this->budgetRepository->spentInPeriod( - new Collection([$budgetLimit->budget]), new Collection([$account]), $budgetLimit->start_date, $budgetLimit->end_date - ); + $spent = $this->budgetRepository->spentInPeriodCollector(new Collection([$budgetLimit->budget]), new Collection([$account]), $budgetLimit->start_date, $budgetLimit->end_date); $balanceEntry->setSpent($spent); $line->addBalanceEntry($balanceEntry); } diff --git a/app/Helpers/Report/BudgetReportHelper.php b/app/Helpers/Report/BudgetReportHelper.php index cf6836ce96..f5ad6285dd 100644 --- a/app/Helpers/Report/BudgetReportHelper.php +++ b/app/Helpers/Report/BudgetReportHelper.php @@ -59,7 +59,7 @@ class BudgetReportHelper implements BudgetReportHelperInterface foreach ($set as $budget) { $budgetLimits = $this->repository->getBudgetLimits($budget, $start, $end); if ($budgetLimits->count() == 0) { // no budget limit(s) for this budget - $spent = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end);// spent for budget in time range + $spent = $this->repository->spentInPeriodCollector(new Collection([$budget]), $accounts, $start, $end);// spent for budget in time range if ($spent > 0) { $budgetLine = new BudgetLine; $budgetLine->setBudget($budget)->setOverspent($spent); @@ -104,7 +104,7 @@ class BudgetReportHelper implements BudgetReportHelperInterface $set = new Collection; /** @var Budget $budget */ foreach ($budgets as $budget) { - $total = $repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end); + $total = $repository->spentInPeriodCollector(new Collection([$budget]), $accounts, $start, $end); if (bccomp($total, '0') === -1) { $set->push($budget); } @@ -128,7 +128,7 @@ class BudgetReportHelper implements BudgetReportHelperInterface private function calculateExpenses(Budget $budget, BudgetLimit $budgetLimit, Collection $accounts): array { $array = []; - $expenses = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $budgetLimit->start_date, $budgetLimit->end_date); + $expenses = $this->repository->spentInPeriodCollector(new Collection([$budget]), $accounts, $budgetLimit->start_date, $budgetLimit->end_date); $array['left'] = bccomp(bcadd($budgetLimit->amount, $expenses), '0') === 1 ? bcadd($budgetLimit->amount, $expenses) : '0'; $array['spent'] = bccomp(bcadd($budgetLimit->amount, $expenses), '0') === 1 ? $expenses : '0'; $array['overspent'] = bccomp(bcadd($budgetLimit->amount, $expenses), '0') === 1 ? '0' : bcadd($expenses, $budgetLimit->amount); diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index fce86e46a1..b3261a066b 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -268,7 +268,7 @@ class BudgetController extends Controller /** @var BudgetLimit $entry */ foreach ($set as $entry) { - $entry->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $entry->start_date, $entry->end_date); + $entry->spent = $repository->spentInPeriodCollector(new Collection([$budget]), $accounts, $entry->start_date, $entry->end_date); $limits->push($entry); } @@ -313,7 +313,7 @@ class BudgetController extends Controller $journals->setPath('/budgets/show/' . $budget->id . '/' . $budgetLimit->id); - $budgetLimit->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $budgetLimit->start_date, $budgetLimit->end_date); + $budgetLimit->spent = $repository->spentInPeriodCollector(new Collection([$budget]), $accounts, $budgetLimit->start_date, $budgetLimit->end_date); $limits = new Collection([$budgetLimit]); return view('budgets.show', compact('limits', 'budget', 'budgetLimit', 'journals', 'subTitle')); @@ -405,7 +405,7 @@ class BudgetController extends Controller foreach ($budgets as $budget) { $budgetId = $budget->id; $return[$budgetId] = [ - 'spent' => $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end), + 'spent' => $this->repository->spentInPeriodCollector(new Collection([$budget]), $accounts, $start, $end), 'budgeted' => '0', 'currentRep' => false, ]; diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index 0a8fe543cf..f2dd6ea111 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -95,7 +95,7 @@ class BudgetController extends Controller $currentEnd = Navigation::endOfPeriod($first, $range); // sub another day because reasons. $currentEnd->subDay(); - $spent = $this->repository->spentInPeriod($budgetCollection, new Collection, $currentStart, $currentEnd); + $spent = $this->repository->spentInPeriodCollector($budgetCollection, new Collection, $currentStart, $currentEnd); $format = Navigation::periodShow($first, $range); $entries[$format] = bcmul($spent, '-1'); $first = Navigation::addPeriod($first, $range, 0); @@ -140,7 +140,7 @@ class BudgetController extends Controller $amount = $budgetLimit->amount; $budgetCollection = new Collection([$budget]); while ($start <= $end) { - $spent = $this->repository->spentInPeriod($budgetCollection, new Collection, $start, $start); + $spent = $this->repository->spentInPeriodCollector($budgetCollection, new Collection, $start, $start); $amount = bcadd($amount, $spent); $format = $start->formatLocalized(strval(trans('config.month_and_day'))); $entries[$format] = $amount; @@ -329,7 +329,7 @@ class BudgetController extends Controller { $return = []; if ($limits->count() === 0) { - $spent = $this->repository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end); + $spent = $this->repository->spentInPeriodCollector(new Collection([$budget]), new Collection, $start, $end); if (bccomp($spent, '0') !== 0) { $return[$budget->name]['spent'] = bcmul($spent, '-1'); $return[$budget->name]['left'] = 0; @@ -375,7 +375,7 @@ class BudgetController extends Controller $name = $budget->name; /** @var BudgetLimit $budgetLimit */ foreach ($limits as $budgetLimit) { - $expenses = $this->repository->spentInPeriod(new Collection([$budget]), new Collection, $budgetLimit->start_date, $budgetLimit->end_date); + $expenses = $this->repository->spentInPeriodCollector(new Collection([$budget]), new Collection, $budgetLimit->start_date, $budgetLimit->end_date); if ($limits->count() > 1) { $name = $budget->name . ' ' . trans( diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index d272982654..ff3e0b069b 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -291,7 +291,7 @@ class BudgetRepository implements BudgetRepositoryInterface } ); } - )->orderBy('budget_limits.start_date','DESC')->get(['budget_limits.*']); + )->orderBy('budget_limits.start_date', 'DESC')->get(['budget_limits.*']); return $set; } @@ -521,6 +521,33 @@ class BudgetRepository implements BudgetRepositoryInterface return bcadd($first, $second); } + /** + * @param Collection $budgets + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function spentInPeriodCollector(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): string + { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class, [$this->user]); + $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setBudgets($budgets); + + if($accounts->count() > 0) { + $collector->setAccounts($accounts); + } + if($accounts->count() === 0) { + $collector->setAllAssetAccounts(); + } + + $set = $collector->getJournals(); + $sum = strval($set->sum('transaction_amount')); + + return $sum; + } + /** * @param Collection $accounts * @param Carbon $start @@ -663,4 +690,40 @@ class BudgetRepository implements BudgetRepositoryInterface return $limit; } + + /** + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function spentInPeriodWithoutBudgetCollector(Collection $accounts, Carbon $start, Carbon $end): string + { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class, [$this->user]); + $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->withoutBudget(); + + if ($accounts->count() > 0) { + $collector->setAccounts($accounts); + } + if ($accounts->count() === 0) { + $collector->setAllAssetAccounts(); + } + + $set = $collector->getJournals(); + $set = $set->filter( + function (Transaction $transaction) { + if (bccomp($transaction->transaction_amount, '0') === -1) { + return $transaction; + } + + return null; + } + ); + + $sum = strval($set->sum('transaction_amount')); + + return $sum; + } } diff --git a/app/Repositories/Budget/BudgetRepositoryInterface.php b/app/Repositories/Budget/BudgetRepositoryInterface.php index 6ccb7da0bf..4a0f453d11 100644 --- a/app/Repositories/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetRepositoryInterface.php @@ -159,6 +159,16 @@ interface BudgetRepositoryInterface */ public function spentInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): string; + /** + * @param Collection $budgets + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function spentInPeriodCollector(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): string; + /** * @param Collection $accounts * @param Carbon $start @@ -168,6 +178,15 @@ interface BudgetRepositoryInterface */ public function spentInPeriodWithoutBudget(Collection $accounts, Carbon $start, Carbon $end): string; + /** + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function spentInPeriodWithoutBudgetCollector(Collection $accounts, Carbon $start, Carbon $end): string; + /** * @param array $data * From 8e8b0115876b4d43097a852da6f7c2c7957b26a2 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 09:07:56 +0100 Subject: [PATCH 518/709] Removed unused budget methods. --- app/Repositories/Budget/BudgetRepository.php | 201 ++---------------- .../Budget/BudgetRepositoryInterface.php | 19 -- 2 files changed, 23 insertions(+), 197 deletions(-) diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index ff3e0b069b..bd7e0b6fe5 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -24,9 +24,7 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\User; use Illuminate\Database\Eloquent\Builder; -use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; -use Log; use Navigation; use stdClass; @@ -435,92 +433,6 @@ class BudgetRepository implements BudgetRepositoryInterface return true; } - /** - * @param Collection $budgets - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - public function spentInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): string - { - // collect amount of transaction journals, which is easy: - $budgetIds = $budgets->pluck('id')->toArray(); - - $accountIds = $accounts->pluck('id')->toArray(); - - Log::debug(sprintf('spentInPeriod: Now in spentInPeriod for these budgets (%d): ', count($budgetIds)), $budgetIds); - Log::debug('spentInPeriod: and these accounts: ', $accountIds); - Log::debug(sprintf('spentInPeriod: Start date is "%s", end date is "%s"', $start->format('Y-m-d'), $end->format('Y-m-d'))); - - $fromJournalsQuery = TransactionJournal::leftJoin( - 'budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id' - ) - ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') - ->leftJoin( - 'transactions', function (JoinClause $join) { - $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where( - 'transactions.amount', '<', 0 - ); - } - ) - ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) - ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) - ->whereNull('transaction_journals.deleted_at') - ->whereNull('transactions.deleted_at') - ->where('transaction_journals.user_id', $this->user->id) - ->where('transaction_types.type', 'Withdrawal'); - - // add budgets: - if ($budgets->count() > 0) { - $fromJournalsQuery->whereIn('budget_transaction_journal.budget_id', $budgetIds); - } - - // add accounts: - if ($accounts->count() > 0) { - $fromJournalsQuery->whereIn('transactions.account_id', $accountIds); - } - $first = strval($fromJournalsQuery->sum('transactions.amount')); - Log::debug(sprintf('spentInPeriod: Result from first query: %s', $first)); - unset($fromJournalsQuery); - - // collect amount from transactions: - /** - * select transactions.id, budget_transaction.budget_id , transactions.amount - * - * - * and budget_transaction.budget_id in (1,61) - * and transactions.account_id in (2) - */ - $fromTransactionsQuery = Transaction::leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'transactions.id') - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') - ->whereNull('transactions.deleted_at') - ->whereNull('transaction_journals.deleted_at') - ->where('transactions.amount', '<', 0) - ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) - ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) - ->where('transaction_journals.user_id', $this->user->id) - ->where('transaction_types.type', 'Withdrawal'); - - // add budgets: - if ($budgets->count() > 0) { - $fromTransactionsQuery->whereIn('budget_transaction.budget_id', $budgetIds); - } - - // add accounts: - if ($accounts->count() > 0) { - $fromTransactionsQuery->whereIn('transactions.account_id', $accountIds); - } - $second = strval($fromTransactionsQuery->sum('transactions.amount')); - Log::debug(sprintf('spentInPeriod: Result from second query: %s', $second)); - - Log::debug(sprintf('spentInPeriod: FINAL: %s', bcadd($first, $second))); - - return bcadd($first, $second); - } - /** * @param Collection $budgets * @param Collection $accounts @@ -535,10 +447,10 @@ class BudgetRepository implements BudgetRepositoryInterface $collector = app(JournalCollectorInterface::class, [$this->user]); $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setBudgets($budgets); - if($accounts->count() > 0) { + if ($accounts->count() > 0) { $collector->setAccounts($accounts); } - if($accounts->count() === 0) { + if ($accounts->count() === 0) { $collector->setAllAssetAccounts(); } @@ -555,62 +467,31 @@ class BudgetRepository implements BudgetRepositoryInterface * * @return string */ - public function spentInPeriodWithoutBudget(Collection $accounts, Carbon $start, Carbon $end): string + public function spentInPeriodWithoutBudgetCollector(Collection $accounts, Carbon $start, Carbon $end): string { - $types = [TransactionType::WITHDRAWAL]; - $query = $this->user->transactionJournals() - ->distinct() - ->transactionTypes($types) - ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') - ->leftJoin( - 'transactions as source', function (JoinClause $join) { - $join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', 0); - } - ) - ->leftJoin( - 'transactions as destination', function (JoinClause $join) { - $join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')->where('destination.amount', '>', 0); - } - ) - ->leftJoin('budget_transaction', 'source.id', '=', 'budget_transaction.transaction_id') - ->whereNull('budget_transaction_journal.id') - ->whereNull('budget_transaction.id') - ->before($end) - ->after($start) - ->whereNull('source.deleted_at') - ->whereNull('destination.deleted_at') - ->where('transaction_journals.completed', 1); + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class, [$this->user]); + $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->withoutBudget(); if ($accounts->count() > 0) { - $accountIds = $accounts->pluck('id')->toArray(); - $query->where( - // source.account_id in accountIds XOR destination.account_id in accountIds - function (Builder $sourceXorDestinationQuery) use ($accountIds) { - $sourceXorDestinationQuery->where( - function (Builder $inSourceButNotDestinationQuery) use ($accountIds) { - $inSourceButNotDestinationQuery->whereIn('source.account_id', $accountIds) - ->whereNotIn('destination.account_id', $accountIds); - } - )->orWhere( - function (Builder $inDestinationButNotSourceQuery) use ($accountIds) { - $inDestinationButNotSourceQuery->whereIn('destination.account_id', $accountIds) - ->whereNotIn('source.account_id', $accountIds); - } - ); + $collector->setAccounts($accounts); + } + if ($accounts->count() === 0) { + $collector->setAllAssetAccounts(); + } + + $set = $collector->getJournals(); + $set = $set->filter( + function (Transaction $transaction) { + if (bccomp($transaction->transaction_amount, '0') === -1) { + return $transaction; } - ); - } - $ids = $query->get(['transaction_journals.id'])->pluck('id')->toArray(); - $sum = '0'; - if (count($ids) > 0) { - $sum = strval( - $this->user->transactions() - ->whereIn('transaction_journal_id', $ids) - ->where('amount', '<', '0') - ->whereNull('transactions.deleted_at') - ->sum('amount') - ); - } + + return null; + } + ); + + $sum = strval($set->sum('transaction_amount')); return $sum; } @@ -690,40 +571,4 @@ class BudgetRepository implements BudgetRepositoryInterface return $limit; } - - /** - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - public function spentInPeriodWithoutBudgetCollector(Collection $accounts, Carbon $start, Carbon $end): string - { - /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [$this->user]); - $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->withoutBudget(); - - if ($accounts->count() > 0) { - $collector->setAccounts($accounts); - } - if ($accounts->count() === 0) { - $collector->setAllAssetAccounts(); - } - - $set = $collector->getJournals(); - $set = $set->filter( - function (Transaction $transaction) { - if (bccomp($transaction->transaction_amount, '0') === -1) { - return $transaction; - } - - return null; - } - ); - - $sum = strval($set->sum('transaction_amount')); - - return $sum; - } } diff --git a/app/Repositories/Budget/BudgetRepositoryInterface.php b/app/Repositories/Budget/BudgetRepositoryInterface.php index 4a0f453d11..f9c287a3ad 100644 --- a/app/Repositories/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetRepositoryInterface.php @@ -149,16 +149,6 @@ interface BudgetRepositoryInterface */ public function setAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end, string $amount): bool; - /** - * @param Collection $budgets - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - public function spentInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): string; - /** * @param Collection $budgets * @param Collection $accounts @@ -169,15 +159,6 @@ interface BudgetRepositoryInterface */ public function spentInPeriodCollector(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): string; - /** - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - public function spentInPeriodWithoutBudget(Collection $accounts, Carbon $start, Carbon $end): string; - /** * @param Collection $accounts * @param Carbon $start From 2d8ca363db1993bfcaaa9b80f4bc6528b1396381 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 09:08:35 +0100 Subject: [PATCH 519/709] Gave method old name back. --- app/Helpers/Report/BalanceReportHelper.php | 2 +- app/Helpers/Report/BudgetReportHelper.php | 6 +++--- app/Http/Controllers/BudgetController.php | 6 +++--- app/Http/Controllers/Chart/BudgetController.php | 8 ++++---- app/Repositories/Budget/BudgetRepository.php | 2 +- app/Repositories/Budget/BudgetRepositoryInterface.php | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/Helpers/Report/BalanceReportHelper.php b/app/Helpers/Report/BalanceReportHelper.php index 0b14e18f91..c62d0e29d0 100644 --- a/app/Helpers/Report/BalanceReportHelper.php +++ b/app/Helpers/Report/BalanceReportHelper.php @@ -158,7 +158,7 @@ class BalanceReportHelper implements BalanceReportHelperInterface foreach ($accounts as $account) { $balanceEntry = new BalanceEntry; $balanceEntry->setAccount($account); - $spent = $this->budgetRepository->spentInPeriodCollector(new Collection([$budgetLimit->budget]), new Collection([$account]), $budgetLimit->start_date, $budgetLimit->end_date); + $spent = $this->budgetRepository->spentInPeriod(new Collection([$budgetLimit->budget]), new Collection([$account]), $budgetLimit->start_date, $budgetLimit->end_date); $balanceEntry->setSpent($spent); $line->addBalanceEntry($balanceEntry); } diff --git a/app/Helpers/Report/BudgetReportHelper.php b/app/Helpers/Report/BudgetReportHelper.php index f5ad6285dd..cf6836ce96 100644 --- a/app/Helpers/Report/BudgetReportHelper.php +++ b/app/Helpers/Report/BudgetReportHelper.php @@ -59,7 +59,7 @@ class BudgetReportHelper implements BudgetReportHelperInterface foreach ($set as $budget) { $budgetLimits = $this->repository->getBudgetLimits($budget, $start, $end); if ($budgetLimits->count() == 0) { // no budget limit(s) for this budget - $spent = $this->repository->spentInPeriodCollector(new Collection([$budget]), $accounts, $start, $end);// spent for budget in time range + $spent = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end);// spent for budget in time range if ($spent > 0) { $budgetLine = new BudgetLine; $budgetLine->setBudget($budget)->setOverspent($spent); @@ -104,7 +104,7 @@ class BudgetReportHelper implements BudgetReportHelperInterface $set = new Collection; /** @var Budget $budget */ foreach ($budgets as $budget) { - $total = $repository->spentInPeriodCollector(new Collection([$budget]), $accounts, $start, $end); + $total = $repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end); if (bccomp($total, '0') === -1) { $set->push($budget); } @@ -128,7 +128,7 @@ class BudgetReportHelper implements BudgetReportHelperInterface private function calculateExpenses(Budget $budget, BudgetLimit $budgetLimit, Collection $accounts): array { $array = []; - $expenses = $this->repository->spentInPeriodCollector(new Collection([$budget]), $accounts, $budgetLimit->start_date, $budgetLimit->end_date); + $expenses = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $budgetLimit->start_date, $budgetLimit->end_date); $array['left'] = bccomp(bcadd($budgetLimit->amount, $expenses), '0') === 1 ? bcadd($budgetLimit->amount, $expenses) : '0'; $array['spent'] = bccomp(bcadd($budgetLimit->amount, $expenses), '0') === 1 ? $expenses : '0'; $array['overspent'] = bccomp(bcadd($budgetLimit->amount, $expenses), '0') === 1 ? '0' : bcadd($expenses, $budgetLimit->amount); diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index b3261a066b..fce86e46a1 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -268,7 +268,7 @@ class BudgetController extends Controller /** @var BudgetLimit $entry */ foreach ($set as $entry) { - $entry->spent = $repository->spentInPeriodCollector(new Collection([$budget]), $accounts, $entry->start_date, $entry->end_date); + $entry->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $entry->start_date, $entry->end_date); $limits->push($entry); } @@ -313,7 +313,7 @@ class BudgetController extends Controller $journals->setPath('/budgets/show/' . $budget->id . '/' . $budgetLimit->id); - $budgetLimit->spent = $repository->spentInPeriodCollector(new Collection([$budget]), $accounts, $budgetLimit->start_date, $budgetLimit->end_date); + $budgetLimit->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $budgetLimit->start_date, $budgetLimit->end_date); $limits = new Collection([$budgetLimit]); return view('budgets.show', compact('limits', 'budget', 'budgetLimit', 'journals', 'subTitle')); @@ -405,7 +405,7 @@ class BudgetController extends Controller foreach ($budgets as $budget) { $budgetId = $budget->id; $return[$budgetId] = [ - 'spent' => $this->repository->spentInPeriodCollector(new Collection([$budget]), $accounts, $start, $end), + 'spent' => $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end), 'budgeted' => '0', 'currentRep' => false, ]; diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index f2dd6ea111..0a8fe543cf 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -95,7 +95,7 @@ class BudgetController extends Controller $currentEnd = Navigation::endOfPeriod($first, $range); // sub another day because reasons. $currentEnd->subDay(); - $spent = $this->repository->spentInPeriodCollector($budgetCollection, new Collection, $currentStart, $currentEnd); + $spent = $this->repository->spentInPeriod($budgetCollection, new Collection, $currentStart, $currentEnd); $format = Navigation::periodShow($first, $range); $entries[$format] = bcmul($spent, '-1'); $first = Navigation::addPeriod($first, $range, 0); @@ -140,7 +140,7 @@ class BudgetController extends Controller $amount = $budgetLimit->amount; $budgetCollection = new Collection([$budget]); while ($start <= $end) { - $spent = $this->repository->spentInPeriodCollector($budgetCollection, new Collection, $start, $start); + $spent = $this->repository->spentInPeriod($budgetCollection, new Collection, $start, $start); $amount = bcadd($amount, $spent); $format = $start->formatLocalized(strval(trans('config.month_and_day'))); $entries[$format] = $amount; @@ -329,7 +329,7 @@ class BudgetController extends Controller { $return = []; if ($limits->count() === 0) { - $spent = $this->repository->spentInPeriodCollector(new Collection([$budget]), new Collection, $start, $end); + $spent = $this->repository->spentInPeriod(new Collection([$budget]), new Collection, $start, $end); if (bccomp($spent, '0') !== 0) { $return[$budget->name]['spent'] = bcmul($spent, '-1'); $return[$budget->name]['left'] = 0; @@ -375,7 +375,7 @@ class BudgetController extends Controller $name = $budget->name; /** @var BudgetLimit $budgetLimit */ foreach ($limits as $budgetLimit) { - $expenses = $this->repository->spentInPeriodCollector(new Collection([$budget]), new Collection, $budgetLimit->start_date, $budgetLimit->end_date); + $expenses = $this->repository->spentInPeriod(new Collection([$budget]), new Collection, $budgetLimit->start_date, $budgetLimit->end_date); if ($limits->count() > 1) { $name = $budget->name . ' ' . trans( diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index bd7e0b6fe5..022c263a57 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -441,7 +441,7 @@ class BudgetRepository implements BudgetRepositoryInterface * * @return string */ - public function spentInPeriodCollector(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): string + public function spentInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): string { /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class, [$this->user]); diff --git a/app/Repositories/Budget/BudgetRepositoryInterface.php b/app/Repositories/Budget/BudgetRepositoryInterface.php index f9c287a3ad..66fe5e12e8 100644 --- a/app/Repositories/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetRepositoryInterface.php @@ -157,7 +157,7 @@ interface BudgetRepositoryInterface * * @return string */ - public function spentInPeriodCollector(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): string; + public function spentInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): string; /** * @param Collection $accounts From a872cf7061d23d2f73258eaefac845bc41e75142 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 09:10:04 +0100 Subject: [PATCH 520/709] Renamed the other method. --- app/Helpers/Report/BalanceReportHelper.php | 2 +- app/Helpers/Report/BudgetReportHelper.php | 2 +- app/Repositories/Budget/BudgetRepository.php | 2 +- app/Repositories/Budget/BudgetRepositoryInterface.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Helpers/Report/BalanceReportHelper.php b/app/Helpers/Report/BalanceReportHelper.php index c62d0e29d0..ca0667cf66 100644 --- a/app/Helpers/Report/BalanceReportHelper.php +++ b/app/Helpers/Report/BalanceReportHelper.php @@ -213,7 +213,7 @@ class BalanceReportHelper implements BalanceReportHelperInterface $empty = new BalanceLine; foreach ($accounts as $account) { - $spent = $this->budgetRepository->spentInPeriodWithoutBudget(new Collection([$account]), $start, $end); + $spent = $this->budgetRepository->spentInPeriodWoBudget(new Collection([$account]), $start, $end); // budget $budgetEntry = new BalanceEntry; $budgetEntry->setAccount($account); diff --git a/app/Helpers/Report/BudgetReportHelper.php b/app/Helpers/Report/BudgetReportHelper.php index cf6836ce96..dff9d99d74 100644 --- a/app/Helpers/Report/BudgetReportHelper.php +++ b/app/Helpers/Report/BudgetReportHelper.php @@ -80,7 +80,7 @@ class BudgetReportHelper implements BudgetReportHelperInterface } } - $noBudget = $this->repository->spentInPeriodWithoutBudget($accounts, $start, $end); // stuff outside of budgets + $noBudget = $this->repository->spentInPeriodWoBudget($accounts, $start, $end); // stuff outside of budgets $budgetLine = new BudgetLine; $budgetLine->setOverspent($noBudget)->setSpent($noBudget); $object->addOverspent($noBudget)->addBudgetLine($budgetLine); diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 022c263a57..7c8d2f6391 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -467,7 +467,7 @@ class BudgetRepository implements BudgetRepositoryInterface * * @return string */ - public function spentInPeriodWithoutBudgetCollector(Collection $accounts, Carbon $start, Carbon $end): string + public function spentInPeriodWoBudget(Collection $accounts, Carbon $start, Carbon $end): string { /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class, [$this->user]); diff --git a/app/Repositories/Budget/BudgetRepositoryInterface.php b/app/Repositories/Budget/BudgetRepositoryInterface.php index 66fe5e12e8..ee9eba1641 100644 --- a/app/Repositories/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetRepositoryInterface.php @@ -166,7 +166,7 @@ interface BudgetRepositoryInterface * * @return string */ - public function spentInPeriodWithoutBudgetCollector(Collection $accounts, Carbon $start, Carbon $end): string; + public function spentInPeriodWoBudget(Collection $accounts, Carbon $start, Carbon $end): string; /** * @param array $data From 4538ef3cf996c4c5232078d1ff79d66c7e0561f4 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 10:06:46 +0100 Subject: [PATCH 521/709] Various small optimisations [skip ci] --- app/Http/Controllers/AccountController.php | 2 +- app/Http/Controllers/AttachmentController.php | 5 +++-- app/Http/Controllers/PiggyBankController.php | 2 +- app/Http/Controllers/ProfileController.php | 21 +++++++++++-------- app/Support/Navigation.php | 9 ++++---- app/Support/Preferences.php | 2 +- 6 files changed, 22 insertions(+), 19 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 8549e18907..af0fd7b24f 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -65,7 +65,7 @@ class AccountController extends Controller /** * @param string $what * - * @return View + * @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory|View */ public function create(string $what = 'asset') { diff --git a/app/Http/Controllers/AttachmentController.php b/app/Http/Controllers/AttachmentController.php index 0b5d09d776..8febccf3e4 100644 --- a/app/Http/Controllers/AttachmentController.php +++ b/app/Http/Controllers/AttachmentController.php @@ -88,10 +88,11 @@ class AttachmentController extends Controller } /** - * @param Attachment $attachment + * @param AttachmentRepositoryInterface $repository + * @param Attachment $attachment * + * @return mixed * @throws FireflyException - * */ public function download(AttachmentRepositoryInterface $repository, Attachment $attachment) { diff --git a/app/Http/Controllers/PiggyBankController.php b/app/Http/Controllers/PiggyBankController.php index 3279328282..f67464f475 100644 --- a/app/Http/Controllers/PiggyBankController.php +++ b/app/Http/Controllers/PiggyBankController.php @@ -414,7 +414,7 @@ class PiggyBankController extends Controller * @param PiggyBankFormRequest $request * @param PiggyBank $piggyBank * - * @return $this + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ public function update(PiggyBankRepositoryInterface $repository, PiggyBankFormRequest $request, PiggyBank $piggyBank) { diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 30b0553d0b..aebec981cb 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -13,6 +13,7 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; +use FireflyIII\Exceptions\ValidationException; use FireflyIII\Http\Requests\DeleteAccountFormRequest; use FireflyIII\Http\Requests\ProfileFormRequest; use FireflyIII\Repositories\User\UserRepositoryInterface; @@ -114,9 +115,11 @@ class ProfileController extends Controller return redirect(route('profile.change-password')); } - $result = $this->validatePassword($request->get('current_password'), $request->get('new_password')); - if (!($result === true)) { - Session::flash('error', $result); + + try { + $this->validatePassword($request->get('current_password'), $request->get('new_password')); + } catch (ValidationException $e) { + Session::flash('error', $e->getMessage()); return redirect(route('profile.change-password')); } @@ -163,16 +166,16 @@ class ProfileController extends Controller } /** - * * @param string $old - * @param string $new1 + * @param string $new * - * @return string|bool + * @return bool + * @throws ValidationException */ - protected function validatePassword(string $old, string $new1) + protected function validatePassword(string $old, string $new): bool { - if ($new1 == $old) { - return trans('firefly.should_change'); + if ($new === $old) { + throw new ValidationException(strval(trans('firefly.should_change'))); } return true; diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php index 189b48437e..cbdeafbabc 100644 --- a/app/Support/Navigation.php +++ b/app/Support/Navigation.php @@ -129,12 +129,11 @@ class Navigation } /** + * @param \Carbon\Carbon $theCurrentEnd + * @param string $repeatFreq + * @param \Carbon\Carbon|null $maxDate * - * @param \Carbon\Carbon $theCurrentEnd - * @param $repeatFreq - * @param \Carbon\Carbon $maxDate - * - * @return \Carbon\Carbon + * @return Carbon */ public function endOfX(Carbon $theCurrentEnd, string $repeatFreq, Carbon $maxDate = null): Carbon { diff --git a/app/Support/Preferences.php b/app/Support/Preferences.php index 8507f3d806..999b8847ef 100644 --- a/app/Support/Preferences.php +++ b/app/Support/Preferences.php @@ -85,7 +85,7 @@ class Preferences /** * @param \FireflyIII\User $user * @param string $name - * @param string $default + * @param null|string $default * * @return \FireflyIII\Models\Preference|null */ From ac55b0fafb58c381617a9b366442e70339527c08 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 21:01:16 +0100 Subject: [PATCH 522/709] This should fix the print thing. [skip ci] --- public/css/firefly.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/public/css/firefly.css b/public/css/firefly.css index a09432b2c7..d7958dabc0 100644 --- a/public/css/firefly.css +++ b/public/css/firefly.css @@ -95,4 +95,10 @@ body.waiting * { .loading { background: url('/images/loading-small.gif') no-repeat center center; min-height: 30px; +} + +@media print { + a[href]:after { + content: none !important; + } } \ No newline at end of file From be52abbe3b13df423d310500e86e7fefd553801a Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 21:02:24 +0100 Subject: [PATCH 523/709] Add no-print tag to options box. [skip ci] --- resources/views/reports/audit/report.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/reports/audit/report.twig b/resources/views/reports/audit/report.twig index 762406c3c0..9d0c59843f 100644 --- a/resources/views/reports/audit/report.twig +++ b/resources/views/reports/audit/report.twig @@ -7,7 +7,7 @@ {% block content %} <!-- options block --> - <div class="row"> + <div class="row no-print"> <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12"> <div class="box"> <div class="box-header with-border"> From 3de52b6bc1a6f2f197e976e1769db69e5fbb4a84 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 21:05:34 +0100 Subject: [PATCH 524/709] Some new files for layout [skip ci] --- public/lib/adminlte/css/AdminLTE.css | 4932 +++++++++++++++++ .../adminlte/css/skins/skin-blue-light.css | 164 + public/lib/adminlte/js/app.js | 763 +++ resources/views/layout/default.twig | 2 +- 4 files changed, 5860 insertions(+), 1 deletion(-) create mode 100755 public/lib/adminlte/css/AdminLTE.css create mode 100755 public/lib/adminlte/css/skins/skin-blue-light.css create mode 100755 public/lib/adminlte/js/app.js diff --git a/public/lib/adminlte/css/AdminLTE.css b/public/lib/adminlte/css/AdminLTE.css new file mode 100755 index 0000000000..97ef7bec14 --- /dev/null +++ b/public/lib/adminlte/css/AdminLTE.css @@ -0,0 +1,4932 @@ +@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic); +/*! + * AdminLTE v2.3.7 + * Author: Almsaeed Studio + * Website: Almsaeed Studio <http://almsaeedstudio.com> + * License: Open source - MIT + * Please visit http://opensource.org/licenses/MIT for more information +!*/ +/* + * Core: General Layout Style + * ------------------------- + */ +html, +body { + min-height: 100%; +} +.layout-boxed html, +.layout-boxed body { + height: 100%; +} +body { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-family: 'Source Sans Pro', 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-weight: 400; + overflow-x: hidden; + overflow-y: auto; +} +/* Layout */ +.wrapper { + min-height: 100%; + position: relative; + overflow: hidden; +} +.wrapper:before, +.wrapper:after { + content: " "; + display: table; +} +.wrapper:after { + clear: both; +} +.layout-boxed .wrapper { + max-width: 1250px; + margin: 0 auto; + min-height: 100%; + box-shadow: 0 0 8px rgba(0, 0, 0, 0.5); + position: relative; +} +.layout-boxed { + background: url('../img/boxed-bg.jpg') repeat fixed; +} +/* + * Content Wrapper - contains the main content + * ```.right-side has been deprecated as of v2.0.0 in favor of .content-wrapper ``` + */ +.content-wrapper, +.right-side, +.main-footer { + -webkit-transition: -webkit-transform 0.3s ease-in-out, margin 0.3s ease-in-out; + -moz-transition: -moz-transform 0.3s ease-in-out, margin 0.3s ease-in-out; + -o-transition: -o-transform 0.3s ease-in-out, margin 0.3s ease-in-out; + transition: transform 0.3s ease-in-out, margin 0.3s ease-in-out; + margin-left: 230px; + z-index: 820; +} +.layout-top-nav .content-wrapper, +.layout-top-nav .right-side, +.layout-top-nav .main-footer { + margin-left: 0; +} +@media (max-width: 767px) { + .content-wrapper, + .right-side, + .main-footer { + margin-left: 0; + } +} +@media (min-width: 768px) { + .sidebar-collapse .content-wrapper, + .sidebar-collapse .right-side, + .sidebar-collapse .main-footer { + margin-left: 0; + } +} +@media (max-width: 767px) { + .sidebar-open .content-wrapper, + .sidebar-open .right-side, + .sidebar-open .main-footer { + -webkit-transform: translate(230px, 0); + -ms-transform: translate(230px, 0); + -o-transform: translate(230px, 0); + transform: translate(230px, 0); + } +} +.content-wrapper, +.right-side { + min-height: 100%; + background-color: #ecf0f5; + z-index: 800; +} +.main-footer { + background: #fff; + padding: 15px; + color: #444; + border-top: 1px solid #d2d6de; +} +/* Fixed layout */ +.fixed .main-header, +.fixed .main-sidebar, +.fixed .left-side { + position: fixed; +} +.fixed .main-header { + top: 0; + right: 0; + left: 0; +} +.fixed .content-wrapper, +.fixed .right-side { + padding-top: 50px; +} +@media (max-width: 767px) { + .fixed .content-wrapper, + .fixed .right-side { + padding-top: 100px; + } +} +.fixed.layout-boxed .wrapper { + max-width: 100%; +} +body.hold-transition .content-wrapper, +body.hold-transition .right-side, +body.hold-transition .main-footer, +body.hold-transition .main-sidebar, +body.hold-transition .left-side, +body.hold-transition .main-header .navbar, +body.hold-transition .main-header .logo { + /* Fix for IE */ + -webkit-transition: none; + -o-transition: none; + transition: none; +} +/* Content */ +.content { + min-height: 250px; + padding: 15px; + margin-right: auto; + margin-left: auto; + padding-left: 15px; + padding-right: 15px; +} +/* H1 - H6 font */ +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: 'Source Sans Pro', sans-serif; +} +/* General Links */ +a { + color: #3c8dbc; +} +a:hover, +a:active, +a:focus { + outline: none; + text-decoration: none; + color: #72afd2; +} +/* Page Header */ +.page-header { + margin: 10px 0 20px 0; + font-size: 22px; +} +.page-header > small { + color: #666; + display: block; + margin-top: 5px; +} +/* + * Component: Main Header + * ---------------------- + */ +.main-header { + position: relative; + max-height: 100px; + z-index: 1030; +} +.main-header .navbar { + -webkit-transition: margin-left 0.3s ease-in-out; + -o-transition: margin-left 0.3s ease-in-out; + transition: margin-left 0.3s ease-in-out; + margin-bottom: 0; + margin-left: 230px; + border: none; + min-height: 50px; + border-radius: 0; +} +.layout-top-nav .main-header .navbar { + margin-left: 0; +} +.main-header #navbar-search-input.form-control { + background: rgba(255, 255, 255, 0.2); + border-color: transparent; +} +.main-header #navbar-search-input.form-control:focus, +.main-header #navbar-search-input.form-control:active { + border-color: rgba(0, 0, 0, 0.1); + background: rgba(255, 255, 255, 0.9); +} +.main-header #navbar-search-input.form-control::-moz-placeholder { + color: #ccc; + opacity: 1; +} +.main-header #navbar-search-input.form-control:-ms-input-placeholder { + color: #ccc; +} +.main-header #navbar-search-input.form-control::-webkit-input-placeholder { + color: #ccc; +} +.main-header .navbar-custom-menu, +.main-header .navbar-right { + float: right; +} +@media (max-width: 991px) { + .main-header .navbar-custom-menu a, + .main-header .navbar-right a { + color: inherit; + background: transparent; + } +} +@media (max-width: 767px) { + .main-header .navbar-right { + float: none; + } + .navbar-collapse .main-header .navbar-right { + margin: 7.5px -15px; + } + .main-header .navbar-right > li { + color: inherit; + border: 0; + } +} +.main-header .sidebar-toggle { + float: left; + background-color: transparent; + background-image: none; + padding: 15px 15px; + font-family: fontAwesome; +} +.main-header .sidebar-toggle:before { + content: "\f0c9"; +} +.main-header .sidebar-toggle:hover { + color: #fff; +} +.main-header .sidebar-toggle:focus, +.main-header .sidebar-toggle:active { + background: transparent; +} +.main-header .sidebar-toggle .icon-bar { + display: none; +} +.main-header .navbar .nav > li.user > a > .fa, +.main-header .navbar .nav > li.user > a > .glyphicon, +.main-header .navbar .nav > li.user > a > .ion { + margin-right: 5px; +} +.main-header .navbar .nav > li > a > .label { + position: absolute; + top: 9px; + right: 7px; + text-align: center; + font-size: 9px; + padding: 2px 3px; + line-height: .9; +} +.main-header .logo { + -webkit-transition: width 0.3s ease-in-out; + -o-transition: width 0.3s ease-in-out; + transition: width 0.3s ease-in-out; + display: block; + float: left; + height: 50px; + font-size: 20px; + line-height: 50px; + text-align: center; + width: 230px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 0 15px; + font-weight: 300; + overflow: hidden; +} +.main-header .logo .logo-lg { + display: block; +} +.main-header .logo .logo-mini { + display: none; +} +.main-header .navbar-brand { + color: #fff; +} +.content-header { + position: relative; + padding: 15px 15px 0 15px; +} +.content-header > h1 { + margin: 0; + font-size: 24px; +} +.content-header > h1 > small { + font-size: 15px; + display: inline-block; + padding-left: 4px; + font-weight: 300; +} +.content-header > .breadcrumb { + float: right; + background: transparent; + margin-top: 0; + margin-bottom: 0; + font-size: 12px; + padding: 7px 5px; + position: absolute; + top: 15px; + right: 10px; + border-radius: 2px; +} +.content-header > .breadcrumb > li > a { + color: #444; + text-decoration: none; + display: inline-block; +} +.content-header > .breadcrumb > li > a > .fa, +.content-header > .breadcrumb > li > a > .glyphicon, +.content-header > .breadcrumb > li > a > .ion { + margin-right: 5px; +} +.content-header > .breadcrumb > li + li:before { + content: '>\00a0'; +} +@media (max-width: 991px) { + .content-header > .breadcrumb { + position: relative; + margin-top: 5px; + top: 0; + right: 0; + float: none; + background: #d2d6de; + padding-left: 10px; + } + .content-header > .breadcrumb li:before { + color: #97a0b3; + } +} +.navbar-toggle { + color: #fff; + border: 0; + margin: 0; + padding: 15px 15px; +} +@media (max-width: 991px) { + .navbar-custom-menu .navbar-nav > li { + float: left; + } + .navbar-custom-menu .navbar-nav { + margin: 0; + float: left; + } + .navbar-custom-menu .navbar-nav > li > a { + padding-top: 15px; + padding-bottom: 15px; + line-height: 20px; + } +} +@media (max-width: 767px) { + .main-header { + position: relative; + } + .main-header .logo, + .main-header .navbar { + width: 100%; + float: none; + } + .main-header .navbar { + margin: 0; + } + .main-header .navbar-custom-menu { + float: right; + } +} +@media (max-width: 991px) { + .navbar-collapse.pull-left { + float: none !important; + } + .navbar-collapse.pull-left + .navbar-custom-menu { + display: block; + position: absolute; + top: 0; + right: 40px; + } +} +/* + * Component: Sidebar + * ------------------ + */ +.main-sidebar, +.left-side { + position: absolute; + top: 0; + left: 0; + padding-top: 50px; + min-height: 100%; + width: 230px; + z-index: 810; + -webkit-transition: -webkit-transform 0.3s ease-in-out, width 0.3s ease-in-out; + -moz-transition: -moz-transform 0.3s ease-in-out, width 0.3s ease-in-out; + -o-transition: -o-transform 0.3s ease-in-out, width 0.3s ease-in-out; + transition: transform 0.3s ease-in-out, width 0.3s ease-in-out; +} +@media (max-width: 767px) { + .main-sidebar, + .left-side { + padding-top: 100px; + } +} +@media (max-width: 767px) { + .main-sidebar, + .left-side { + -webkit-transform: translate(-230px, 0); + -ms-transform: translate(-230px, 0); + -o-transform: translate(-230px, 0); + transform: translate(-230px, 0); + } +} +@media (min-width: 768px) { + .sidebar-collapse .main-sidebar, + .sidebar-collapse .left-side { + -webkit-transform: translate(-230px, 0); + -ms-transform: translate(-230px, 0); + -o-transform: translate(-230px, 0); + transform: translate(-230px, 0); + } +} +@media (max-width: 767px) { + .sidebar-open .main-sidebar, + .sidebar-open .left-side { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0); + } +} +.sidebar { + padding-bottom: 10px; +} +.sidebar-form input:focus { + border-color: transparent; +} +.user-panel { + position: relative; + width: 100%; + padding: 10px; + overflow: hidden; +} +.user-panel:before, +.user-panel:after { + content: " "; + display: table; +} +.user-panel:after { + clear: both; +} +.user-panel > .image > img { + width: 100%; + max-width: 45px; + height: auto; +} +.user-panel > .info { + padding: 5px 5px 5px 15px; + line-height: 1; + position: absolute; + left: 55px; +} +.user-panel > .info > p { + font-weight: 600; + margin-bottom: 9px; +} +.user-panel > .info > a { + text-decoration: none; + padding-right: 5px; + margin-top: 3px; + font-size: 11px; +} +.user-panel > .info > a > .fa, +.user-panel > .info > a > .ion, +.user-panel > .info > a > .glyphicon { + margin-right: 3px; +} +.sidebar-menu { + list-style: none; + margin: 0; + padding: 0; +} +.sidebar-menu > li { + position: relative; + margin: 0; + padding: 0; +} +.sidebar-menu > li > a { + padding: 12px 5px 12px 15px; + display: block; +} +.sidebar-menu > li > a > .fa, +.sidebar-menu > li > a > .glyphicon, +.sidebar-menu > li > a > .ion { + width: 20px; +} +.sidebar-menu > li .label, +.sidebar-menu > li .badge { + margin-right: 5px; +} +.sidebar-menu > li .badge { + margin-top: 3px; +} +.sidebar-menu li.header { + padding: 10px 25px 10px 15px; + font-size: 12px; +} +.sidebar-menu li > a > .fa-angle-left, +.sidebar-menu li > a > .pull-right-container > .fa-angle-left { + width: auto; + height: auto; + padding: 0; + margin-right: 10px; +} +.sidebar-menu li.active > a > .fa-angle-left, +.sidebar-menu li.active > a > .pull-right-container > .fa-angle-left { + -webkit-transform: rotate(-90deg); + -ms-transform: rotate(-90deg); + -o-transform: rotate(-90deg); + transform: rotate(-90deg); +} +.sidebar-menu li.active > .treeview-menu { + display: block; +} +.sidebar-menu .treeview-menu { + display: none; + list-style: none; + padding: 0; + margin: 0; + padding-left: 5px; +} +.sidebar-menu .treeview-menu .treeview-menu { + padding-left: 20px; +} +.sidebar-menu .treeview-menu > li { + margin: 0; +} +.sidebar-menu .treeview-menu > li > a { + padding: 5px 5px 5px 15px; + display: block; + font-size: 14px; +} +.sidebar-menu .treeview-menu > li > a > .fa, +.sidebar-menu .treeview-menu > li > a > .glyphicon, +.sidebar-menu .treeview-menu > li > a > .ion { + width: 20px; +} +.sidebar-menu .treeview-menu > li > a > .pull-right-container > .fa-angle-left, +.sidebar-menu .treeview-menu > li > a > .pull-right-container > .fa-angle-down, +.sidebar-menu .treeview-menu > li > a > .fa-angle-left, +.sidebar-menu .treeview-menu > li > a > .fa-angle-down { + width: auto; +} +/* + * Component: Sidebar Mini + */ +@media (min-width: 768px) { + .sidebar-mini.sidebar-collapse .content-wrapper, + .sidebar-mini.sidebar-collapse .right-side, + .sidebar-mini.sidebar-collapse .main-footer { + margin-left: 50px !important; + z-index: 840; + } + .sidebar-mini.sidebar-collapse .main-sidebar { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0); + width: 50px !important; + z-index: 850; + } + .sidebar-mini.sidebar-collapse .sidebar-menu > li { + position: relative; + } + .sidebar-mini.sidebar-collapse .sidebar-menu > li > a { + margin-right: 0; + } + .sidebar-mini.sidebar-collapse .sidebar-menu > li > a > span { + border-top-right-radius: 4px; + } + .sidebar-mini.sidebar-collapse .sidebar-menu > li:not(.treeview) > a > span { + border-bottom-right-radius: 4px; + } + .sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu { + padding-top: 5px; + padding-bottom: 5px; + border-bottom-right-radius: 4px; + } + .sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > a > span:not(.pull-right), + .sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > .treeview-menu { + display: block !important; + position: absolute; + width: 180px; + left: 50px; + } + .sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > a > span { + top: 0; + margin-left: -3px; + padding: 12px 5px 12px 20px; + background-color: inherit; + } + .sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > a > .pull-right-container { + float: right; + width: auto!important; + left: 200px!important; + top: 10px!important; + } + .sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > a > .pull-right-container > .label:not(:first-of-type) { + display: none; + } + .sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > .treeview-menu { + top: 44px; + margin-left: 0; + } + .sidebar-mini.sidebar-collapse .main-sidebar .user-panel > .info, + .sidebar-mini.sidebar-collapse .sidebar-form, + .sidebar-mini.sidebar-collapse .sidebar-menu > li > a > span, + .sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu, + .sidebar-mini.sidebar-collapse .sidebar-menu > li > a > .pull-right, + .sidebar-mini.sidebar-collapse .sidebar-menu li.header { + display: none !important; + -webkit-transform: translateZ(0); + } + .sidebar-mini.sidebar-collapse .main-header .logo { + width: 50px; + } + .sidebar-mini.sidebar-collapse .main-header .logo > .logo-mini { + display: block; + margin-left: -15px; + margin-right: -15px; + font-size: 18px; + } + .sidebar-mini.sidebar-collapse .main-header .logo > .logo-lg { + display: none; + } + .sidebar-mini.sidebar-collapse .main-header .navbar { + margin-left: 50px; + } +} +.sidebar-menu, +.main-sidebar .user-panel, +.sidebar-menu > li.header { + white-space: nowrap; + overflow: hidden; +} +.sidebar-menu:hover { + overflow: visible; +} +.sidebar-form, +.sidebar-menu > li.header { + overflow: hidden; + text-overflow: clip; +} +.sidebar-menu li > a { + position: relative; +} +.sidebar-menu li > a > .pull-right-container { + position: absolute; + right: 10px; + top: 50%; + margin-top: -7px; +} +/* + * Component: Control sidebar. By default, this is the right sidebar. + */ +.control-sidebar-bg { + position: fixed; + z-index: 1000; + bottom: 0; +} +.control-sidebar-bg, +.control-sidebar { + top: 0; + right: -230px; + width: 230px; + -webkit-transition: right 0.3s ease-in-out; + -o-transition: right 0.3s ease-in-out; + transition: right 0.3s ease-in-out; +} +.control-sidebar { + position: absolute; + padding-top: 50px; + z-index: 1010; +} +@media (max-width: 768px) { + .control-sidebar { + padding-top: 100px; + } +} +.control-sidebar > .tab-content { + padding: 10px 15px; +} +.control-sidebar.control-sidebar-open, +.control-sidebar.control-sidebar-open + .control-sidebar-bg { + right: 0; +} +.control-sidebar-open .control-sidebar-bg, +.control-sidebar-open .control-sidebar { + right: 0; +} +@media (min-width: 768px) { + .control-sidebar-open .content-wrapper, + .control-sidebar-open .right-side, + .control-sidebar-open .main-footer { + margin-right: 230px; + } +} +.nav-tabs.control-sidebar-tabs > li:first-of-type > a, +.nav-tabs.control-sidebar-tabs > li:first-of-type > a:hover, +.nav-tabs.control-sidebar-tabs > li:first-of-type > a:focus { + border-left-width: 0; +} +.nav-tabs.control-sidebar-tabs > li > a { + border-radius: 0; +} +.nav-tabs.control-sidebar-tabs > li > a, +.nav-tabs.control-sidebar-tabs > li > a:hover { + border-top: none; + border-right: none; + border-left: 1px solid transparent; + border-bottom: 1px solid transparent; +} +.nav-tabs.control-sidebar-tabs > li > a .icon { + font-size: 16px; +} +.nav-tabs.control-sidebar-tabs > li.active > a, +.nav-tabs.control-sidebar-tabs > li.active > a:hover, +.nav-tabs.control-sidebar-tabs > li.active > a:focus, +.nav-tabs.control-sidebar-tabs > li.active > a:active { + border-top: none; + border-right: none; + border-bottom: none; +} +@media (max-width: 768px) { + .nav-tabs.control-sidebar-tabs { + display: table; + } + .nav-tabs.control-sidebar-tabs > li { + display: table-cell; + } +} +.control-sidebar-heading { + font-weight: 400; + font-size: 16px; + padding: 10px 0; + margin-bottom: 10px; +} +.control-sidebar-subheading { + display: block; + font-weight: 400; + font-size: 14px; +} +.control-sidebar-menu { + list-style: none; + padding: 0; + margin: 0 -15px; +} +.control-sidebar-menu > li > a { + display: block; + padding: 10px 15px; +} +.control-sidebar-menu > li > a:before, +.control-sidebar-menu > li > a:after { + content: " "; + display: table; +} +.control-sidebar-menu > li > a:after { + clear: both; +} +.control-sidebar-menu > li > a > .control-sidebar-subheading { + margin-top: 0; +} +.control-sidebar-menu .menu-icon { + float: left; + width: 35px; + height: 35px; + border-radius: 50%; + text-align: center; + line-height: 35px; +} +.control-sidebar-menu .menu-info { + margin-left: 45px; + margin-top: 3px; +} +.control-sidebar-menu .menu-info > .control-sidebar-subheading { + margin: 0; +} +.control-sidebar-menu .menu-info > p { + margin: 0; + font-size: 11px; +} +.control-sidebar-menu .progress { + margin: 0; +} +.control-sidebar-dark { + color: #b8c7ce; +} +.control-sidebar-dark, +.control-sidebar-dark + .control-sidebar-bg { + background: #222d32; +} +.control-sidebar-dark .nav-tabs.control-sidebar-tabs { + border-bottom: #1c2529; +} +.control-sidebar-dark .nav-tabs.control-sidebar-tabs > li > a { + background: #181f23; + color: #b8c7ce; +} +.control-sidebar-dark .nav-tabs.control-sidebar-tabs > li > a, +.control-sidebar-dark .nav-tabs.control-sidebar-tabs > li > a:hover, +.control-sidebar-dark .nav-tabs.control-sidebar-tabs > li > a:focus { + border-left-color: #141a1d; + border-bottom-color: #141a1d; +} +.control-sidebar-dark .nav-tabs.control-sidebar-tabs > li > a:hover, +.control-sidebar-dark .nav-tabs.control-sidebar-tabs > li > a:focus, +.control-sidebar-dark .nav-tabs.control-sidebar-tabs > li > a:active { + background: #1c2529; +} +.control-sidebar-dark .nav-tabs.control-sidebar-tabs > li > a:hover { + color: #fff; +} +.control-sidebar-dark .nav-tabs.control-sidebar-tabs > li.active > a, +.control-sidebar-dark .nav-tabs.control-sidebar-tabs > li.active > a:hover, +.control-sidebar-dark .nav-tabs.control-sidebar-tabs > li.active > a:focus, +.control-sidebar-dark .nav-tabs.control-sidebar-tabs > li.active > a:active { + background: #222d32; + color: #fff; +} +.control-sidebar-dark .control-sidebar-heading, +.control-sidebar-dark .control-sidebar-subheading { + color: #fff; +} +.control-sidebar-dark .control-sidebar-menu > li > a:hover { + background: #1e282c; +} +.control-sidebar-dark .control-sidebar-menu > li > a .menu-info > p { + color: #b8c7ce; +} +.control-sidebar-light { + color: #5e5e5e; +} +.control-sidebar-light, +.control-sidebar-light + .control-sidebar-bg { + background: #f9fafc; + border-left: 1px solid #d2d6de; +} +.control-sidebar-light .nav-tabs.control-sidebar-tabs { + border-bottom: #d2d6de; +} +.control-sidebar-light .nav-tabs.control-sidebar-tabs > li > a { + background: #e8ecf4; + color: #444444; +} +.control-sidebar-light .nav-tabs.control-sidebar-tabs > li > a, +.control-sidebar-light .nav-tabs.control-sidebar-tabs > li > a:hover, +.control-sidebar-light .nav-tabs.control-sidebar-tabs > li > a:focus { + border-left-color: #d2d6de; + border-bottom-color: #d2d6de; +} +.control-sidebar-light .nav-tabs.control-sidebar-tabs > li > a:hover, +.control-sidebar-light .nav-tabs.control-sidebar-tabs > li > a:focus, +.control-sidebar-light .nav-tabs.control-sidebar-tabs > li > a:active { + background: #eff1f7; +} +.control-sidebar-light .nav-tabs.control-sidebar-tabs > li.active > a, +.control-sidebar-light .nav-tabs.control-sidebar-tabs > li.active > a:hover, +.control-sidebar-light .nav-tabs.control-sidebar-tabs > li.active > a:focus, +.control-sidebar-light .nav-tabs.control-sidebar-tabs > li.active > a:active { + background: #f9fafc; + color: #111; +} +.control-sidebar-light .control-sidebar-heading, +.control-sidebar-light .control-sidebar-subheading { + color: #111; +} +.control-sidebar-light .control-sidebar-menu { + margin-left: -14px; +} +.control-sidebar-light .control-sidebar-menu > li > a:hover { + background: #f4f4f5; +} +.control-sidebar-light .control-sidebar-menu > li > a .menu-info > p { + color: #5e5e5e; +} +/* + * Component: Dropdown menus + * ------------------------- + */ +/*Dropdowns in general*/ +.dropdown-menu { + box-shadow: none; + border-color: #eee; +} +.dropdown-menu > li > a { + color: #777; +} +.dropdown-menu > li > a > .glyphicon, +.dropdown-menu > li > a > .fa, +.dropdown-menu > li > a > .ion { + margin-right: 10px; +} +.dropdown-menu > li > a:hover { + background-color: #e1e3e9; + color: #333; +} +.dropdown-menu > .divider { + background-color: #eee; +} +.navbar-nav > .notifications-menu > .dropdown-menu, +.navbar-nav > .messages-menu > .dropdown-menu, +.navbar-nav > .tasks-menu > .dropdown-menu { + width: 280px; + padding: 0 0 0 0; + margin: 0; + top: 100%; +} +.navbar-nav > .notifications-menu > .dropdown-menu > li, +.navbar-nav > .messages-menu > .dropdown-menu > li, +.navbar-nav > .tasks-menu > .dropdown-menu > li { + position: relative; +} +.navbar-nav > .notifications-menu > .dropdown-menu > li.header, +.navbar-nav > .messages-menu > .dropdown-menu > li.header, +.navbar-nav > .tasks-menu > .dropdown-menu > li.header { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + background-color: #ffffff; + padding: 7px 10px; + border-bottom: 1px solid #f4f4f4; + color: #444444; + font-size: 14px; +} +.navbar-nav > .notifications-menu > .dropdown-menu > li.footer > a, +.navbar-nav > .messages-menu > .dropdown-menu > li.footer > a, +.navbar-nav > .tasks-menu > .dropdown-menu > li.footer > a { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + font-size: 12px; + background-color: #fff; + padding: 7px 10px; + border-bottom: 1px solid #eeeeee; + color: #444 !important; + text-align: center; +} +@media (max-width: 991px) { + .navbar-nav > .notifications-menu > .dropdown-menu > li.footer > a, + .navbar-nav > .messages-menu > .dropdown-menu > li.footer > a, + .navbar-nav > .tasks-menu > .dropdown-menu > li.footer > a { + background: #fff !important; + color: #444 !important; + } +} +.navbar-nav > .notifications-menu > .dropdown-menu > li.footer > a:hover, +.navbar-nav > .messages-menu > .dropdown-menu > li.footer > a:hover, +.navbar-nav > .tasks-menu > .dropdown-menu > li.footer > a:hover { + text-decoration: none; + font-weight: normal; +} +.navbar-nav > .notifications-menu > .dropdown-menu > li .menu, +.navbar-nav > .messages-menu > .dropdown-menu > li .menu, +.navbar-nav > .tasks-menu > .dropdown-menu > li .menu { + max-height: 200px; + margin: 0; + padding: 0; + list-style: none; + overflow-x: hidden; +} +.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a, +.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a, +.navbar-nav > .tasks-menu > .dropdown-menu > li .menu > li > a { + display: block; + white-space: nowrap; + /* Prevent text from breaking */ + border-bottom: 1px solid #f4f4f4; +} +.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a:hover, +.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a:hover, +.navbar-nav > .tasks-menu > .dropdown-menu > li .menu > li > a:hover { + background: #f4f4f4; + text-decoration: none; +} +.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a { + color: #444444; + overflow: hidden; + text-overflow: ellipsis; + padding: 10px; +} +.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a > .glyphicon, +.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a > .fa, +.navbar-nav > .notifications-menu > .dropdown-menu > li .menu > li > a > .ion { + width: 20px; +} +.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a { + margin: 0; + padding: 10px 10px; +} +.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a > div > img { + margin: auto 10px auto auto; + width: 40px; + height: 40px; +} +.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a > h4 { + padding: 0; + margin: 0 0 0 45px; + color: #444444; + font-size: 15px; + position: relative; +} +.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a > h4 > small { + color: #999999; + font-size: 10px; + position: absolute; + top: 0; + right: 0; +} +.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a > p { + margin: 0 0 0 45px; + font-size: 12px; + color: #888888; +} +.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a:before, +.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a:after { + content: " "; + display: table; +} +.navbar-nav > .messages-menu > .dropdown-menu > li .menu > li > a:after { + clear: both; +} +.navbar-nav > .tasks-menu > .dropdown-menu > li .menu > li > a { + padding: 10px; +} +.navbar-nav > .tasks-menu > .dropdown-menu > li .menu > li > a > h3 { + font-size: 14px; + padding: 0; + margin: 0 0 10px 0; + color: #666666; +} +.navbar-nav > .tasks-menu > .dropdown-menu > li .menu > li > a > .progress { + padding: 0; + margin: 0; +} +.navbar-nav > .user-menu > .dropdown-menu { + border-top-right-radius: 0; + border-top-left-radius: 0; + padding: 1px 0 0 0; + border-top-width: 0; + width: 280px; +} +.navbar-nav > .user-menu > .dropdown-menu, +.navbar-nav > .user-menu > .dropdown-menu > .user-body { + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.navbar-nav > .user-menu > .dropdown-menu > li.user-header { + height: 175px; + padding: 10px; + text-align: center; +} +.navbar-nav > .user-menu > .dropdown-menu > li.user-header > img { + z-index: 5; + height: 90px; + width: 90px; + border: 3px solid; + border-color: transparent; + border-color: rgba(255, 255, 255, 0.2); +} +.navbar-nav > .user-menu > .dropdown-menu > li.user-header > p { + z-index: 5; + color: #fff; + color: rgba(255, 255, 255, 0.8); + font-size: 17px; + margin-top: 10px; +} +.navbar-nav > .user-menu > .dropdown-menu > li.user-header > p > small { + display: block; + font-size: 12px; +} +.navbar-nav > .user-menu > .dropdown-menu > .user-body { + padding: 15px; + border-bottom: 1px solid #f4f4f4; + border-top: 1px solid #dddddd; +} +.navbar-nav > .user-menu > .dropdown-menu > .user-body:before, +.navbar-nav > .user-menu > .dropdown-menu > .user-body:after { + content: " "; + display: table; +} +.navbar-nav > .user-menu > .dropdown-menu > .user-body:after { + clear: both; +} +.navbar-nav > .user-menu > .dropdown-menu > .user-body a { + color: #444 !important; +} +@media (max-width: 991px) { + .navbar-nav > .user-menu > .dropdown-menu > .user-body a { + background: #fff !important; + color: #444 !important; + } +} +.navbar-nav > .user-menu > .dropdown-menu > .user-footer { + background-color: #f9f9f9; + padding: 10px; +} +.navbar-nav > .user-menu > .dropdown-menu > .user-footer:before, +.navbar-nav > .user-menu > .dropdown-menu > .user-footer:after { + content: " "; + display: table; +} +.navbar-nav > .user-menu > .dropdown-menu > .user-footer:after { + clear: both; +} +.navbar-nav > .user-menu > .dropdown-menu > .user-footer .btn-default { + color: #666666; +} +@media (max-width: 991px) { + .navbar-nav > .user-menu > .dropdown-menu > .user-footer .btn-default:hover { + background-color: #f9f9f9; + } +} +.navbar-nav > .user-menu .user-image { + float: left; + width: 25px; + height: 25px; + border-radius: 50%; + margin-right: 10px; + margin-top: -2px; +} +@media (max-width: 767px) { + .navbar-nav > .user-menu .user-image { + float: none; + margin-right: 0; + margin-top: -8px; + line-height: 10px; + } +} +/* Add fade animation to dropdown menus by appending + the class .animated-dropdown-menu to the .dropdown-menu ul (or ol)*/ +.open:not(.dropup) > .animated-dropdown-menu { + backface-visibility: visible !important; + -webkit-animation: flipInX 0.7s both; + -o-animation: flipInX 0.7s both; + animation: flipInX 0.7s both; +} +@keyframes flipInX { + 0% { + transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + transition-timing-function: ease-in; + opacity: 0; + } + 40% { + transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + transition-timing-function: ease-in; + } + 60% { + transform: perspective(400px) rotate3d(1, 0, 0, 10deg); + opacity: 1; + } + 80% { + transform: perspective(400px) rotate3d(1, 0, 0, -5deg); + } + 100% { + transform: perspective(400px); + } +} +@-webkit-keyframes flipInX { + 0% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); + -webkit-transition-timing-function: ease-in; + opacity: 0; + } + 40% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); + -webkit-transition-timing-function: ease-in; + } + 60% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg); + opacity: 1; + } + 80% { + -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg); + } + 100% { + -webkit-transform: perspective(400px); + } +} +/* Fix dropdown menu in navbars */ +.navbar-custom-menu > .navbar-nav > li { + position: relative; +} +.navbar-custom-menu > .navbar-nav > li > .dropdown-menu { + position: absolute; + right: 0; + left: auto; +} +@media (max-width: 991px) { + .navbar-custom-menu > .navbar-nav { + float: right; + } + .navbar-custom-menu > .navbar-nav > li { + position: static; + } + .navbar-custom-menu > .navbar-nav > li > .dropdown-menu { + position: absolute; + right: 5%; + left: auto; + border: 1px solid #ddd; + background: #fff; + } +} +/* + * Component: Form + * --------------- + */ +.form-control { + border-radius: 0; + box-shadow: none; + border-color: #d2d6de; +} +.form-control:focus { + border-color: #3c8dbc; + box-shadow: none; +} +.form-control::-moz-placeholder, +.form-control:-ms-input-placeholder, +.form-control::-webkit-input-placeholder { + color: #bbb; + opacity: 1; +} +.form-control:not(select) { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} +.form-group.has-success label { + color: #00a65a; +} +.form-group.has-success .form-control, +.form-group.has-success .input-group-addon { + border-color: #00a65a; + box-shadow: none; +} +.form-group.has-success .help-block { + color: #00a65a; +} +.form-group.has-warning label { + color: #f39c12; +} +.form-group.has-warning .form-control, +.form-group.has-warning .input-group-addon { + border-color: #f39c12; + box-shadow: none; +} +.form-group.has-warning .help-block { + color: #f39c12; +} +.form-group.has-error label { + color: #dd4b39; +} +.form-group.has-error .form-control, +.form-group.has-error .input-group-addon { + border-color: #dd4b39; + box-shadow: none; +} +.form-group.has-error .help-block { + color: #dd4b39; +} +/* Input group */ +.input-group .input-group-addon { + border-radius: 0; + border-color: #d2d6de; + background-color: #fff; +} +/* button groups */ +.btn-group-vertical .btn.btn-flat:first-of-type, +.btn-group-vertical .btn.btn-flat:last-of-type { + border-radius: 0; +} +.icheck > label { + padding-left: 0; +} +/* support Font Awesome icons in form-control */ +.form-control-feedback.fa { + line-height: 34px; +} +.input-lg + .form-control-feedback.fa, +.input-group-lg + .form-control-feedback.fa, +.form-group-lg .form-control + .form-control-feedback.fa { + line-height: 46px; +} +.input-sm + .form-control-feedback.fa, +.input-group-sm + .form-control-feedback.fa, +.form-group-sm .form-control + .form-control-feedback.fa { + line-height: 30px; +} +/* + * Component: Progress Bar + * ----------------------- + */ +.progress, +.progress > .progress-bar { + -webkit-box-shadow: none; + box-shadow: none; +} +.progress, +.progress > .progress-bar, +.progress .progress-bar, +.progress > .progress-bar .progress-bar { + border-radius: 1px; +} +/* size variation */ +.progress.sm, +.progress-sm { + height: 10px; +} +.progress.sm, +.progress-sm, +.progress.sm .progress-bar, +.progress-sm .progress-bar { + border-radius: 1px; +} +.progress.xs, +.progress-xs { + height: 7px; +} +.progress.xs, +.progress-xs, +.progress.xs .progress-bar, +.progress-xs .progress-bar { + border-radius: 1px; +} +.progress.xxs, +.progress-xxs { + height: 3px; +} +.progress.xxs, +.progress-xxs, +.progress.xxs .progress-bar, +.progress-xxs .progress-bar { + border-radius: 1px; +} +/* Vertical bars */ +.progress.vertical { + position: relative; + width: 30px; + height: 200px; + display: inline-block; + margin-right: 10px; +} +.progress.vertical > .progress-bar { + width: 100%; + position: absolute; + bottom: 0; +} +.progress.vertical.sm, +.progress.vertical.progress-sm { + width: 20px; +} +.progress.vertical.xs, +.progress.vertical.progress-xs { + width: 10px; +} +.progress.vertical.xxs, +.progress.vertical.progress-xxs { + width: 3px; +} +.progress-group .progress-text { + font-weight: 600; +} +.progress-group .progress-number { + float: right; +} +/* Remove margins from progress bars when put in a table */ +.table tr > td .progress { + margin: 0; +} +.progress-bar-light-blue, +.progress-bar-primary { + background-color: #3c8dbc; +} +.progress-striped .progress-bar-light-blue, +.progress-striped .progress-bar-primary { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-bar-green, +.progress-bar-success { + background-color: #00a65a; +} +.progress-striped .progress-bar-green, +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-bar-aqua, +.progress-bar-info { + background-color: #00c0ef; +} +.progress-striped .progress-bar-aqua, +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-bar-yellow, +.progress-bar-warning { + background-color: #f39c12; +} +.progress-striped .progress-bar-yellow, +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-bar-red, +.progress-bar-danger { + background-color: #dd4b39; +} +.progress-striped .progress-bar-red, +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +/* + * Component: Small Box + * -------------------- + */ +.small-box { + border-radius: 2px; + position: relative; + display: block; + margin-bottom: 20px; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); +} +.small-box > .inner { + padding: 10px; +} +.small-box > .small-box-footer { + position: relative; + text-align: center; + padding: 3px 0; + color: #fff; + color: rgba(255, 255, 255, 0.8); + display: block; + z-index: 10; + background: rgba(0, 0, 0, 0.1); + text-decoration: none; +} +.small-box > .small-box-footer:hover { + color: #fff; + background: rgba(0, 0, 0, 0.15); +} +.small-box h3 { + font-size: 38px; + font-weight: bold; + margin: 0 0 10px 0; + white-space: nowrap; + padding: 0; +} +.small-box p { + font-size: 15px; +} +.small-box p > small { + display: block; + color: #f9f9f9; + font-size: 13px; + margin-top: 5px; +} +.small-box h3, +.small-box p { + z-index: 5; +} +.small-box .icon { + -webkit-transition: all 0.3s linear; + -o-transition: all 0.3s linear; + transition: all 0.3s linear; + position: absolute; + top: -10px; + right: 10px; + z-index: 0; + font-size: 90px; + color: rgba(0, 0, 0, 0.15); +} +.small-box:hover { + text-decoration: none; + color: #f9f9f9; +} +.small-box:hover .icon { + font-size: 95px; +} +@media (max-width: 767px) { + .small-box { + text-align: center; + } + .small-box .icon { + display: none; + } + .small-box p { + font-size: 12px; + } +} +/* + * Component: Box + * -------------- + */ +.box { + position: relative; + border-radius: 3px; + background: #ffffff; + border-top: 3px solid #d2d6de; + margin-bottom: 20px; + width: 100%; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); +} +.box.box-primary { + border-top-color: #3c8dbc; +} +.box.box-info { + border-top-color: #00c0ef; +} +.box.box-danger { + border-top-color: #dd4b39; +} +.box.box-warning { + border-top-color: #f39c12; +} +.box.box-success { + border-top-color: #00a65a; +} +.box.box-default { + border-top-color: #d2d6de; +} +.box.collapsed-box .box-body, +.box.collapsed-box .box-footer { + display: none; +} +.box .nav-stacked > li { + border-bottom: 1px solid #f4f4f4; + margin: 0; +} +.box .nav-stacked > li:last-of-type { + border-bottom: none; +} +.box.height-control .box-body { + max-height: 300px; + overflow: auto; +} +.box .border-right { + border-right: 1px solid #f4f4f4; +} +.box .border-left { + border-left: 1px solid #f4f4f4; +} +.box.box-solid { + border-top: 0; +} +.box.box-solid > .box-header .btn.btn-default { + background: transparent; +} +.box.box-solid > .box-header .btn:hover, +.box.box-solid > .box-header a:hover { + background: rgba(0, 0, 0, 0.1); +} +.box.box-solid.box-default { + border: 1px solid #d2d6de; +} +.box.box-solid.box-default > .box-header { + color: #444444; + background: #d2d6de; + background-color: #d2d6de; +} +.box.box-solid.box-default > .box-header a, +.box.box-solid.box-default > .box-header .btn { + color: #444444; +} +.box.box-solid.box-primary { + border: 1px solid #3c8dbc; +} +.box.box-solid.box-primary > .box-header { + color: #ffffff; + background: #3c8dbc; + background-color: #3c8dbc; +} +.box.box-solid.box-primary > .box-header a, +.box.box-solid.box-primary > .box-header .btn { + color: #ffffff; +} +.box.box-solid.box-info { + border: 1px solid #00c0ef; +} +.box.box-solid.box-info > .box-header { + color: #ffffff; + background: #00c0ef; + background-color: #00c0ef; +} +.box.box-solid.box-info > .box-header a, +.box.box-solid.box-info > .box-header .btn { + color: #ffffff; +} +.box.box-solid.box-danger { + border: 1px solid #dd4b39; +} +.box.box-solid.box-danger > .box-header { + color: #ffffff; + background: #dd4b39; + background-color: #dd4b39; +} +.box.box-solid.box-danger > .box-header a, +.box.box-solid.box-danger > .box-header .btn { + color: #ffffff; +} +.box.box-solid.box-warning { + border: 1px solid #f39c12; +} +.box.box-solid.box-warning > .box-header { + color: #ffffff; + background: #f39c12; + background-color: #f39c12; +} +.box.box-solid.box-warning > .box-header a, +.box.box-solid.box-warning > .box-header .btn { + color: #ffffff; +} +.box.box-solid.box-success { + border: 1px solid #00a65a; +} +.box.box-solid.box-success > .box-header { + color: #ffffff; + background: #00a65a; + background-color: #00a65a; +} +.box.box-solid.box-success > .box-header a, +.box.box-solid.box-success > .box-header .btn { + color: #ffffff; +} +.box.box-solid > .box-header > .box-tools .btn { + border: 0; + box-shadow: none; +} +.box.box-solid[class*='bg'] > .box-header { + color: #fff; +} +.box .box-group > .box { + margin-bottom: 5px; +} +.box .knob-label { + text-align: center; + color: #333; + font-weight: 100; + font-size: 12px; + margin-bottom: 0.3em; +} +.box > .overlay, +.overlay-wrapper > .overlay, +.box > .loading-img, +.overlay-wrapper > .loading-img { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.box .overlay, +.overlay-wrapper .overlay { + z-index: 50; + background: rgba(255, 255, 255, 0.7); + border-radius: 3px; +} +.box .overlay > .fa, +.overlay-wrapper .overlay > .fa { + position: absolute; + top: 50%; + left: 50%; + margin-left: -15px; + margin-top: -15px; + color: #000; + font-size: 30px; +} +.box .overlay.dark, +.overlay-wrapper .overlay.dark { + background: rgba(0, 0, 0, 0.5); +} +.box-header:before, +.box-body:before, +.box-footer:before, +.box-header:after, +.box-body:after, +.box-footer:after { + content: " "; + display: table; +} +.box-header:after, +.box-body:after, +.box-footer:after { + clear: both; +} +.box-header { + color: #444; + display: block; + padding: 10px; + position: relative; +} +.box-header.with-border { + border-bottom: 1px solid #f4f4f4; +} +.collapsed-box .box-header.with-border { + border-bottom: none; +} +.box-header > .fa, +.box-header > .glyphicon, +.box-header > .ion, +.box-header .box-title { + display: inline-block; + font-size: 18px; + margin: 0; + line-height: 1; +} +.box-header > .fa, +.box-header > .glyphicon, +.box-header > .ion { + margin-right: 5px; +} +.box-header > .box-tools { + position: absolute; + right: 10px; + top: 5px; +} +.box-header > .box-tools [data-toggle="tooltip"] { + position: relative; +} +.box-header > .box-tools.pull-right .dropdown-menu { + right: 0; + left: auto; +} +.btn-box-tool { + padding: 5px; + font-size: 12px; + background: transparent; + color: #97a0b3; +} +.open .btn-box-tool, +.btn-box-tool:hover { + color: #606c84; +} +.btn-box-tool.btn:active { + box-shadow: none; +} +.box-body { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; + padding: 10px; +} +.no-header .box-body { + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} +.box-body > .table { + margin-bottom: 0; +} +.box-body .fc { + margin-top: 5px; +} +.box-body .full-width-chart { + margin: -19px; +} +.box-body.no-padding .full-width-chart { + margin: -9px; +} +.box-body .box-pane { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-bottom-left-radius: 3px; +} +.box-body .box-pane-right { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 0; +} +.box-footer { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; + border-top: 1px solid #f4f4f4; + padding: 10px; + background-color: #ffffff; +} +.chart-legend { + margin: 10px 0; +} +@media (max-width: 991px) { + .chart-legend > li { + float: left; + margin-right: 10px; + } +} +.box-comments { + background: #f7f7f7; +} +.box-comments .box-comment { + padding: 8px 0; + border-bottom: 1px solid #eee; +} +.box-comments .box-comment:before, +.box-comments .box-comment:after { + content: " "; + display: table; +} +.box-comments .box-comment:after { + clear: both; +} +.box-comments .box-comment:last-of-type { + border-bottom: 0; +} +.box-comments .box-comment:first-of-type { + padding-top: 0; +} +.box-comments .box-comment img { + float: left; +} +.box-comments .comment-text { + margin-left: 40px; + color: #555; +} +.box-comments .username { + color: #444; + display: block; + font-weight: 600; +} +.box-comments .text-muted { + font-weight: 400; + font-size: 12px; +} +/* Widget: TODO LIST */ +.todo-list { + margin: 0; + padding: 0; + list-style: none; + overflow: auto; +} +.todo-list > li { + border-radius: 2px; + padding: 10px; + background: #f4f4f4; + margin-bottom: 2px; + border-left: 2px solid #e6e7e8; + color: #444; +} +.todo-list > li:last-of-type { + margin-bottom: 0; +} +.todo-list > li > input[type='checkbox'] { + margin: 0 10px 0 5px; +} +.todo-list > li .text { + display: inline-block; + margin-left: 5px; + font-weight: 600; +} +.todo-list > li .label { + margin-left: 10px; + font-size: 9px; +} +.todo-list > li .tools { + display: none; + float: right; + color: #dd4b39; +} +.todo-list > li .tools > .fa, +.todo-list > li .tools > .glyphicon, +.todo-list > li .tools > .ion { + margin-right: 5px; + cursor: pointer; +} +.todo-list > li:hover .tools { + display: inline-block; +} +.todo-list > li.done { + color: #999; +} +.todo-list > li.done .text { + text-decoration: line-through; + font-weight: 500; +} +.todo-list > li.done .label { + background: #d2d6de !important; +} +.todo-list .danger { + border-left-color: #dd4b39; +} +.todo-list .warning { + border-left-color: #f39c12; +} +.todo-list .info { + border-left-color: #00c0ef; +} +.todo-list .success { + border-left-color: #00a65a; +} +.todo-list .primary { + border-left-color: #3c8dbc; +} +.todo-list .handle { + display: inline-block; + cursor: move; + margin: 0 5px; +} +/* Chat widget (DEPRECATED - this will be removed in the next major release. Use Direct Chat instead)*/ +.chat { + padding: 5px 20px 5px 10px; +} +.chat .item { + margin-bottom: 10px; +} +.chat .item:before, +.chat .item:after { + content: " "; + display: table; +} +.chat .item:after { + clear: both; +} +.chat .item > img { + width: 40px; + height: 40px; + border: 2px solid transparent; + border-radius: 50%; +} +.chat .item > .online { + border: 2px solid #00a65a; +} +.chat .item > .offline { + border: 2px solid #dd4b39; +} +.chat .item > .message { + margin-left: 55px; + margin-top: -40px; +} +.chat .item > .message > .name { + display: block; + font-weight: 600; +} +.chat .item > .attachment { + border-radius: 3px; + background: #f4f4f4; + margin-left: 65px; + margin-right: 15px; + padding: 10px; +} +.chat .item > .attachment > h4 { + margin: 0 0 5px 0; + font-weight: 600; + font-size: 14px; +} +.chat .item > .attachment > p, +.chat .item > .attachment > .filename { + font-weight: 600; + font-size: 13px; + font-style: italic; + margin: 0; +} +.chat .item > .attachment:before, +.chat .item > .attachment:after { + content: " "; + display: table; +} +.chat .item > .attachment:after { + clear: both; +} +.box-input { + max-width: 200px; +} +.modal .panel-body { + color: #444; +} +/* + * Component: Info Box + * ------------------- + */ +.info-box { + display: block; + min-height: 90px; + background: #fff; + width: 100%; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); + border-radius: 2px; + margin-bottom: 15px; +} +.info-box small { + font-size: 14px; +} +.info-box .progress { + background: rgba(0, 0, 0, 0.2); + margin: 5px -10px 5px -10px; + height: 2px; +} +.info-box .progress, +.info-box .progress .progress-bar { + border-radius: 0; +} +.info-box .progress .progress-bar { + background: #fff; +} +.info-box-icon { + border-top-left-radius: 2px; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-bottom-left-radius: 2px; + display: block; + float: left; + height: 90px; + width: 90px; + text-align: center; + font-size: 45px; + line-height: 90px; + background: rgba(0, 0, 0, 0.2); +} +.info-box-icon > img { + max-width: 100%; +} +.info-box-content { + padding: 5px 10px; + margin-left: 90px; +} +.info-box-number { + display: block; + font-weight: bold; + font-size: 18px; +} +.progress-description, +.info-box-text { + display: block; + font-size: 14px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.info-box-text { + text-transform: uppercase; +} +.info-box-more { + display: block; +} +.progress-description { + margin: 0; +} +/* + * Component: Timeline + * ------------------- + */ +.timeline { + position: relative; + margin: 0 0 30px 0; + padding: 0; + list-style: none; +} +.timeline:before { + content: ''; + position: absolute; + top: 0; + bottom: 0; + width: 4px; + background: #ddd; + left: 31px; + margin: 0; + border-radius: 2px; +} +.timeline > li { + position: relative; + margin-right: 10px; + margin-bottom: 15px; +} +.timeline > li:before, +.timeline > li:after { + content: " "; + display: table; +} +.timeline > li:after { + clear: both; +} +.timeline > li > .timeline-item { + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); + border-radius: 3px; + margin-top: 0; + background: #fff; + color: #444; + margin-left: 60px; + margin-right: 15px; + padding: 0; + position: relative; +} +.timeline > li > .timeline-item > .time { + color: #999; + float: right; + padding: 10px; + font-size: 12px; +} +.timeline > li > .timeline-item > .timeline-header { + margin: 0; + color: #555; + border-bottom: 1px solid #f4f4f4; + padding: 10px; + font-size: 16px; + line-height: 1.1; +} +.timeline > li > .timeline-item > .timeline-header > a { + font-weight: 600; +} +.timeline > li > .timeline-item > .timeline-body, +.timeline > li > .timeline-item > .timeline-footer { + padding: 10px; +} +.timeline > li > .fa, +.timeline > li > .glyphicon, +.timeline > li > .ion { + width: 30px; + height: 30px; + font-size: 15px; + line-height: 30px; + position: absolute; + color: #666; + background: #d2d6de; + border-radius: 50%; + text-align: center; + left: 18px; + top: 0; +} +.timeline > .time-label > span { + font-weight: 600; + padding: 5px; + display: inline-block; + background-color: #fff; + border-radius: 4px; +} +.timeline-inverse > li > .timeline-item { + background: #f0f0f0; + border: 1px solid #ddd; + -webkit-box-shadow: none; + box-shadow: none; +} +.timeline-inverse > li > .timeline-item > .timeline-header { + border-bottom-color: #ddd; +} +/* + * Component: Button + * ----------------- + */ +.btn { + border-radius: 3px; + -webkit-box-shadow: none; + box-shadow: none; + border: 1px solid transparent; +} +.btn.uppercase { + text-transform: uppercase; +} +.btn.btn-flat { + border-radius: 0; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + border-width: 1px; +} +.btn:active { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + -moz-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +} +.btn:focus { + outline: none; +} +.btn.btn-file { + position: relative; + overflow: hidden; +} +.btn.btn-file > input[type='file'] { + position: absolute; + top: 0; + right: 0; + min-width: 100%; + min-height: 100%; + font-size: 100px; + text-align: right; + opacity: 0; + filter: alpha(opacity=0); + outline: none; + background: white; + cursor: inherit; + display: block; +} +.btn-default { + background-color: #f4f4f4; + color: #444; + border-color: #ddd; +} +.btn-default:hover, +.btn-default:active, +.btn-default.hover { + background-color: #e7e7e7; +} +.btn-primary { + background-color: #3c8dbc; + border-color: #367fa9; +} +.btn-primary:hover, +.btn-primary:active, +.btn-primary.hover { + background-color: #367fa9; +} +.btn-success { + background-color: #00a65a; + border-color: #008d4c; +} +.btn-success:hover, +.btn-success:active, +.btn-success.hover { + background-color: #008d4c; +} +.btn-info { + background-color: #00c0ef; + border-color: #00acd6; +} +.btn-info:hover, +.btn-info:active, +.btn-info.hover { + background-color: #00acd6; +} +.btn-danger { + background-color: #dd4b39; + border-color: #d73925; +} +.btn-danger:hover, +.btn-danger:active, +.btn-danger.hover { + background-color: #d73925; +} +.btn-warning { + background-color: #f39c12; + border-color: #e08e0b; +} +.btn-warning:hover, +.btn-warning:active, +.btn-warning.hover { + background-color: #e08e0b; +} +.btn-outline { + border: 1px solid #fff; + background: transparent; + color: #fff; +} +.btn-outline:hover, +.btn-outline:focus, +.btn-outline:active { + color: rgba(255, 255, 255, 0.7); + border-color: rgba(255, 255, 255, 0.7); +} +.btn-link { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn[class*='bg-']:hover { + -webkit-box-shadow: inset 0 0 100px rgba(0, 0, 0, 0.2); + box-shadow: inset 0 0 100px rgba(0, 0, 0, 0.2); +} +.btn-app { + border-radius: 3px; + position: relative; + padding: 15px 5px; + margin: 0 0 10px 10px; + min-width: 80px; + height: 60px; + text-align: center; + color: #666; + border: 1px solid #ddd; + background-color: #f4f4f4; + font-size: 12px; +} +.btn-app > .fa, +.btn-app > .glyphicon, +.btn-app > .ion { + font-size: 20px; + display: block; +} +.btn-app:hover { + background: #f4f4f4; + color: #444; + border-color: #aaa; +} +.btn-app:active, +.btn-app:focus { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + -moz-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +} +.btn-app > .badge { + position: absolute; + top: -3px; + right: -10px; + font-size: 10px; + font-weight: 400; +} +/* + * Component: Callout + * ------------------ + */ +.callout { + border-radius: 3px; + margin: 0 0 20px 0; + padding: 15px 30px 15px 15px; + border-left: 5px solid #eee; +} +.callout a { + color: #fff; + text-decoration: underline; +} +.callout a:hover { + color: #eee; +} +.callout h4 { + margin-top: 0; + font-weight: 600; +} +.callout p:last-child { + margin-bottom: 0; +} +.callout code, +.callout .highlight { + background-color: #fff; +} +.callout.callout-danger { + border-color: #c23321; +} +.callout.callout-warning { + border-color: #c87f0a; +} +.callout.callout-info { + border-color: #0097bc; +} +.callout.callout-success { + border-color: #00733e; +} +/* + * Component: alert + * ---------------- + */ +.alert { + border-radius: 3px; +} +.alert h4 { + font-weight: 600; +} +.alert .icon { + margin-right: 10px; +} +.alert .close { + color: #000; + opacity: 0.2; + filter: alpha(opacity=20); +} +.alert .close:hover { + opacity: 0.5; + filter: alpha(opacity=50); +} +.alert a { + color: #fff; + text-decoration: underline; +} +.alert-success { + border-color: #008d4c; +} +.alert-danger, +.alert-error { + border-color: #d73925; +} +.alert-warning { + border-color: #e08e0b; +} +.alert-info { + border-color: #00acd6; +} +/* + * Component: Nav + * -------------- + */ +.nav > li > a:hover, +.nav > li > a:active, +.nav > li > a:focus { + color: #444; + background: #f7f7f7; +} +/* NAV PILLS */ +.nav-pills > li > a { + border-radius: 0; + border-top: 3px solid transparent; + color: #444; +} +.nav-pills > li > a > .fa, +.nav-pills > li > a > .glyphicon, +.nav-pills > li > a > .ion { + margin-right: 5px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + border-top-color: #3c8dbc; +} +.nav-pills > li.active > a { + font-weight: 600; +} +/* NAV STACKED */ +.nav-stacked > li > a { + border-radius: 0; + border-top: 0; + border-left: 3px solid transparent; + color: #444; +} +.nav-stacked > li.active > a, +.nav-stacked > li.active > a:hover { + background: transparent; + color: #444; + border-top: 0; + border-left-color: #3c8dbc; +} +.nav-stacked > li.header { + border-bottom: 1px solid #ddd; + color: #777; + margin-bottom: 10px; + padding: 5px 10px; + text-transform: uppercase; +} +/* NAV TABS */ +.nav-tabs-custom { + margin-bottom: 20px; + background: #fff; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); + border-radius: 3px; +} +.nav-tabs-custom > .nav-tabs { + margin: 0; + border-bottom-color: #f4f4f4; + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} +.nav-tabs-custom > .nav-tabs > li { + border-top: 3px solid transparent; + margin-bottom: -2px; + margin-right: 5px; +} +.nav-tabs-custom > .nav-tabs > li > a { + color: #444; + border-radius: 0; +} +.nav-tabs-custom > .nav-tabs > li > a.text-muted { + color: #999; +} +.nav-tabs-custom > .nav-tabs > li > a, +.nav-tabs-custom > .nav-tabs > li > a:hover { + background: transparent; + margin: 0; +} +.nav-tabs-custom > .nav-tabs > li > a:hover { + color: #999; +} +.nav-tabs-custom > .nav-tabs > li:not(.active) > a:hover, +.nav-tabs-custom > .nav-tabs > li:not(.active) > a:focus, +.nav-tabs-custom > .nav-tabs > li:not(.active) > a:active { + border-color: transparent; +} +.nav-tabs-custom > .nav-tabs > li.active { + border-top-color: #3c8dbc; +} +.nav-tabs-custom > .nav-tabs > li.active > a, +.nav-tabs-custom > .nav-tabs > li.active:hover > a { + background-color: #fff; + color: #444; +} +.nav-tabs-custom > .nav-tabs > li.active > a { + border-top-color: transparent; + border-left-color: #f4f4f4; + border-right-color: #f4f4f4; +} +.nav-tabs-custom > .nav-tabs > li:first-of-type { + margin-left: 0; +} +.nav-tabs-custom > .nav-tabs > li:first-of-type.active > a { + border-left-color: transparent; +} +.nav-tabs-custom > .nav-tabs.pull-right { + float: none !important; +} +.nav-tabs-custom > .nav-tabs.pull-right > li { + float: right; +} +.nav-tabs-custom > .nav-tabs.pull-right > li:first-of-type { + margin-right: 0; +} +.nav-tabs-custom > .nav-tabs.pull-right > li:first-of-type > a { + border-left-width: 1px; +} +.nav-tabs-custom > .nav-tabs.pull-right > li:first-of-type.active > a { + border-left-color: #f4f4f4; + border-right-color: transparent; +} +.nav-tabs-custom > .nav-tabs > li.header { + line-height: 35px; + padding: 0 10px; + font-size: 20px; + color: #444; +} +.nav-tabs-custom > .nav-tabs > li.header > .fa, +.nav-tabs-custom > .nav-tabs > li.header > .glyphicon, +.nav-tabs-custom > .nav-tabs > li.header > .ion { + margin-right: 5px; +} +.nav-tabs-custom > .tab-content { + background: #fff; + padding: 10px; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.nav-tabs-custom .dropdown.open > a:active, +.nav-tabs-custom .dropdown.open > a:focus { + background: transparent; + color: #999; +} +.nav-tabs-custom.tab-primary > .nav-tabs > li.active { + border-top-color: #3c8dbc; +} +.nav-tabs-custom.tab-info > .nav-tabs > li.active { + border-top-color: #00c0ef; +} +.nav-tabs-custom.tab-danger > .nav-tabs > li.active { + border-top-color: #dd4b39; +} +.nav-tabs-custom.tab-warning > .nav-tabs > li.active { + border-top-color: #f39c12; +} +.nav-tabs-custom.tab-success > .nav-tabs > li.active { + border-top-color: #00a65a; +} +.nav-tabs-custom.tab-default > .nav-tabs > li.active { + border-top-color: #d2d6de; +} +/* PAGINATION */ +.pagination > li > a { + background: #fafafa; + color: #666; +} +.pagination.pagination-flat > li > a { + border-radius: 0 !important; +} +/* + * Component: Products List + * ------------------------ + */ +.products-list { + list-style: none; + margin: 0; + padding: 0; +} +.products-list > .item { + border-radius: 3px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); + padding: 10px 0; + background: #fff; +} +.products-list > .item:before, +.products-list > .item:after { + content: " "; + display: table; +} +.products-list > .item:after { + clear: both; +} +.products-list .product-img { + float: left; +} +.products-list .product-img img { + width: 50px; + height: 50px; +} +.products-list .product-info { + margin-left: 60px; +} +.products-list .product-title { + font-weight: 600; +} +.products-list .product-description { + display: block; + color: #999; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +.product-list-in-box > .item { + -webkit-box-shadow: none; + box-shadow: none; + border-radius: 0; + border-bottom: 1px solid #f4f4f4; +} +.product-list-in-box > .item:last-of-type { + border-bottom-width: 0; +} +/* + * Component: Table + * ---------------- + */ +.table > thead > tr > th, +.table > tbody > tr > th, +.table > tfoot > tr > th, +.table > thead > tr > td, +.table > tbody > tr > td, +.table > tfoot > tr > td { + border-top: 1px solid #f4f4f4; +} +.table > thead > tr > th { + border-bottom: 2px solid #f4f4f4; +} +.table tr td .progress { + margin-top: 5px; +} +.table-bordered { + border: 1px solid #f4f4f4; +} +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #f4f4f4; +} +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} +.table.no-border, +.table.no-border td, +.table.no-border th { + border: 0; +} +/* .text-center in tables */ +table.text-center, +table.text-center td, +table.text-center th { + text-align: center; +} +.table.align th { + text-align: left; +} +.table.align td { + text-align: right; +} +/* + * Component: Label + * ---------------- + */ +.label-default { + background-color: #d2d6de; + color: #444; +} +/* + * Component: Direct Chat + * ---------------------- + */ +.direct-chat .box-body { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + position: relative; + overflow-x: hidden; + padding: 0; +} +.direct-chat.chat-pane-open .direct-chat-contacts { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0); +} +.direct-chat-messages { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0); + padding: 10px; + height: 250px; + overflow: auto; +} +.direct-chat-msg, +.direct-chat-text { + display: block; +} +.direct-chat-msg { + margin-bottom: 10px; +} +.direct-chat-msg:before, +.direct-chat-msg:after { + content: " "; + display: table; +} +.direct-chat-msg:after { + clear: both; +} +.direct-chat-messages, +.direct-chat-contacts { + -webkit-transition: -webkit-transform 0.5s ease-in-out; + -moz-transition: -moz-transform 0.5s ease-in-out; + -o-transition: -o-transform 0.5s ease-in-out; + transition: transform 0.5s ease-in-out; +} +.direct-chat-text { + border-radius: 5px; + position: relative; + padding: 5px 10px; + background: #d2d6de; + border: 1px solid #d2d6de; + margin: 5px 0 0 50px; + color: #444444; +} +.direct-chat-text:after, +.direct-chat-text:before { + position: absolute; + right: 100%; + top: 15px; + border: solid transparent; + border-right-color: #d2d6de; + content: ' '; + height: 0; + width: 0; + pointer-events: none; +} +.direct-chat-text:after { + border-width: 5px; + margin-top: -5px; +} +.direct-chat-text:before { + border-width: 6px; + margin-top: -6px; +} +.right .direct-chat-text { + margin-right: 50px; + margin-left: 0; +} +.right .direct-chat-text:after, +.right .direct-chat-text:before { + right: auto; + left: 100%; + border-right-color: transparent; + border-left-color: #d2d6de; +} +.direct-chat-img { + border-radius: 50%; + float: left; + width: 40px; + height: 40px; +} +.right .direct-chat-img { + float: right; +} +.direct-chat-info { + display: block; + margin-bottom: 2px; + font-size: 12px; +} +.direct-chat-name { + font-weight: 600; +} +.direct-chat-timestamp { + color: #999; +} +.direct-chat-contacts-open .direct-chat-contacts { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0); +} +.direct-chat-contacts { + -webkit-transform: translate(101%, 0); + -ms-transform: translate(101%, 0); + -o-transform: translate(101%, 0); + transform: translate(101%, 0); + position: absolute; + top: 0; + bottom: 0; + height: 250px; + width: 100%; + background: #222d32; + color: #fff; + overflow: auto; +} +.contacts-list > li { + border-bottom: 1px solid rgba(0, 0, 0, 0.2); + padding: 10px; + margin: 0; +} +.contacts-list > li:before, +.contacts-list > li:after { + content: " "; + display: table; +} +.contacts-list > li:after { + clear: both; +} +.contacts-list > li:last-of-type { + border-bottom: none; +} +.contacts-list-img { + border-radius: 50%; + width: 40px; + float: left; +} +.contacts-list-info { + margin-left: 45px; + color: #fff; +} +.contacts-list-name, +.contacts-list-status { + display: block; +} +.contacts-list-name { + font-weight: 600; +} +.contacts-list-status { + font-size: 12px; +} +.contacts-list-date { + color: #aaa; + font-weight: normal; +} +.contacts-list-msg { + color: #999; +} +.direct-chat-danger .right > .direct-chat-text { + background: #dd4b39; + border-color: #dd4b39; + color: #ffffff; +} +.direct-chat-danger .right > .direct-chat-text:after, +.direct-chat-danger .right > .direct-chat-text:before { + border-left-color: #dd4b39; +} +.direct-chat-primary .right > .direct-chat-text { + background: #3c8dbc; + border-color: #3c8dbc; + color: #ffffff; +} +.direct-chat-primary .right > .direct-chat-text:after, +.direct-chat-primary .right > .direct-chat-text:before { + border-left-color: #3c8dbc; +} +.direct-chat-warning .right > .direct-chat-text { + background: #f39c12; + border-color: #f39c12; + color: #ffffff; +} +.direct-chat-warning .right > .direct-chat-text:after, +.direct-chat-warning .right > .direct-chat-text:before { + border-left-color: #f39c12; +} +.direct-chat-info .right > .direct-chat-text { + background: #00c0ef; + border-color: #00c0ef; + color: #ffffff; +} +.direct-chat-info .right > .direct-chat-text:after, +.direct-chat-info .right > .direct-chat-text:before { + border-left-color: #00c0ef; +} +.direct-chat-success .right > .direct-chat-text { + background: #00a65a; + border-color: #00a65a; + color: #ffffff; +} +.direct-chat-success .right > .direct-chat-text:after, +.direct-chat-success .right > .direct-chat-text:before { + border-left-color: #00a65a; +} +/* + * Component: Users List + * --------------------- + */ +.users-list > li { + width: 25%; + float: left; + padding: 10px; + text-align: center; +} +.users-list > li img { + border-radius: 50%; + max-width: 100%; + height: auto; +} +.users-list > li > a:hover, +.users-list > li > a:hover .users-list-name { + color: #999; +} +.users-list-name, +.users-list-date { + display: block; +} +.users-list-name { + font-weight: 600; + color: #444; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +.users-list-date { + color: #999; + font-size: 12px; +} +/* + * Component: Carousel + * ------------------- + */ +.carousel-control.left, +.carousel-control.right { + background-image: none; +} +.carousel-control > .fa { + font-size: 40px; + position: absolute; + top: 50%; + z-index: 5; + display: inline-block; + margin-top: -20px; +} +/* + * Component: modal + * ---------------- + */ +.modal { + background: rgba(0, 0, 0, 0.3); +} +.modal-content { + border-radius: 0; + -webkit-box-shadow: 0 2px 3px rgba(0, 0, 0, 0.125); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.125); + border: 0; +} +@media (min-width: 768px) { + .modal-content { + -webkit-box-shadow: 0 2px 3px rgba(0, 0, 0, 0.125); + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.125); + } +} +.modal-header { + border-bottom-color: #f4f4f4; +} +.modal-footer { + border-top-color: #f4f4f4; +} +.modal-primary .modal-header, +.modal-primary .modal-footer { + border-color: #307095; +} +.modal-warning .modal-header, +.modal-warning .modal-footer { + border-color: #c87f0a; +} +.modal-info .modal-header, +.modal-info .modal-footer { + border-color: #0097bc; +} +.modal-success .modal-header, +.modal-success .modal-footer { + border-color: #00733e; +} +.modal-danger .modal-header, +.modal-danger .modal-footer { + border-color: #c23321; +} +/* + * Component: Social Widgets + * ------------------------- + */ +.box-widget { + border: none; + position: relative; +} +.widget-user .widget-user-header { + padding: 20px; + height: 120px; + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} +.widget-user .widget-user-username { + margin-top: 0; + margin-bottom: 5px; + font-size: 25px; + font-weight: 300; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); +} +.widget-user .widget-user-desc { + margin-top: 0; +} +.widget-user .widget-user-image { + position: absolute; + top: 65px; + left: 50%; + margin-left: -45px; +} +.widget-user .widget-user-image > img { + width: 90px; + height: auto; + border: 3px solid #fff; +} +.widget-user .box-footer { + padding-top: 30px; +} +.widget-user-2 .widget-user-header { + padding: 20px; + border-top-right-radius: 3px; + border-top-left-radius: 3px; +} +.widget-user-2 .widget-user-username { + margin-top: 5px; + margin-bottom: 5px; + font-size: 25px; + font-weight: 300; +} +.widget-user-2 .widget-user-desc { + margin-top: 0; +} +.widget-user-2 .widget-user-username, +.widget-user-2 .widget-user-desc { + margin-left: 75px; +} +.widget-user-2 .widget-user-image > img { + width: 65px; + height: auto; + float: left; +} +/* + * Page: Mailbox + * ------------- + */ +.mailbox-messages > .table { + margin: 0; +} +.mailbox-controls { + padding: 5px; +} +.mailbox-controls.with-border { + border-bottom: 1px solid #f4f4f4; +} +.mailbox-read-info { + border-bottom: 1px solid #f4f4f4; + padding: 10px; +} +.mailbox-read-info h3 { + font-size: 20px; + margin: 0; +} +.mailbox-read-info h5 { + margin: 0; + padding: 5px 0 0 0; +} +.mailbox-read-time { + color: #999; + font-size: 13px; +} +.mailbox-read-message { + padding: 10px; +} +.mailbox-attachments li { + float: left; + width: 200px; + border: 1px solid #eee; + margin-bottom: 10px; + margin-right: 10px; +} +.mailbox-attachment-name { + font-weight: bold; + color: #666; +} +.mailbox-attachment-icon, +.mailbox-attachment-info, +.mailbox-attachment-size { + display: block; +} +.mailbox-attachment-info { + padding: 10px; + background: #f4f4f4; +} +.mailbox-attachment-size { + color: #999; + font-size: 12px; +} +.mailbox-attachment-icon { + text-align: center; + font-size: 65px; + color: #666; + padding: 20px 10px; +} +.mailbox-attachment-icon.has-img { + padding: 0; +} +.mailbox-attachment-icon.has-img > img { + max-width: 100%; + height: auto; +} +/* + * Page: Lock Screen + * ----------------- + */ +/* ADD THIS CLASS TO THE <BODY> TAG */ +.lockscreen { + background: #d2d6de; +} +.lockscreen-logo { + font-size: 35px; + text-align: center; + margin-bottom: 25px; + font-weight: 300; +} +.lockscreen-logo a { + color: #444; +} +.lockscreen-wrapper { + max-width: 400px; + margin: 0 auto; + margin-top: 10%; +} +/* User name [optional] */ +.lockscreen .lockscreen-name { + text-align: center; + font-weight: 600; +} +/* Will contain the image and the sign in form */ +.lockscreen-item { + border-radius: 4px; + padding: 0; + background: #fff; + position: relative; + margin: 10px auto 30px auto; + width: 290px; +} +/* User image */ +.lockscreen-image { + border-radius: 50%; + position: absolute; + left: -10px; + top: -25px; + background: #fff; + padding: 5px; + z-index: 10; +} +.lockscreen-image > img { + border-radius: 50%; + width: 70px; + height: 70px; +} +/* Contains the password input and the login button */ +.lockscreen-credentials { + margin-left: 70px; +} +.lockscreen-credentials .form-control { + border: 0; +} +.lockscreen-credentials .btn { + background-color: #fff; + border: 0; + padding: 0 10px; +} +.lockscreen-footer { + margin-top: 10px; +} +/* + * Page: Login & Register + * ---------------------- + */ +.login-logo, +.register-logo { + font-size: 35px; + text-align: center; + margin-bottom: 25px; + font-weight: 300; +} +.login-logo a, +.register-logo a { + color: #444; +} +.login-page, +.register-page { + background: #d2d6de; +} +.login-box, +.register-box { + width: 360px; + margin: 7% auto; +} +@media (max-width: 768px) { + .login-box, + .register-box { + width: 90%; + margin-top: 20px; + } +} +.login-box-body, +.register-box-body { + background: #fff; + padding: 20px; + border-top: 0; + color: #666; +} +.login-box-body .form-control-feedback, +.register-box-body .form-control-feedback { + color: #777; +} +.login-box-msg, +.register-box-msg { + margin: 0; + text-align: center; + padding: 0 20px 20px 20px; +} +.social-auth-links { + margin: 10px 0; +} +/* + * Page: 400 and 500 error pages + * ------------------------------ + */ +.error-page { + width: 600px; + margin: 20px auto 0 auto; +} +@media (max-width: 991px) { + .error-page { + width: 100%; + } +} +.error-page > .headline { + float: left; + font-size: 100px; + font-weight: 300; +} +@media (max-width: 991px) { + .error-page > .headline { + float: none; + text-align: center; + } +} +.error-page > .error-content { + margin-left: 190px; + display: block; +} +@media (max-width: 991px) { + .error-page > .error-content { + margin-left: 0; + } +} +.error-page > .error-content > h3 { + font-weight: 300; + font-size: 25px; +} +@media (max-width: 991px) { + .error-page > .error-content > h3 { + text-align: center; + } +} +/* + * Page: Invoice + * ------------- + */ +.invoice { + position: relative; + background: #fff; + border: 1px solid #f4f4f4; + padding: 20px; + margin: 10px 25px; +} +.invoice-title { + margin-top: 0; +} +/* + * Page: Profile + * ------------- + */ +.profile-user-img { + margin: 0 auto; + width: 100px; + padding: 3px; + border: 3px solid #d2d6de; +} +.profile-username { + font-size: 21px; + margin-top: 5px; +} +.post { + border-bottom: 1px solid #d2d6de; + margin-bottom: 15px; + padding-bottom: 15px; + color: #666; +} +.post:last-of-type { + border-bottom: 0; + margin-bottom: 0; + padding-bottom: 0; +} +.post .user-block { + margin-bottom: 15px; +} +/* + * Social Buttons for Bootstrap + * + * Copyright 2013-2015 Panayiotis Lipiridis + * Licensed under the MIT License + * + * https://github.com/lipis/bootstrap-social + */ +.btn-social { + position: relative; + padding-left: 44px; + text-align: left; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.btn-social > :first-child { + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 32px; + line-height: 34px; + font-size: 1.6em; + text-align: center; + border-right: 1px solid rgba(0, 0, 0, 0.2); +} +.btn-social.btn-lg { + padding-left: 61px; +} +.btn-social.btn-lg > :first-child { + line-height: 45px; + width: 45px; + font-size: 1.8em; +} +.btn-social.btn-sm { + padding-left: 38px; +} +.btn-social.btn-sm > :first-child { + line-height: 28px; + width: 28px; + font-size: 1.4em; +} +.btn-social.btn-xs { + padding-left: 30px; +} +.btn-social.btn-xs > :first-child { + line-height: 20px; + width: 20px; + font-size: 1.2em; +} +.btn-social-icon { + position: relative; + padding-left: 44px; + text-align: left; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + height: 34px; + width: 34px; + padding: 0; +} +.btn-social-icon > :first-child { + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 32px; + line-height: 34px; + font-size: 1.6em; + text-align: center; + border-right: 1px solid rgba(0, 0, 0, 0.2); +} +.btn-social-icon.btn-lg { + padding-left: 61px; +} +.btn-social-icon.btn-lg > :first-child { + line-height: 45px; + width: 45px; + font-size: 1.8em; +} +.btn-social-icon.btn-sm { + padding-left: 38px; +} +.btn-social-icon.btn-sm > :first-child { + line-height: 28px; + width: 28px; + font-size: 1.4em; +} +.btn-social-icon.btn-xs { + padding-left: 30px; +} +.btn-social-icon.btn-xs > :first-child { + line-height: 20px; + width: 20px; + font-size: 1.2em; +} +.btn-social-icon > :first-child { + border: none; + text-align: center; + width: 100%; +} +.btn-social-icon.btn-lg { + height: 45px; + width: 45px; + padding-left: 0; + padding-right: 0; +} +.btn-social-icon.btn-sm { + height: 30px; + width: 30px; + padding-left: 0; + padding-right: 0; +} +.btn-social-icon.btn-xs { + height: 22px; + width: 22px; + padding-left: 0; + padding-right: 0; +} +.btn-adn { + color: #ffffff; + background-color: #d87a68; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-adn:focus, +.btn-adn.focus { + color: #ffffff; + background-color: #ce563f; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-adn:hover { + color: #ffffff; + background-color: #ce563f; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-adn:active, +.btn-adn.active, +.open > .dropdown-toggle.btn-adn { + color: #ffffff; + background-color: #ce563f; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-adn:active, +.btn-adn.active, +.open > .dropdown-toggle.btn-adn { + background-image: none; +} +.btn-adn .badge { + color: #d87a68; + background-color: #ffffff; +} +.btn-bitbucket { + color: #ffffff; + background-color: #205081; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-bitbucket:focus, +.btn-bitbucket.focus { + color: #ffffff; + background-color: #163758; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-bitbucket:hover { + color: #ffffff; + background-color: #163758; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-bitbucket:active, +.btn-bitbucket.active, +.open > .dropdown-toggle.btn-bitbucket { + color: #ffffff; + background-color: #163758; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-bitbucket:active, +.btn-bitbucket.active, +.open > .dropdown-toggle.btn-bitbucket { + background-image: none; +} +.btn-bitbucket .badge { + color: #205081; + background-color: #ffffff; +} +.btn-dropbox { + color: #ffffff; + background-color: #1087dd; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-dropbox:focus, +.btn-dropbox.focus { + color: #ffffff; + background-color: #0d6aad; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-dropbox:hover { + color: #ffffff; + background-color: #0d6aad; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-dropbox:active, +.btn-dropbox.active, +.open > .dropdown-toggle.btn-dropbox { + color: #ffffff; + background-color: #0d6aad; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-dropbox:active, +.btn-dropbox.active, +.open > .dropdown-toggle.btn-dropbox { + background-image: none; +} +.btn-dropbox .badge { + color: #1087dd; + background-color: #ffffff; +} +.btn-facebook { + color: #ffffff; + background-color: #3b5998; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-facebook:focus, +.btn-facebook.focus { + color: #ffffff; + background-color: #2d4373; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-facebook:hover { + color: #ffffff; + background-color: #2d4373; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-facebook:active, +.btn-facebook.active, +.open > .dropdown-toggle.btn-facebook { + color: #ffffff; + background-color: #2d4373; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-facebook:active, +.btn-facebook.active, +.open > .dropdown-toggle.btn-facebook { + background-image: none; +} +.btn-facebook .badge { + color: #3b5998; + background-color: #ffffff; +} +.btn-flickr { + color: #ffffff; + background-color: #ff0084; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-flickr:focus, +.btn-flickr.focus { + color: #ffffff; + background-color: #cc006a; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-flickr:hover { + color: #ffffff; + background-color: #cc006a; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-flickr:active, +.btn-flickr.active, +.open > .dropdown-toggle.btn-flickr { + color: #ffffff; + background-color: #cc006a; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-flickr:active, +.btn-flickr.active, +.open > .dropdown-toggle.btn-flickr { + background-image: none; +} +.btn-flickr .badge { + color: #ff0084; + background-color: #ffffff; +} +.btn-foursquare { + color: #ffffff; + background-color: #f94877; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-foursquare:focus, +.btn-foursquare.focus { + color: #ffffff; + background-color: #f71752; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-foursquare:hover { + color: #ffffff; + background-color: #f71752; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-foursquare:active, +.btn-foursquare.active, +.open > .dropdown-toggle.btn-foursquare { + color: #ffffff; + background-color: #f71752; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-foursquare:active, +.btn-foursquare.active, +.open > .dropdown-toggle.btn-foursquare { + background-image: none; +} +.btn-foursquare .badge { + color: #f94877; + background-color: #ffffff; +} +.btn-github { + color: #ffffff; + background-color: #444444; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-github:focus, +.btn-github.focus { + color: #ffffff; + background-color: #2b2b2b; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-github:hover { + color: #ffffff; + background-color: #2b2b2b; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-github:active, +.btn-github.active, +.open > .dropdown-toggle.btn-github { + color: #ffffff; + background-color: #2b2b2b; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-github:active, +.btn-github.active, +.open > .dropdown-toggle.btn-github { + background-image: none; +} +.btn-github .badge { + color: #444444; + background-color: #ffffff; +} +.btn-google { + color: #ffffff; + background-color: #dd4b39; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-google:focus, +.btn-google.focus { + color: #ffffff; + background-color: #c23321; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-google:hover { + color: #ffffff; + background-color: #c23321; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-google:active, +.btn-google.active, +.open > .dropdown-toggle.btn-google { + color: #ffffff; + background-color: #c23321; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-google:active, +.btn-google.active, +.open > .dropdown-toggle.btn-google { + background-image: none; +} +.btn-google .badge { + color: #dd4b39; + background-color: #ffffff; +} +.btn-instagram { + color: #ffffff; + background-color: #3f729b; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-instagram:focus, +.btn-instagram.focus { + color: #ffffff; + background-color: #305777; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-instagram:hover { + color: #ffffff; + background-color: #305777; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-instagram:active, +.btn-instagram.active, +.open > .dropdown-toggle.btn-instagram { + color: #ffffff; + background-color: #305777; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-instagram:active, +.btn-instagram.active, +.open > .dropdown-toggle.btn-instagram { + background-image: none; +} +.btn-instagram .badge { + color: #3f729b; + background-color: #ffffff; +} +.btn-linkedin { + color: #ffffff; + background-color: #007bb6; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-linkedin:focus, +.btn-linkedin.focus { + color: #ffffff; + background-color: #005983; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-linkedin:hover { + color: #ffffff; + background-color: #005983; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-linkedin:active, +.btn-linkedin.active, +.open > .dropdown-toggle.btn-linkedin { + color: #ffffff; + background-color: #005983; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-linkedin:active, +.btn-linkedin.active, +.open > .dropdown-toggle.btn-linkedin { + background-image: none; +} +.btn-linkedin .badge { + color: #007bb6; + background-color: #ffffff; +} +.btn-microsoft { + color: #ffffff; + background-color: #2672ec; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-microsoft:focus, +.btn-microsoft.focus { + color: #ffffff; + background-color: #125acd; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-microsoft:hover { + color: #ffffff; + background-color: #125acd; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-microsoft:active, +.btn-microsoft.active, +.open > .dropdown-toggle.btn-microsoft { + color: #ffffff; + background-color: #125acd; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-microsoft:active, +.btn-microsoft.active, +.open > .dropdown-toggle.btn-microsoft { + background-image: none; +} +.btn-microsoft .badge { + color: #2672ec; + background-color: #ffffff; +} +.btn-openid { + color: #ffffff; + background-color: #f7931e; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-openid:focus, +.btn-openid.focus { + color: #ffffff; + background-color: #da7908; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-openid:hover { + color: #ffffff; + background-color: #da7908; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-openid:active, +.btn-openid.active, +.open > .dropdown-toggle.btn-openid { + color: #ffffff; + background-color: #da7908; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-openid:active, +.btn-openid.active, +.open > .dropdown-toggle.btn-openid { + background-image: none; +} +.btn-openid .badge { + color: #f7931e; + background-color: #ffffff; +} +.btn-pinterest { + color: #ffffff; + background-color: #cb2027; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-pinterest:focus, +.btn-pinterest.focus { + color: #ffffff; + background-color: #9f191f; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-pinterest:hover { + color: #ffffff; + background-color: #9f191f; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-pinterest:active, +.btn-pinterest.active, +.open > .dropdown-toggle.btn-pinterest { + color: #ffffff; + background-color: #9f191f; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-pinterest:active, +.btn-pinterest.active, +.open > .dropdown-toggle.btn-pinterest { + background-image: none; +} +.btn-pinterest .badge { + color: #cb2027; + background-color: #ffffff; +} +.btn-reddit { + color: #000000; + background-color: #eff7ff; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-reddit:focus, +.btn-reddit.focus { + color: #000000; + background-color: #bcddff; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-reddit:hover { + color: #000000; + background-color: #bcddff; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-reddit:active, +.btn-reddit.active, +.open > .dropdown-toggle.btn-reddit { + color: #000000; + background-color: #bcddff; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-reddit:active, +.btn-reddit.active, +.open > .dropdown-toggle.btn-reddit { + background-image: none; +} +.btn-reddit .badge { + color: #eff7ff; + background-color: #000000; +} +.btn-soundcloud { + color: #ffffff; + background-color: #ff5500; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-soundcloud:focus, +.btn-soundcloud.focus { + color: #ffffff; + background-color: #cc4400; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-soundcloud:hover { + color: #ffffff; + background-color: #cc4400; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-soundcloud:active, +.btn-soundcloud.active, +.open > .dropdown-toggle.btn-soundcloud { + color: #ffffff; + background-color: #cc4400; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-soundcloud:active, +.btn-soundcloud.active, +.open > .dropdown-toggle.btn-soundcloud { + background-image: none; +} +.btn-soundcloud .badge { + color: #ff5500; + background-color: #ffffff; +} +.btn-tumblr { + color: #ffffff; + background-color: #2c4762; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-tumblr:focus, +.btn-tumblr.focus { + color: #ffffff; + background-color: #1c2d3f; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-tumblr:hover { + color: #ffffff; + background-color: #1c2d3f; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-tumblr:active, +.btn-tumblr.active, +.open > .dropdown-toggle.btn-tumblr { + color: #ffffff; + background-color: #1c2d3f; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-tumblr:active, +.btn-tumblr.active, +.open > .dropdown-toggle.btn-tumblr { + background-image: none; +} +.btn-tumblr .badge { + color: #2c4762; + background-color: #ffffff; +} +.btn-twitter { + color: #ffffff; + background-color: #55acee; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-twitter:focus, +.btn-twitter.focus { + color: #ffffff; + background-color: #2795e9; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-twitter:hover { + color: #ffffff; + background-color: #2795e9; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-twitter:active, +.btn-twitter.active, +.open > .dropdown-toggle.btn-twitter { + color: #ffffff; + background-color: #2795e9; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-twitter:active, +.btn-twitter.active, +.open > .dropdown-toggle.btn-twitter { + background-image: none; +} +.btn-twitter .badge { + color: #55acee; + background-color: #ffffff; +} +.btn-vimeo { + color: #ffffff; + background-color: #1ab7ea; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-vimeo:focus, +.btn-vimeo.focus { + color: #ffffff; + background-color: #1295bf; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-vimeo:hover { + color: #ffffff; + background-color: #1295bf; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-vimeo:active, +.btn-vimeo.active, +.open > .dropdown-toggle.btn-vimeo { + color: #ffffff; + background-color: #1295bf; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-vimeo:active, +.btn-vimeo.active, +.open > .dropdown-toggle.btn-vimeo { + background-image: none; +} +.btn-vimeo .badge { + color: #1ab7ea; + background-color: #ffffff; +} +.btn-vk { + color: #ffffff; + background-color: #587ea3; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-vk:focus, +.btn-vk.focus { + color: #ffffff; + background-color: #466482; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-vk:hover { + color: #ffffff; + background-color: #466482; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-vk:active, +.btn-vk.active, +.open > .dropdown-toggle.btn-vk { + color: #ffffff; + background-color: #466482; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-vk:active, +.btn-vk.active, +.open > .dropdown-toggle.btn-vk { + background-image: none; +} +.btn-vk .badge { + color: #587ea3; + background-color: #ffffff; +} +.btn-yahoo { + color: #ffffff; + background-color: #720e9e; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-yahoo:focus, +.btn-yahoo.focus { + color: #ffffff; + background-color: #500a6f; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-yahoo:hover { + color: #ffffff; + background-color: #500a6f; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-yahoo:active, +.btn-yahoo.active, +.open > .dropdown-toggle.btn-yahoo { + color: #ffffff; + background-color: #500a6f; + border-color: rgba(0, 0, 0, 0.2); +} +.btn-yahoo:active, +.btn-yahoo.active, +.open > .dropdown-toggle.btn-yahoo { + background-image: none; +} +.btn-yahoo .badge { + color: #720e9e; + background-color: #ffffff; +} +/* + * Plugin: Full Calendar + * --------------------- + */ +.fc-button { + background: #f4f4f4; + background-image: none; + color: #444; + border-color: #ddd; + border-bottom-color: #ddd; +} +.fc-button:hover, +.fc-button:active, +.fc-button.hover { + background-color: #e9e9e9; +} +.fc-header-title h2 { + font-size: 15px; + line-height: 1.6em; + color: #666; + margin-left: 10px; +} +.fc-header-right { + padding-right: 10px; +} +.fc-header-left { + padding-left: 10px; +} +.fc-widget-header { + background: #fafafa; +} +.fc-grid { + width: 100%; + border: 0; +} +.fc-widget-header:first-of-type, +.fc-widget-content:first-of-type { + border-left: 0; + border-right: 0; +} +.fc-widget-header:last-of-type, +.fc-widget-content:last-of-type { + border-right: 0; +} +.fc-toolbar { + padding: 10px; + margin: 0; +} +.fc-day-number { + font-size: 20px; + font-weight: 300; + padding-right: 10px; +} +.fc-color-picker { + list-style: none; + margin: 0; + padding: 0; +} +.fc-color-picker > li { + float: left; + font-size: 30px; + margin-right: 5px; + line-height: 30px; +} +.fc-color-picker > li .fa { + -webkit-transition: -webkit-transform linear 0.3s; + -moz-transition: -moz-transform linear 0.3s; + -o-transition: -o-transform linear 0.3s; + transition: transform linear 0.3s; +} +.fc-color-picker > li .fa:hover { + -webkit-transform: rotate(30deg); + -ms-transform: rotate(30deg); + -o-transform: rotate(30deg); + transform: rotate(30deg); +} +#add-new-event { + -webkit-transition: all linear 0.3s; + -o-transition: all linear 0.3s; + transition: all linear 0.3s; +} +.external-event { + padding: 5px 10px; + font-weight: bold; + margin-bottom: 4px; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); + border-radius: 3px; + cursor: move; +} +.external-event:hover { + box-shadow: inset 0 0 90px rgba(0, 0, 0, 0.2); +} +/* + * Plugin: Select2 + * --------------- + */ +.select2-container--default.select2-container--focus, +.select2-selection.select2-container--focus, +.select2-container--default:focus, +.select2-selection:focus, +.select2-container--default:active, +.select2-selection:active { + outline: none; +} +.select2-container--default .select2-selection--single, +.select2-selection .select2-selection--single { + border: 1px solid #d2d6de; + border-radius: 0; + padding: 6px 12px; + height: 34px; +} +.select2-container--default.select2-container--open { + border-color: #3c8dbc; +} +.select2-dropdown { + border: 1px solid #d2d6de; + border-radius: 0; +} +.select2-container--default .select2-results__option--highlighted[aria-selected] { + background-color: #3c8dbc; + color: white; +} +.select2-results__option { + padding: 6px 12px; + user-select: none; + -webkit-user-select: none; +} +.select2-container .select2-selection--single .select2-selection__rendered { + padding-left: 0; + padding-right: 0; + height: auto; + margin-top: -4px; +} +.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered { + padding-right: 6px; + padding-left: 20px; +} +.select2-container--default .select2-selection--single .select2-selection__arrow { + height: 28px; + right: 3px; +} +.select2-container--default .select2-selection--single .select2-selection__arrow b { + margin-top: 0; +} +.select2-dropdown .select2-search__field, +.select2-search--inline .select2-search__field { + border: 1px solid #d2d6de; +} +.select2-dropdown .select2-search__field:focus, +.select2-search--inline .select2-search__field:focus { + outline: none; + border: 1px solid #3c8dbc; +} +.select2-container--default .select2-results__option[aria-disabled=true] { + color: #999; +} +.select2-container--default .select2-results__option[aria-selected=true] { + background-color: #ddd; +} +.select2-container--default .select2-results__option[aria-selected=true], +.select2-container--default .select2-results__option[aria-selected=true]:hover { + color: #444; +} +.select2-container--default .select2-selection--multiple { + border: 1px solid #d2d6de; + border-radius: 0; +} +.select2-container--default .select2-selection--multiple:focus { + border-color: #3c8dbc; +} +.select2-container--default.select2-container--focus .select2-selection--multiple { + border-color: #d2d6de; +} +.select2-container--default .select2-selection--multiple .select2-selection__choice { + background-color: #3c8dbc; + border-color: #367fa9; + padding: 1px 10px; + color: #fff; +} +.select2-container--default .select2-selection--multiple .select2-selection__choice__remove { + margin-right: 5px; + color: rgba(255, 255, 255, 0.7); +} +.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover { + color: #fff; +} +.select2-container .select2-selection--single .select2-selection__rendered { + padding-right: 10px; +} +/* + * General: Miscellaneous + * ---------------------- + */ +.pad { + padding: 10px; +} +.margin { + margin: 10px; +} +.margin-bottom { + margin-bottom: 20px; +} +.margin-bottom-none { + margin-bottom: 0; +} +.margin-r-5 { + margin-right: 5px; +} +.inline { + display: inline; +} +.description-block { + display: block; + margin: 10px 0; + text-align: center; +} +.description-block.margin-bottom { + margin-bottom: 25px; +} +.description-block > .description-header { + margin: 0; + padding: 0; + font-weight: 600; + font-size: 16px; +} +.description-block > .description-text { + text-transform: uppercase; +} +.bg-red, +.bg-yellow, +.bg-aqua, +.bg-blue, +.bg-light-blue, +.bg-green, +.bg-navy, +.bg-teal, +.bg-olive, +.bg-lime, +.bg-orange, +.bg-fuchsia, +.bg-purple, +.bg-maroon, +.bg-black, +.bg-red-active, +.bg-yellow-active, +.bg-aqua-active, +.bg-blue-active, +.bg-light-blue-active, +.bg-green-active, +.bg-navy-active, +.bg-teal-active, +.bg-olive-active, +.bg-lime-active, +.bg-orange-active, +.bg-fuchsia-active, +.bg-purple-active, +.bg-maroon-active, +.bg-black-active, +.callout.callout-danger, +.callout.callout-warning, +.callout.callout-info, +.callout.callout-success, +.alert-success, +.alert-danger, +.alert-error, +.alert-warning, +.alert-info, +.label-danger, +.label-info, +.label-warning, +.label-primary, +.label-success, +.modal-primary .modal-body, +.modal-primary .modal-header, +.modal-primary .modal-footer, +.modal-warning .modal-body, +.modal-warning .modal-header, +.modal-warning .modal-footer, +.modal-info .modal-body, +.modal-info .modal-header, +.modal-info .modal-footer, +.modal-success .modal-body, +.modal-success .modal-header, +.modal-success .modal-footer, +.modal-danger .modal-body, +.modal-danger .modal-header, +.modal-danger .modal-footer { + color: #fff !important; +} +.bg-gray { + color: #000; + background-color: #d2d6de !important; +} +.bg-gray-light { + background-color: #f7f7f7; +} +.bg-black { + background-color: #111111 !important; +} +.bg-red, +.callout.callout-danger, +.alert-danger, +.alert-error, +.label-danger, +.modal-danger .modal-body { + background-color: #dd4b39 !important; +} +.bg-yellow, +.callout.callout-warning, +.alert-warning, +.label-warning, +.modal-warning .modal-body { + background-color: #f39c12 !important; +} +.bg-aqua, +.callout.callout-info, +.alert-info, +.label-info, +.modal-info .modal-body { + background-color: #00c0ef !important; +} +.bg-blue { + background-color: #0073b7 !important; +} +.bg-light-blue, +.label-primary, +.modal-primary .modal-body { + background-color: #3c8dbc !important; +} +.bg-green, +.callout.callout-success, +.alert-success, +.label-success, +.modal-success .modal-body { + background-color: #00a65a !important; +} +.bg-navy { + background-color: #001f3f !important; +} +.bg-teal { + background-color: #39cccc !important; +} +.bg-olive { + background-color: #3d9970 !important; +} +.bg-lime { + background-color: #01ff70 !important; +} +.bg-orange { + background-color: #ff851b !important; +} +.bg-fuchsia { + background-color: #f012be !important; +} +.bg-purple { + background-color: #605ca8 !important; +} +.bg-maroon { + background-color: #d81b60 !important; +} +.bg-gray-active { + color: #000; + background-color: #b5bbc8 !important; +} +.bg-black-active { + background-color: #000000 !important; +} +.bg-red-active, +.modal-danger .modal-header, +.modal-danger .modal-footer { + background-color: #d33724 !important; +} +.bg-yellow-active, +.modal-warning .modal-header, +.modal-warning .modal-footer { + background-color: #db8b0b !important; +} +.bg-aqua-active, +.modal-info .modal-header, +.modal-info .modal-footer { + background-color: #00a7d0 !important; +} +.bg-blue-active { + background-color: #005384 !important; +} +.bg-light-blue-active, +.modal-primary .modal-header, +.modal-primary .modal-footer { + background-color: #357ca5 !important; +} +.bg-green-active, +.modal-success .modal-header, +.modal-success .modal-footer { + background-color: #008d4c !important; +} +.bg-navy-active { + background-color: #001a35 !important; +} +.bg-teal-active { + background-color: #30bbbb !important; +} +.bg-olive-active { + background-color: #368763 !important; +} +.bg-lime-active { + background-color: #00e765 !important; +} +.bg-orange-active { + background-color: #ff7701 !important; +} +.bg-fuchsia-active { + background-color: #db0ead !important; +} +.bg-purple-active { + background-color: #555299 !important; +} +.bg-maroon-active { + background-color: #ca195a !important; +} +[class^="bg-"].disabled { + opacity: 0.65; + filter: alpha(opacity=65); +} +.text-red { + color: #dd4b39 !important; +} +.text-yellow { + color: #f39c12 !important; +} +.text-aqua { + color: #00c0ef !important; +} +.text-blue { + color: #0073b7 !important; +} +.text-black { + color: #111111 !important; +} +.text-light-blue { + color: #3c8dbc !important; +} +.text-green { + color: #00a65a !important; +} +.text-gray { + color: #d2d6de !important; +} +.text-navy { + color: #001f3f !important; +} +.text-teal { + color: #39cccc !important; +} +.text-olive { + color: #3d9970 !important; +} +.text-lime { + color: #01ff70 !important; +} +.text-orange { + color: #ff851b !important; +} +.text-fuchsia { + color: #f012be !important; +} +.text-purple { + color: #605ca8 !important; +} +.text-maroon { + color: #d81b60 !important; +} +.link-muted { + color: #7a869d; +} +.link-muted:hover, +.link-muted:focus { + color: #606c84; +} +.link-black { + color: #666; +} +.link-black:hover, +.link-black:focus { + color: #999; +} +.hide { + display: none !important; +} +.no-border { + border: 0 !important; +} +.no-padding { + padding: 0 !important; +} +.no-margin { + margin: 0 !important; +} +.no-shadow { + box-shadow: none !important; +} +.list-unstyled, +.chart-legend, +.contacts-list, +.users-list, +.mailbox-attachments { + list-style: none; + margin: 0; + padding: 0; +} +.list-group-unbordered > .list-group-item { + border-left: 0; + border-right: 0; + border-radius: 0; + padding-left: 0; + padding-right: 0; +} +.flat { + border-radius: 0 !important; +} +.text-bold, +.text-bold.table td, +.text-bold.table th { + font-weight: 700; +} +.text-sm { + font-size: 12px; +} +.jqstooltip { + padding: 5px !important; + width: auto !important; + height: auto !important; +} +.bg-teal-gradient { + background: #39cccc !important; + background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #39cccc), color-stop(1, #7adddd)) !important; + background: -ms-linear-gradient(bottom, #39cccc, #7adddd) !important; + background: -moz-linear-gradient(center bottom, #39cccc 0%, #7adddd 100%) !important; + background: -o-linear-gradient(#7adddd, #39cccc) !important; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#7adddd', endColorstr='#39cccc', GradientType=0) !important; + color: #fff; +} +.bg-light-blue-gradient { + background: #3c8dbc !important; + background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #3c8dbc), color-stop(1, #67a8ce)) !important; + background: -ms-linear-gradient(bottom, #3c8dbc, #67a8ce) !important; + background: -moz-linear-gradient(center bottom, #3c8dbc 0%, #67a8ce 100%) !important; + background: -o-linear-gradient(#67a8ce, #3c8dbc) !important; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#67a8ce', endColorstr='#3c8dbc', GradientType=0) !important; + color: #fff; +} +.bg-blue-gradient { + background: #0073b7 !important; + background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #0073b7), color-stop(1, #0089db)) !important; + background: -ms-linear-gradient(bottom, #0073b7, #0089db) !important; + background: -moz-linear-gradient(center bottom, #0073b7 0%, #0089db 100%) !important; + background: -o-linear-gradient(#0089db, #0073b7) !important; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0089db', endColorstr='#0073b7', GradientType=0) !important; + color: #fff; +} +.bg-aqua-gradient { + background: #00c0ef !important; + background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #00c0ef), color-stop(1, #14d1ff)) !important; + background: -ms-linear-gradient(bottom, #00c0ef, #14d1ff) !important; + background: -moz-linear-gradient(center bottom, #00c0ef 0%, #14d1ff 100%) !important; + background: -o-linear-gradient(#14d1ff, #00c0ef) !important; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#14d1ff', endColorstr='#00c0ef', GradientType=0) !important; + color: #fff; +} +.bg-yellow-gradient { + background: #f39c12 !important; + background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #f39c12), color-stop(1, #f7bc60)) !important; + background: -ms-linear-gradient(bottom, #f39c12, #f7bc60) !important; + background: -moz-linear-gradient(center bottom, #f39c12 0%, #f7bc60 100%) !important; + background: -o-linear-gradient(#f7bc60, #f39c12) !important; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f7bc60', endColorstr='#f39c12', GradientType=0) !important; + color: #fff; +} +.bg-purple-gradient { + background: #605ca8 !important; + background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #605ca8), color-stop(1, #9491c4)) !important; + background: -ms-linear-gradient(bottom, #605ca8, #9491c4) !important; + background: -moz-linear-gradient(center bottom, #605ca8 0%, #9491c4 100%) !important; + background: -o-linear-gradient(#9491c4, #605ca8) !important; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#9491c4', endColorstr='#605ca8', GradientType=0) !important; + color: #fff; +} +.bg-green-gradient { + background: #00a65a !important; + background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #00a65a), color-stop(1, #00ca6d)) !important; + background: -ms-linear-gradient(bottom, #00a65a, #00ca6d) !important; + background: -moz-linear-gradient(center bottom, #00a65a 0%, #00ca6d 100%) !important; + background: -o-linear-gradient(#00ca6d, #00a65a) !important; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ca6d', endColorstr='#00a65a', GradientType=0) !important; + color: #fff; +} +.bg-red-gradient { + background: #dd4b39 !important; + background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #dd4b39), color-stop(1, #e47365)) !important; + background: -ms-linear-gradient(bottom, #dd4b39, #e47365) !important; + background: -moz-linear-gradient(center bottom, #dd4b39 0%, #e47365 100%) !important; + background: -o-linear-gradient(#e47365, #dd4b39) !important; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#e47365', endColorstr='#dd4b39', GradientType=0) !important; + color: #fff; +} +.bg-black-gradient { + background: #111111 !important; + background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #111111), color-stop(1, #2b2b2b)) !important; + background: -ms-linear-gradient(bottom, #111111, #2b2b2b) !important; + background: -moz-linear-gradient(center bottom, #111111 0%, #2b2b2b 100%) !important; + background: -o-linear-gradient(#2b2b2b, #111111) !important; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#2b2b2b', endColorstr='#111111', GradientType=0) !important; + color: #fff; +} +.bg-maroon-gradient { + background: #d81b60 !important; + background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #d81b60), color-stop(1, #e73f7c)) !important; + background: -ms-linear-gradient(bottom, #d81b60, #e73f7c) !important; + background: -moz-linear-gradient(center bottom, #d81b60 0%, #e73f7c 100%) !important; + background: -o-linear-gradient(#e73f7c, #d81b60) !important; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#e73f7c', endColorstr='#d81b60', GradientType=0) !important; + color: #fff; +} +.description-block .description-icon { + font-size: 16px; +} +.no-pad-top { + padding-top: 0; +} +.position-static { + position: static !important; +} +.list-header { + font-size: 15px; + padding: 10px 4px; + font-weight: bold; + color: #666; +} +.list-seperator { + height: 1px; + background: #f4f4f4; + margin: 15px 0 9px 0; +} +.list-link > a { + padding: 4px; + color: #777; +} +.list-link > a:hover { + color: #222; +} +.font-light { + font-weight: 300; +} +.user-block:before, +.user-block:after { + content: " "; + display: table; +} +.user-block:after { + clear: both; +} +.user-block img { + width: 40px; + height: 40px; + float: left; +} +.user-block .username, +.user-block .description, +.user-block .comment { + display: block; + margin-left: 50px; +} +.user-block .username { + font-size: 16px; + font-weight: 600; +} +.user-block .description { + color: #999; + font-size: 13px; +} +.user-block.user-block-sm .username, +.user-block.user-block-sm .description, +.user-block.user-block-sm .comment { + margin-left: 40px; +} +.user-block.user-block-sm .username { + font-size: 14px; +} +.img-sm, +.img-md, +.img-lg, +.box-comments .box-comment img, +.user-block.user-block-sm img { + float: left; +} +.img-sm, +.box-comments .box-comment img, +.user-block.user-block-sm img { + width: 30px !important; + height: 30px !important; +} +.img-sm + .img-push { + margin-left: 40px; +} +.img-md { + width: 60px; + height: 60px; +} +.img-md + .img-push { + margin-left: 70px; +} +.img-lg { + width: 100px; + height: 100px; +} +.img-lg + .img-push { + margin-left: 110px; +} +.img-bordered { + border: 3px solid #d2d6de; + padding: 3px; +} +.img-bordered-sm { + border: 2px solid #d2d6de; + padding: 2px; +} +.attachment-block { + border: 1px solid #f4f4f4; + padding: 5px; + margin-bottom: 10px; + background: #f7f7f7; +} +.attachment-block .attachment-img { + max-width: 100px; + max-height: 100px; + height: auto; + float: left; +} +.attachment-block .attachment-pushed { + margin-left: 110px; +} +.attachment-block .attachment-heading { + margin: 0; +} +.attachment-block .attachment-text { + color: #555; +} +.connectedSortable { + min-height: 100px; +} +.ui-helper-hidden-accessible { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} +.sort-highlight { + background: #f4f4f4; + border: 1px dashed #ddd; + margin-bottom: 10px; +} +.full-opacity-hover { + opacity: 0.65; + filter: alpha(opacity=65); +} +.full-opacity-hover:hover { + opacity: 1; + filter: alpha(opacity=100); +} +.chart { + position: relative; + overflow: hidden; + width: 100%; +} +.chart svg, +.chart canvas { + width: 100% !important; +} +/* + * Misc: print + * ----------- + */ +@media print { + .no-print, + .main-sidebar, + .left-side, + .main-header, + .content-header { + display: none !important; + } + .content-wrapper, + .right-side, + .main-footer { + margin-left: 0 !important; + min-height: 0 !important; + -webkit-transform: translate(0, 0) !important; + -ms-transform: translate(0, 0) !important; + -o-transform: translate(0, 0) !important; + transform: translate(0, 0) !important; + } + .fixed .content-wrapper, + .fixed .right-side { + padding-top: 0 !important; + } + .invoice { + width: 100%; + border: 0; + margin: 0; + padding: 0; + } + .invoice-col { + float: left; + width: 33.3333333%; + } + .table-responsive { + overflow: auto; + } + .table-responsive > .table tr th, + .table-responsive > .table tr td { + white-space: normal !important; + } +} diff --git a/public/lib/adminlte/css/skins/skin-blue-light.css b/public/lib/adminlte/css/skins/skin-blue-light.css new file mode 100755 index 0000000000..7e63a6f12c --- /dev/null +++ b/public/lib/adminlte/css/skins/skin-blue-light.css @@ -0,0 +1,164 @@ +/* + * Skin: Blue + * ---------- + */ +.skin-blue-light .main-header .navbar { + background-color: #3c8dbc; +} +.skin-blue-light .main-header .navbar .nav > li > a { + color: #ffffff; +} +.skin-blue-light .main-header .navbar .nav > li > a:hover, +.skin-blue-light .main-header .navbar .nav > li > a:active, +.skin-blue-light .main-header .navbar .nav > li > a:focus, +.skin-blue-light .main-header .navbar .nav .open > a, +.skin-blue-light .main-header .navbar .nav .open > a:hover, +.skin-blue-light .main-header .navbar .nav .open > a:focus, +.skin-blue-light .main-header .navbar .nav > .active > a { + background: rgba(0, 0, 0, 0.1); + color: #f6f6f6; +} +.skin-blue-light .main-header .navbar .sidebar-toggle { + color: #ffffff; +} +.skin-blue-light .main-header .navbar .sidebar-toggle:hover { + color: #f6f6f6; + background: rgba(0, 0, 0, 0.1); +} +.skin-blue-light .main-header .navbar .sidebar-toggle { + color: #fff; +} +.skin-blue-light .main-header .navbar .sidebar-toggle:hover { + background-color: #367fa9; +} +@media (max-width: 767px) { + .skin-blue-light .main-header .navbar .dropdown-menu li.divider { + background-color: rgba(255, 255, 255, 0.1); + } + .skin-blue-light .main-header .navbar .dropdown-menu li a { + color: #fff; + } + .skin-blue-light .main-header .navbar .dropdown-menu li a:hover { + background: #367fa9; + } +} +.skin-blue-light .main-header .logo { + background-color: #3c8dbc; + color: #ffffff; + border-bottom: 0 solid transparent; +} +.skin-blue-light .main-header .logo:hover { + background-color: #3b8ab8; +} +.skin-blue-light .main-header li.user-header { + background-color: #3c8dbc; +} +.skin-blue-light .content-header { + background: transparent; +} +.skin-blue-light .wrapper, +.skin-blue-light .main-sidebar, +.skin-blue-light .left-side { + background-color: #f9fafc; +} +.skin-blue-light .content-wrapper, +.skin-blue-light .main-footer { + border-left: 1px solid #d2d6de; +} +.skin-blue-light .user-panel > .info, +.skin-blue-light .user-panel > .info > a { + color: #444444; +} +.skin-blue-light .sidebar-menu > li { + -webkit-transition: border-left-color 0.3s ease; + -o-transition: border-left-color 0.3s ease; + transition: border-left-color 0.3s ease; +} +.skin-blue-light .sidebar-menu > li.header { + color: #848484; + background: #f9fafc; +} +.skin-blue-light .sidebar-menu > li > a { + border-left: 3px solid transparent; + font-weight: 600; +} +.skin-blue-light .sidebar-menu > li:hover > a, +.skin-blue-light .sidebar-menu > li.active > a { + color: #000000; + background: #f4f4f5; +} +.skin-blue-light .sidebar-menu > li.active { + border-left-color: #3c8dbc; +} +.skin-blue-light .sidebar-menu > li.active > a { + font-weight: 600; +} +.skin-blue-light .sidebar-menu > li > .treeview-menu { + background: #f4f4f5; +} +.skin-blue-light .sidebar a { + color: #444444; +} +.skin-blue-light .sidebar a:hover { + text-decoration: none; +} +.skin-blue-light .treeview-menu > li > a { + color: #777777; +} +.skin-blue-light .treeview-menu > li.active > a, +.skin-blue-light .treeview-menu > li > a:hover { + color: #000000; +} +.skin-blue-light .treeview-menu > li.active > a { + font-weight: 600; +} +.skin-blue-light .sidebar-form { + border-radius: 3px; + border: 1px solid #d2d6de; + margin: 10px 10px; +} +.skin-blue-light .sidebar-form input[type="text"], +.skin-blue-light .sidebar-form .btn { + box-shadow: none; + background-color: #fff; + border: 1px solid transparent; + height: 35px; +} +.skin-blue-light .sidebar-form input[type="text"] { + color: #666; + border-top-left-radius: 2px; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-bottom-left-radius: 2px; +} +.skin-blue-light .sidebar-form input[type="text"]:focus, +.skin-blue-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn { + background-color: #fff; + color: #666; +} +.skin-blue-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn { + border-left-color: #fff; +} +.skin-blue-light .sidebar-form .btn { + color: #999; + border-top-left-radius: 0; + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; + border-bottom-left-radius: 0; +} +@media (min-width: 768px) { + .skin-blue-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu { + border-left: 1px solid #d2d6de; + } +} +.skin-blue-light .main-footer { + border-top-color: #d2d6de; +} +.skin-blue.layout-top-nav .main-header > .logo { + background-color: #3c8dbc; + color: #ffffff; + border-bottom: 0 solid transparent; +} +.skin-blue.layout-top-nav .main-header > .logo:hover { + background-color: #3b8ab8; +} diff --git a/public/lib/adminlte/js/app.js b/public/lib/adminlte/js/app.js new file mode 100755 index 0000000000..31671e3ac5 --- /dev/null +++ b/public/lib/adminlte/js/app.js @@ -0,0 +1,763 @@ +/*! AdminLTE app.js + * ================ + * Main JS application file for AdminLTE v2. This file + * should be included in all pages. It controls some layout + * options and implements exclusive AdminLTE plugins. + * + * @Author Almsaeed Studio + * @Support <http://www.almsaeedstudio.com> + * @Email <abdullah@almsaeedstudio.com> + * @version 2.3.7 + * @license MIT <http://opensource.org/licenses/MIT> + */ + +//Make sure jQuery has been loaded before app.js +if (typeof jQuery === "undefined") { + throw new Error("AdminLTE requires jQuery"); +} + +/* AdminLTE + * + * @type Object + * @description $.AdminLTE is the main object for the template's app. + * It's used for implementing functions and options related + * to the template. Keeping everything wrapped in an object + * prevents conflict with other plugins and is a better + * way to organize our code. + */ +$.AdminLTE = {}; + +/* -------------------- + * - AdminLTE Options - + * -------------------- + * Modify these options to suit your implementation + */ +$.AdminLTE.options = { + //Add slimscroll to navbar menus + //This requires you to load the slimscroll plugin + //in every page before app.js + navbarMenuSlimscroll: true, + navbarMenuSlimscrollWidth: "3px", //The width of the scroll bar + navbarMenuHeight: "200px", //The height of the inner menu + //General animation speed for JS animated elements such as box collapse/expand and + //sidebar treeview slide up/down. This options accepts an integer as milliseconds, + //'fast', 'normal', or 'slow' + animationSpeed: 500, + //Sidebar push menu toggle button selector + sidebarToggleSelector: "[data-toggle='offcanvas']", + //Activate sidebar push menu + sidebarPushMenu: true, + //Activate sidebar slimscroll if the fixed layout is set (requires SlimScroll Plugin) + sidebarSlimScroll: true, + //Enable sidebar expand on hover effect for sidebar mini + //This option is forced to true if both the fixed layout and sidebar mini + //are used together + sidebarExpandOnHover: false, + //BoxRefresh Plugin + enableBoxRefresh: true, + //Bootstrap.js tooltip + enableBSToppltip: true, + BSTooltipSelector: "[data-toggle='tooltip']", + //Enable Fast Click. Fastclick.js creates a more + //native touch experience with touch devices. If you + //choose to enable the plugin, make sure you load the script + //before AdminLTE's app.js + enableFastclick: false, + //Control Sidebar Options + enableControlSidebar: true, + controlSidebarOptions: { + //Which button should trigger the open/close event + toggleBtnSelector: "[data-toggle='control-sidebar']", + //The sidebar selector + selector: ".control-sidebar", + //Enable slide over content + slide: true + }, + //Box Widget Plugin. Enable this plugin + //to allow boxes to be collapsed and/or removed + enableBoxWidget: true, + //Box Widget plugin options + boxWidgetOptions: { + boxWidgetIcons: { + //Collapse icon + collapse: 'fa-minus', + //Open icon + open: 'fa-plus', + //Remove icon + remove: 'fa-times' + }, + boxWidgetSelectors: { + //Remove button selector + remove: '[data-widget="remove"]', + //Collapse button selector + collapse: '[data-widget="collapse"]' + } + }, + //Direct Chat plugin options + directChat: { + //Enable direct chat by default + enable: true, + //The button to open and close the chat contacts pane + contactToggleSelector: '[data-widget="chat-pane-toggle"]' + }, + //Define the set of colors to use globally around the website + colors: { + lightBlue: "#3c8dbc", + red: "#f56954", + green: "#00a65a", + aqua: "#00c0ef", + yellow: "#f39c12", + blue: "#0073b7", + navy: "#001F3F", + teal: "#39CCCC", + olive: "#3D9970", + lime: "#01FF70", + orange: "#FF851B", + fuchsia: "#F012BE", + purple: "#8E24AA", + maroon: "#D81B60", + black: "#222222", + gray: "#d2d6de" + }, + //The standard screen sizes that bootstrap uses. + //If you change these in the variables.less file, change + //them here too. + screenSizes: { + xs: 480, + sm: 768, + md: 992, + lg: 1200 + } +}; + +/* ------------------ + * - Implementation - + * ------------------ + * The next block of code implements AdminLTE's + * functions and plugins as specified by the + * options above. + */ +$(function () { + "use strict"; + + //Fix for IE page transitions + $("body").removeClass("hold-transition"); + + //Extend options if external options exist + if (typeof AdminLTEOptions !== "undefined") { + $.extend(true, + $.AdminLTE.options, + AdminLTEOptions); + } + + //Easy access to options + var o = $.AdminLTE.options; + + //Set up the object + _init(); + + //Activate the layout maker + $.AdminLTE.layout.activate(); + + //Enable sidebar tree view controls + $.AdminLTE.tree('.sidebar'); + + //Enable control sidebar + if (o.enableControlSidebar) { + $.AdminLTE.controlSidebar.activate(); + } + + //Add slimscroll to navbar dropdown + if (o.navbarMenuSlimscroll && typeof $.fn.slimscroll != 'undefined') { + $(".navbar .menu").slimscroll({ + height: o.navbarMenuHeight, + alwaysVisible: false, + size: o.navbarMenuSlimscrollWidth + }).css("width", "100%"); + } + + //Activate sidebar push menu + if (o.sidebarPushMenu) { + $.AdminLTE.pushMenu.activate(o.sidebarToggleSelector); + } + + //Activate Bootstrap tooltip + if (o.enableBSToppltip) { + $('body').tooltip({ + selector: o.BSTooltipSelector + }); + } + + //Activate box widget + if (o.enableBoxWidget) { + $.AdminLTE.boxWidget.activate(); + } + + //Activate fast click + if (o.enableFastclick && typeof FastClick != 'undefined') { + FastClick.attach(document.body); + } + + //Activate direct chat widget + if (o.directChat.enable) { + $(document).on('click', o.directChat.contactToggleSelector, function () { + var box = $(this).parents('.direct-chat').first(); + box.toggleClass('direct-chat-contacts-open'); + }); + } + + /* + * INITIALIZE BUTTON TOGGLE + * ------------------------ + */ + $('.btn-group[data-toggle="btn-toggle"]').each(function () { + var group = $(this); + $(this).find(".btn").on('click', function (e) { + group.find(".btn.active").removeClass("active"); + $(this).addClass("active"); + e.preventDefault(); + }); + + }); +}); + +/* ---------------------------------- + * - Initialize the AdminLTE Object - + * ---------------------------------- + * All AdminLTE functions are implemented below. + */ +function _init() { + 'use strict'; + /* Layout + * ====== + * Fixes the layout height in case min-height fails. + * + * @type Object + * @usage $.AdminLTE.layout.activate() + * $.AdminLTE.layout.fix() + * $.AdminLTE.layout.fixSidebar() + */ + $.AdminLTE.layout = { + activate: function () { + var _this = this; + _this.fix(); + _this.fixSidebar(); + $(window, ".wrapper").resize(function () { + _this.fix(); + _this.fixSidebar(); + }); + }, + fix: function () { + //Get window height and the wrapper height + var neg = $('.main-header').outerHeight() + $('.main-footer').outerHeight(); + var window_height = $(window).height(); + var sidebar_height = $(".sidebar").height(); + //Set the min-height of the content and sidebar based on the + //the height of the document. + if ($("body").hasClass("fixed")) { + $(".content-wrapper, .right-side").css('min-height', window_height - $('.main-footer').outerHeight()); + } else { + var postSetWidth; + if (window_height >= sidebar_height) { + $(".content-wrapper, .right-side").css('min-height', window_height - neg); + postSetWidth = window_height - neg; + } else { + $(".content-wrapper, .right-side").css('min-height', sidebar_height); + postSetWidth = sidebar_height; + } + + //Fix for the control sidebar height + var controlSidebar = $($.AdminLTE.options.controlSidebarOptions.selector); + if (typeof controlSidebar !== "undefined") { + if (controlSidebar.height() > postSetWidth) + $(".content-wrapper, .right-side").css('min-height', controlSidebar.height()); + } + + } + }, + fixSidebar: function () { + //Make sure the body tag has the .fixed class + if (!$("body").hasClass("fixed")) { + if (typeof $.fn.slimScroll != 'undefined') { + $(".sidebar").slimScroll({destroy: true}).height("auto"); + } + return; + } else if (typeof $.fn.slimScroll == 'undefined' && window.console) { + window.console.error("Error: the fixed layout requires the slimscroll plugin!"); + } + //Enable slimscroll for fixed layout + if ($.AdminLTE.options.sidebarSlimScroll) { + if (typeof $.fn.slimScroll != 'undefined') { + //Destroy if it exists + $(".sidebar").slimScroll({destroy: true}).height("auto"); + //Add slimscroll + $(".sidebar").slimscroll({ + height: ($(window).height() - $(".main-header").height()) + "px", + color: "rgba(0,0,0,0.2)", + size: "3px" + }); + } + } + } + }; + + /* PushMenu() + * ========== + * Adds the push menu functionality to the sidebar. + * + * @type Function + * @usage: $.AdminLTE.pushMenu("[data-toggle='offcanvas']") + */ + $.AdminLTE.pushMenu = { + activate: function (toggleBtn) { + //Get the screen sizes + var screenSizes = $.AdminLTE.options.screenSizes; + + //Enable sidebar toggle + $(document).on('click', toggleBtn, function (e) { + e.preventDefault(); + + //Enable sidebar push menu + if ($(window).width() > (screenSizes.sm - 1)) { + if ($("body").hasClass('sidebar-collapse')) { + $("body").removeClass('sidebar-collapse').trigger('expanded.pushMenu'); + } else { + $("body").addClass('sidebar-collapse').trigger('collapsed.pushMenu'); + } + } + //Handle sidebar push menu for small screens + else { + if ($("body").hasClass('sidebar-open')) { + $("body").removeClass('sidebar-open').removeClass('sidebar-collapse').trigger('collapsed.pushMenu'); + } else { + $("body").addClass('sidebar-open').trigger('expanded.pushMenu'); + } + } + }); + + $(".content-wrapper").click(function () { + //Enable hide menu when clicking on the content-wrapper on small screens + if ($(window).width() <= (screenSizes.sm - 1) && $("body").hasClass("sidebar-open")) { + $("body").removeClass('sidebar-open'); + } + }); + + //Enable expand on hover for sidebar mini + if ($.AdminLTE.options.sidebarExpandOnHover + || ($('body').hasClass('fixed') + && $('body').hasClass('sidebar-mini'))) { + this.expandOnHover(); + } + }, + expandOnHover: function () { + var _this = this; + var screenWidth = $.AdminLTE.options.screenSizes.sm - 1; + //Expand sidebar on hover + $('.main-sidebar').hover(function () { + if ($('body').hasClass('sidebar-mini') + && $("body").hasClass('sidebar-collapse') + && $(window).width() > screenWidth) { + _this.expand(); + } + }, function () { + if ($('body').hasClass('sidebar-mini') + && $('body').hasClass('sidebar-expanded-on-hover') + && $(window).width() > screenWidth) { + _this.collapse(); + } + }); + }, + expand: function () { + $("body").removeClass('sidebar-collapse').addClass('sidebar-expanded-on-hover'); + }, + collapse: function () { + if ($('body').hasClass('sidebar-expanded-on-hover')) { + $('body').removeClass('sidebar-expanded-on-hover').addClass('sidebar-collapse'); + } + } + }; + + /* Tree() + * ====== + * Converts the sidebar into a multilevel + * tree view menu. + * + * @type Function + * @Usage: $.AdminLTE.tree('.sidebar') + */ + $.AdminLTE.tree = function (menu) { + var _this = this; + var animationSpeed = $.AdminLTE.options.animationSpeed; + $(document).off('click', menu + ' li a') + .on('click', menu + ' li a', function (e) { + //Get the clicked link and the next element + var $this = $(this); + var checkElement = $this.next(); + + //Check if the next element is a menu and is visible + if ((checkElement.is('.treeview-menu')) && (checkElement.is(':visible')) && (!$('body').hasClass('sidebar-collapse'))) { + //Close the menu + checkElement.slideUp(animationSpeed, function () { + checkElement.removeClass('menu-open'); + //Fix the layout in case the sidebar stretches over the height of the window + //_this.layout.fix(); + }); + checkElement.parent("li").removeClass("active"); + } + //If the menu is not visible + else if ((checkElement.is('.treeview-menu')) && (!checkElement.is(':visible'))) { + //Get the parent menu + var parent = $this.parents('ul').first(); + //Close all open menus within the parent + var ul = parent.find('ul:visible').slideUp(animationSpeed); + //Remove the menu-open class from the parent + ul.removeClass('menu-open'); + //Get the parent li + var parent_li = $this.parent("li"); + + //Open the target menu and add the menu-open class + checkElement.slideDown(animationSpeed, function () { + //Add the class active to the parent li + checkElement.addClass('menu-open'); + parent.find('li.active').removeClass('active'); + parent_li.addClass('active'); + //Fix the layout in case the sidebar stretches over the height of the window + _this.layout.fix(); + }); + } + //if this isn't a link, prevent the page from being redirected + if (checkElement.is('.treeview-menu')) { + e.preventDefault(); + } + }); + }; + + /* ControlSidebar + * ============== + * Adds functionality to the right sidebar + * + * @type Object + * @usage $.AdminLTE.controlSidebar.activate(options) + */ + $.AdminLTE.controlSidebar = { + //instantiate the object + activate: function () { + //Get the object + var _this = this; + //Update options + var o = $.AdminLTE.options.controlSidebarOptions; + //Get the sidebar + var sidebar = $(o.selector); + //The toggle button + var btn = $(o.toggleBtnSelector); + + //Listen to the click event + btn.on('click', function (e) { + e.preventDefault(); + //If the sidebar is not open + if (!sidebar.hasClass('control-sidebar-open') + && !$('body').hasClass('control-sidebar-open')) { + //Open the sidebar + _this.open(sidebar, o.slide); + } else { + _this.close(sidebar, o.slide); + } + }); + + //If the body has a boxed layout, fix the sidebar bg position + var bg = $(".control-sidebar-bg"); + _this._fix(bg); + + //If the body has a fixed layout, make the control sidebar fixed + if ($('body').hasClass('fixed')) { + _this._fixForFixed(sidebar); + } else { + //If the content height is less than the sidebar's height, force max height + if ($('.content-wrapper, .right-side').height() < sidebar.height()) { + _this._fixForContent(sidebar); + } + } + }, + //Open the control sidebar + open: function (sidebar, slide) { + //Slide over content + if (slide) { + sidebar.addClass('control-sidebar-open'); + } else { + //Push the content by adding the open class to the body instead + //of the sidebar itself + $('body').addClass('control-sidebar-open'); + } + }, + //Close the control sidebar + close: function (sidebar, slide) { + if (slide) { + sidebar.removeClass('control-sidebar-open'); + } else { + $('body').removeClass('control-sidebar-open'); + } + }, + _fix: function (sidebar) { + var _this = this; + if ($("body").hasClass('layout-boxed')) { + sidebar.css('position', 'absolute'); + sidebar.height($(".wrapper").height()); + if (_this.hasBindedResize) { + return; + } + $(window).resize(function () { + _this._fix(sidebar); + }); + _this.hasBindedResize = true; + } else { + sidebar.css({ + 'position': 'fixed', + 'height': 'auto' + }); + } + }, + _fixForFixed: function (sidebar) { + sidebar.css({ + 'position': 'fixed', + 'max-height': '100%', + 'overflow': 'auto', + 'padding-bottom': '50px' + }); + }, + _fixForContent: function (sidebar) { + $(".content-wrapper, .right-side").css('min-height', sidebar.height()); + } + }; + + /* BoxWidget + * ========= + * BoxWidget is a plugin to handle collapsing and + * removing boxes from the screen. + * + * @type Object + * @usage $.AdminLTE.boxWidget.activate() + * Set all your options in the main $.AdminLTE.options object + */ + $.AdminLTE.boxWidget = { + selectors: $.AdminLTE.options.boxWidgetOptions.boxWidgetSelectors, + icons: $.AdminLTE.options.boxWidgetOptions.boxWidgetIcons, + animationSpeed: $.AdminLTE.options.animationSpeed, + activate: function (_box) { + var _this = this; + if (!_box) { + _box = document; // activate all boxes per default + } + //Listen for collapse event triggers + $(_box).on('click', _this.selectors.collapse, function (e) { + e.preventDefault(); + _this.collapse($(this)); + }); + + //Listen for remove event triggers + $(_box).on('click', _this.selectors.remove, function (e) { + e.preventDefault(); + _this.remove($(this)); + }); + }, + collapse: function (element) { + var _this = this; + //Find the box parent + var box = element.parents(".box").first(); + //Find the body and the footer + var box_content = box.find("> .box-body, > .box-footer, > form >.box-body, > form > .box-footer"); + if (!box.hasClass("collapsed-box")) { + //Convert minus into plus + element.children(":first") + .removeClass(_this.icons.collapse) + .addClass(_this.icons.open); + //Hide the content + box_content.slideUp(_this.animationSpeed, function () { + box.addClass("collapsed-box"); + }); + } else { + //Convert plus into minus + element.children(":first") + .removeClass(_this.icons.open) + .addClass(_this.icons.collapse); + //Show the content + box_content.slideDown(_this.animationSpeed, function () { + box.removeClass("collapsed-box"); + }); + } + }, + remove: function (element) { + //Find the box parent + var box = element.parents(".box").first(); + box.slideUp(this.animationSpeed); + } + }; +} + +/* ------------------ + * - Custom Plugins - + * ------------------ + * All custom plugins are defined below. + */ + +/* + * BOX REFRESH BUTTON + * ------------------ + * This is a custom plugin to use with the component BOX. It allows you to add + * a refresh button to the box. It converts the box's state to a loading state. + * + * @type plugin + * @usage $("#box-widget").boxRefresh( options ); + */ +(function ($) { + + "use strict"; + + $.fn.boxRefresh = function (options) { + + // Render options + var settings = $.extend({ + //Refresh button selector + trigger: ".refresh-btn", + //File source to be loaded (e.g: ajax/src.php) + source: "", + //Callbacks + onLoadStart: function (box) { + return box; + }, //Right after the button has been clicked + onLoadDone: function (box) { + return box; + } //When the source has been loaded + + }, options); + + //The overlay + var overlay = $('<div class="overlay"><div class="fa fa-refresh fa-spin"></div></div>'); + + return this.each(function () { + //if a source is specified + if (settings.source === "") { + if (window.console) { + window.console.log("Please specify a source first - boxRefresh()"); + } + return; + } + //the box + var box = $(this); + //the button + var rBtn = box.find(settings.trigger).first(); + + //On trigger click + rBtn.on('click', function (e) { + e.preventDefault(); + //Add loading overlay + start(box); + + //Perform ajax call + box.find(".box-body").load(settings.source, function () { + done(box); + }); + }); + }); + + function start(box) { + //Add overlay and loading img + box.append(overlay); + + settings.onLoadStart.call(box); + } + + function done(box) { + //Remove overlay and loading img + box.find(overlay).remove(); + + settings.onLoadDone.call(box); + } + + }; + +})(jQuery); + +/* + * EXPLICIT BOX CONTROLS + * ----------------------- + * This is a custom plugin to use with the component BOX. It allows you to activate + * a box inserted in the DOM after the app.js was loaded, toggle and remove box. + * + * @type plugin + * @usage $("#box-widget").activateBox(); + * @usage $("#box-widget").toggleBox(); + * @usage $("#box-widget").removeBox(); + */ +(function ($) { + + 'use strict'; + + $.fn.activateBox = function () { + $.AdminLTE.boxWidget.activate(this); + }; + + $.fn.toggleBox = function () { + var button = $($.AdminLTE.boxWidget.selectors.collapse, this); + $.AdminLTE.boxWidget.collapse(button); + }; + + $.fn.removeBox = function () { + var button = $($.AdminLTE.boxWidget.selectors.remove, this); + $.AdminLTE.boxWidget.remove(button); + }; + +})(jQuery); + +/* + * TODO LIST CUSTOM PLUGIN + * ----------------------- + * This plugin depends on iCheck plugin for checkbox and radio inputs + * + * @type plugin + * @usage $("#todo-widget").todolist( options ); + */ +(function ($) { + + 'use strict'; + + $.fn.todolist = function (options) { + // Render options + var settings = $.extend({ + //When the user checks the input + onCheck: function (ele) { + return ele; + }, + //When the user unchecks the input + onUncheck: function (ele) { + return ele; + } + }, options); + + return this.each(function () { + + if (typeof $.fn.iCheck != 'undefined') { + $('input', this).on('ifChecked', function () { + var ele = $(this).parents("li").first(); + ele.toggleClass("done"); + settings.onCheck.call(ele); + }); + + $('input', this).on('ifUnchecked', function () { + var ele = $(this).parents("li").first(); + ele.toggleClass("done"); + settings.onUncheck.call(ele); + }); + } else { + $('input', this).on('change', function () { + var ele = $(this).parents("li").first(); + ele.toggleClass("done"); + if ($('input', ele).is(":checked")) { + settings.onCheck.call(ele); + } else { + settings.onUncheck.call(ele); + } + }); + } + }); + }; +}(jQuery)); diff --git a/resources/views/layout/default.twig b/resources/views/layout/default.twig index aff6f9491d..48b28f506e 100644 --- a/resources/views/layout/default.twig +++ b/resources/views/layout/default.twig @@ -118,7 +118,7 @@ <!-- Main content --> <section class="content"> {% if IS_DEMO_SITE %} - <div class="row"> + <div class="row no-print"> <div class="col-lg-12"> <p class="well"> {% include ['demo.' ~ Route.getCurrentRoute.getName, 'demo.no-demo-text'] %} From 9db0e48f63a6025516b88bbfb23316e17b26406e Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 5 Jan 2017 21:32:54 +0100 Subject: [PATCH 525/709] Fixes #526 --- public/js/ff/transactions/create-edit.js | 4 +++- public/js/lib/bootstrap3-typeahead.min.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/public/js/ff/transactions/create-edit.js b/public/js/ff/transactions/create-edit.js index 9719b76d6b..4684f71a45 100644 --- a/public/js/ff/transactions/create-edit.js +++ b/public/js/ff/transactions/create-edit.js @@ -29,9 +29,11 @@ $(document).ready(function () { if ($('input[name="tags"]').length > 0) { $.getJSON('json/tags').done(function (data) { + var opt = { typeahead: { - source: data + source: data, + afterSelect: function(val) { this.$element.val(""); } } }; $('input[name="tags"]').tagsinput( diff --git a/public/js/lib/bootstrap3-typeahead.min.js b/public/js/lib/bootstrap3-typeahead.min.js index 11262083b9..93d4c9252c 100755 --- a/public/js/lib/bootstrap3-typeahead.min.js +++ b/public/js/lib/bootstrap3-typeahead.min.js @@ -1 +1 @@ -!function(a,b){"use strict";"undefined"!=typeof module&&module.exports?module.exports=b(require("jquery")):"function"==typeof define&&define.amd?define(["jquery"],function(a){return b(a)}):b(a.jQuery)}(this,function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.typeahead.defaults,c),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.select=this.options.select||this.select,this.autoSelect="boolean"==typeof this.options.autoSelect?this.options.autoSelect:!0,this.highlighter=this.options.highlighter||this.highlighter,this.render=this.options.render||this.render,this.updater=this.options.updater||this.updater,this.displayText=this.options.displayText||this.displayText,this.source=this.options.source,this.delay=this.options.delay,this.$menu=a(this.options.menu),this.$appendTo=this.options.appendTo?a(this.options.appendTo):null,this.shown=!1,this.listen(),this.showHintOnFocus="boolean"==typeof this.options.showHintOnFocus?this.options.showHintOnFocus:!1,this.afterSelect=this.options.afterSelect,this.addItem=!1};b.prototype={constructor:b,select:function(){var a=this.$menu.find(".active").data("value");if(this.$element.data("active",a),this.autoSelect||a){var b=this.updater(a);b||(b=""),this.$element.val(this.displayText(b)||b).text(this.displayText(b)||b).change(),this.afterSelect(b)}return this.hide()},updater:function(a){return a},setSource:function(a){this.source=a},show:function(){var b,c=a.extend({},this.$element.position(),{height:this.$element[0].offsetHeight}),d="function"==typeof this.options.scrollHeight?this.options.scrollHeight.call():this.options.scrollHeight;b=this.shown?this.$menu:this.$appendTo?this.$menu.appendTo(this.$appendTo):this.$menu.insertAfter(this.$element);var e=a(b).parent().hasClass("dropup"),f=e?"auto":c.top+c.height+d,g=a(b).hasClass("dropdown-menu-right"),h=g?"auto":c.left;return b.css({top:f,left:h}).show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(b){if("undefined"!=typeof b&&null!==b?this.query=b:this.query=this.$element.val()||this.$element.text()||"",this.query.length<this.options.minLength&&!this.options.showHintOnFocus)return this.shown?this.hide():this;var c=a.proxy(function(){a.isFunction(this.source)?this.source(this.query,a.proxy(this.process,this)):this.source&&this.process(this.source)},this);clearTimeout(this.lookupWorker),this.lookupWorker=setTimeout(c,this.delay)},process:function(b){var c=this;return b=a.grep(b,function(a){return c.matcher(a)}),b=this.sorter(b),b.length||this.options.addItem?(b.length>0?this.$element.data("active",b[0]):this.$element.data("active",null),this.options.addItem&&b.push(this.options.addItem),"all"==this.options.items?this.render(b).show():this.render(b.slice(0,this.options.items)).show()):this.shown?this.hide():this},matcher:function(a){var b=this.displayText(a);return~b.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(a){for(var b,c=[],d=[],e=[];b=a.shift();){var f=this.displayText(b);f.toLowerCase().indexOf(this.query.toLowerCase())?~f.indexOf(this.query)?d.push(b):e.push(b):c.push(b)}return c.concat(d,e)},highlighter:function(b){var c,d,e,f,g=a("<div></div>"),h=this.query,i=b.toLowerCase().indexOf(h.toLowerCase()),j=h.length;if(0===j)return g.text(b).html();for(;i>-1;)c=b.substr(0,i),d=b.substr(i,j),e=b.substr(i+j),f=a("<strong></strong>").text(d),g.append(document.createTextNode(c)).append(f),b=e,i=b.toLowerCase().indexOf(h.toLowerCase());return g.append(document.createTextNode(b)).html()},render:function(b){var c=this,d=this,e=!1,f=[],g=c.options.separator;return a.each(b,function(a,c){a>0&&c[g]!==b[a-1][g]&&f.push({__type:"divider"}),!c[g]||0!==a&&c[g]===b[a-1][g]||f.push({__type:"category",name:c[g]}),f.push(c)}),b=a(f).map(function(b,f){if("category"==(f.__type||!1))return a(c.options.headerHtml).text(f.name)[0];if("divider"==(f.__type||!1))return a(c.options.headerDivider)[0];var g=d.displayText(f);return b=a(c.options.item).data("value",f),b.find("a").html(c.highlighter(g,f)),g==d.$element.val()&&(b.addClass("active"),d.$element.data("active",f),e=!0),b[0]}),this.autoSelect&&!e&&(b.filter(":not(.dropdown-header)").first().addClass("active"),this.$element.data("active",b.first().data("value"))),this.$menu.html(b),this},displayText:function(a){return"undefined"!=typeof a&&"undefined"!=typeof a.name&&a.name||a},next:function(b){var c=this.$menu.find(".active").removeClass("active"),d=c.next();d.length||(d=a(this.$menu.find("li")[0])),d.addClass("active")},prev:function(a){var b=this.$menu.find(".active").removeClass("active"),c=b.prev();c.length||(c=this.$menu.find("li").last()),c.addClass("active")},listen:function(){this.$element.on("focus",a.proxy(this.focus,this)).on("blur",a.proxy(this.blur,this)).on("keypress",a.proxy(this.keypress,this)).on("input",a.proxy(this.input,this)).on("keyup",a.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",a.proxy(this.keydown,this)),this.$menu.on("click",a.proxy(this.click,this)).on("mouseenter","li",a.proxy(this.mouseenter,this)).on("mouseleave","li",a.proxy(this.mouseleave,this)).on("mousedown",a.proxy(this.mousedown,this))},mousedown:function(a){this.mouseddown=!0,a.stopPropagation(),a.preventDefault()},destroy:function(){this.$element.data("typeahead",null),this.$element.data("active",null),this.$element.off("focus").off("blur").off("keypress").off("input").off("keyup"),this.eventSupported("keydown")&&this.$element.off("keydown"),this.$menu.remove();this.destroyed=true;},eventSupported:function(a){var b=a in this.$element;return b||(this.$element.setAttribute(a,"return;"),b="function"==typeof this.$element[a]),b},move:function(a){if(this.shown)switch(a.keyCode){case 9:case 13:case 27:a.preventDefault();break;case 38:if(a.shiftKey)return;a.preventDefault(),this.prev();break;case 40:if(a.shiftKey)return;a.preventDefault(),this.next()}},keydown:function(b){this.suppressKeyPressRepeat=~a.inArray(b.keyCode,[40,38,9,13,27]),this.shown||40!=b.keyCode?this.move(b):this.lookup()},keypress:function(a){this.suppressKeyPressRepeat||this.move(a)},input:function(a){this.lookup(),a.preventDefault()},keyup:function(a){if(this.destroyed){return};switch(a.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide()}a.preventDefault()},focus:function(a){this.focused||(this.focused=!0,this.options.showHintOnFocus&&this.lookup())},blur:function(a){this.focused=!1,!this.mousedover&&this.shown&&(this.mouseddown&&a.originalEvent?this.mouseddown=!1:this.hide())},click:function(a){a.preventDefault(),this.select(),this.$element.focus(),this.hide()},mouseenter:function(b){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),a(b.currentTarget).addClass("active")},mouseleave:function(a){this.mousedover=!1}};var c=a.fn.typeahead;a.fn.typeahead=function(c){var d=arguments;return"string"==typeof c&&"getActive"==c?this.data("active"):this.each(function(){var e=a(this),f=e.data("typeahead"),g="object"==typeof c&&c;f||e.data("typeahead",f=new b(this,g)),"string"==typeof c&&f[c]&&(d.length>1?f[c].apply(f,Array.prototype.slice.call(d,1)):f[c]())})},a.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu" role="listbox"></ul>',item:'<li><a class="dropdown-item" href="#" role="option"></a></li>',minLength:1,scrollHeight:0,autoSelect:!0,afterSelect:a.noop,addItem:!1,delay:0,separator:"category",headerHtml:'<li class="dropdown-header"></li>',headerDivider:'<li class="divider" role="separator"></li>'},a.fn.typeahead.Constructor=b,a.fn.typeahead.noConflict=function(){return a.fn.typeahead=c,this},a(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(b){var c=a(this);c.data("typeahead")||c.typeahead(c.data())})}); \ No newline at end of file +(function(root,factory){"use strict";if(typeof module!=="undefined"&&module.exports){module.exports=factory(require("jquery"))}else if(typeof define==="function"&&define.amd){define(["jquery"],function($){return factory($)})}else{factory(root.jQuery)}})(this,function($){"use strict";var Typeahead=function(element,options){this.$element=$(element);this.options=$.extend({},$.fn.typeahead.defaults,options);this.matcher=this.options.matcher||this.matcher;this.sorter=this.options.sorter||this.sorter;this.select=this.options.select||this.select;this.autoSelect=typeof this.options.autoSelect=="boolean"?this.options.autoSelect:true;this.highlighter=this.options.highlighter||this.highlighter;this.render=this.options.render||this.render;this.updater=this.options.updater||this.updater;this.displayText=this.options.displayText||this.displayText;this.source=this.options.source;this.delay=this.options.delay;this.$menu=$(this.options.menu);this.$appendTo=this.options.appendTo?$(this.options.appendTo):null;this.fitToElement=typeof this.options.fitToElement=="boolean"?this.options.fitToElement:false;this.shown=false;this.listen();this.showHintOnFocus=typeof this.options.showHintOnFocus=="boolean"||this.options.showHintOnFocus==="all"?this.options.showHintOnFocus:false;this.afterSelect=this.options.afterSelect;this.addItem=false;this.value=this.$element.val()||this.$element.text()};Typeahead.prototype={constructor:Typeahead,select:function(){var val=this.$menu.find(".active").data("value");this.$element.data("active",val);if(this.autoSelect||val){var newVal=this.updater(val);if(!newVal){newVal=""}this.$element.val(this.displayText(newVal)||newVal).text(this.displayText(newVal)||newVal).change();this.afterSelect(newVal)}return this.hide()},updater:function(item){return item},setSource:function(source){this.source=source},show:function(){var pos=$.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});var scrollHeight=typeof this.options.scrollHeight=="function"?this.options.scrollHeight.call():this.options.scrollHeight;var element;if(this.shown){element=this.$menu}else if(this.$appendTo){element=this.$menu.appendTo(this.$appendTo);this.hasSameParent=this.$appendTo.is(this.$element.parent())}else{element=this.$menu.insertAfter(this.$element);this.hasSameParent=true}if(!this.hasSameParent){element.css("position","fixed");var offset=this.$element.offset();pos.top=offset.top;pos.left=offset.left}var dropup=$(element).parent().hasClass("dropup");var newTop=dropup?"auto":pos.top+pos.height+scrollHeight;var right=$(element).hasClass("dropdown-menu-right");var newLeft=right?"auto":pos.left;element.css({top:newTop,left:newLeft}).show();if(this.options.fitToElement===true){element.css("width",this.$element.outerWidth()+"px")}this.shown=true;return this},hide:function(){this.$menu.hide();this.shown=false;return this},lookup:function(query){var items;if(typeof query!="undefined"&&query!==null){this.query=query}else{this.query=this.$element.val()||this.$element.text()||""}if(this.query.length<this.options.minLength&&!this.options.showHintOnFocus){return this.shown?this.hide():this}var worker=$.proxy(function(){if($.isFunction(this.source)){this.source(this.query,$.proxy(this.process,this))}else if(this.source){this.process(this.source)}},this);clearTimeout(this.lookupWorker);this.lookupWorker=setTimeout(worker,this.delay)},process:function(items){var that=this;items=$.grep(items,function(item){return that.matcher(item)});items=this.sorter(items);if(!items.length&&!this.options.addItem){return this.shown?this.hide():this}if(items.length>0){this.$element.data("active",items[0])}else{this.$element.data("active",null)}if(this.options.addItem){items.push(this.options.addItem)}if(this.options.items=="all"){return this.render(items).show()}else{return this.render(items.slice(0,this.options.items)).show()}},matcher:function(item){var it=this.displayText(item);return~it.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(items){var beginswith=[];var caseSensitive=[];var caseInsensitive=[];var item;while(item=items.shift()){var it=this.displayText(item);if(!it.toLowerCase().indexOf(this.query.toLowerCase()))beginswith.push(item);else if(~it.indexOf(this.query))caseSensitive.push(item);else caseInsensitive.push(item)}return beginswith.concat(caseSensitive,caseInsensitive)},highlighter:function(item){var html=$("<div></div>");var query=this.query;var i=item.toLowerCase().indexOf(query.toLowerCase());var len=query.length;var leftPart;var middlePart;var rightPart;var strong;if(len===0){return html.text(item).html()}while(i>-1){leftPart=item.substr(0,i);middlePart=item.substr(i,len);rightPart=item.substr(i+len);strong=$("<strong></strong>").text(middlePart);html.append(document.createTextNode(leftPart)).append(strong);item=rightPart;i=item.toLowerCase().indexOf(query.toLowerCase())}return html.append(document.createTextNode(item)).html()},render:function(items){var that=this;var self=this;var activeFound=false;var data=[];var _category=that.options.separator;$.each(items,function(key,value){if(key>0&&value[_category]!==items[key-1][_category]){data.push({__type:"divider"})}if(value[_category]&&(key===0||value[_category]!==items[key-1][_category])){data.push({__type:"category",name:value[_category]})}data.push(value)});items=$(data).map(function(i,item){if((item.__type||false)=="category"){return $(that.options.headerHtml).text(item.name)[0]}if((item.__type||false)=="divider"){return $(that.options.headerDivider)[0]}var text=self.displayText(item);i=$(that.options.item).data("value",item);i.find("a").html(that.highlighter(text,item));if(text==self.$element.val()){i.addClass("active");self.$element.data("active",item);activeFound=true}return i[0]});if(this.autoSelect&&!activeFound){items.filter(":not(.dropdown-header)").first().addClass("active");this.$element.data("active",items.first().data("value"))}this.$menu.html(items);return this},displayText:function(item){return typeof item!=="undefined"&&typeof item.name!="undefined"&&item.name||item},next:function(event){var active=this.$menu.find(".active").removeClass("active");var next=active.next();if(!next.length){next=$(this.$menu.find("li")[0])}next.addClass("active")},prev:function(event){var active=this.$menu.find(".active").removeClass("active");var prev=active.prev();if(!prev.length){prev=this.$menu.find("li").last()}prev.addClass("active")},listen:function(){this.$element.on("focus",$.proxy(this.focus,this)).on("blur",$.proxy(this.blur,this)).on("keypress",$.proxy(this.keypress,this)).on("input",$.proxy(this.input,this)).on("keyup",$.proxy(this.keyup,this));if(this.eventSupported("keydown")){this.$element.on("keydown",$.proxy(this.keydown,this))}this.$menu.on("click",$.proxy(this.click,this)).on("mouseenter","li",$.proxy(this.mouseenter,this)).on("mouseleave","li",$.proxy(this.mouseleave,this)).on("mousedown",$.proxy(this.mousedown,this))},destroy:function(){this.$element.data("typeahead",null);this.$element.data("active",null);this.$element.off("focus").off("blur").off("keypress").off("input").off("keyup");if(this.eventSupported("keydown")){this.$element.off("keydown")}this.$menu.remove();this.destroyed=true},eventSupported:function(eventName){var isSupported=eventName in this.$element;if(!isSupported){this.$element.setAttribute(eventName,"return;");isSupported=typeof this.$element[eventName]==="function"}return isSupported},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:if(e.shiftKey)return;e.preventDefault();this.prev();break;case 40:if(e.shiftKey)return;e.preventDefault();this.next();break}},keydown:function(e){this.suppressKeyPressRepeat=~$.inArray(e.keyCode,[40,38,9,13,27]);if(!this.shown&&e.keyCode==40){this.lookup()}else{this.move(e)}},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},input:function(e){var currentValue=this.$element.val()||this.$element.text();if(this.value!==currentValue){this.value=currentValue;this.lookup()}},keyup:function(e){if(this.destroyed){return}switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break}},focus:function(e){if(!this.focused){this.focused=true;if(this.options.showHintOnFocus&&this.skipShowHintOnFocus!==true){if(this.options.showHintOnFocus==="all"){this.lookup("")}else{this.lookup()}}}if(this.skipShowHintOnFocus){this.skipShowHintOnFocus=false}},blur:function(e){if(!this.mousedover&&!this.mouseddown&&this.shown){this.hide();this.focused=false}else if(this.mouseddown){this.skipShowHintOnFocus=true;this.$element.focus();this.mouseddown=false}},click:function(e){e.preventDefault();this.skipShowHintOnFocus=true;this.select();this.$element.focus();this.hide()},mouseenter:function(e){this.mousedover=true;this.$menu.find(".active").removeClass("active");$(e.currentTarget).addClass("active")},mouseleave:function(e){this.mousedover=false;if(!this.focused&&this.shown)this.hide()},mousedown:function(e){this.mouseddown=true;this.$menu.one("mouseup",function(e){this.mouseddown=false}.bind(this))}};var old=$.fn.typeahead;$.fn.typeahead=function(option){var arg=arguments;if(typeof option=="string"&&option=="getActive"){return this.data("active")}return this.each(function(){var $this=$(this);var data=$this.data("typeahead");var options=typeof option=="object"&&option;if(!data)$this.data("typeahead",data=new Typeahead(this,options));if(typeof option=="string"&&data[option]){if(arg.length>1){data[option].apply(data,Array.prototype.slice.call(arg,1))}else{data[option]()}}})};$.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu" role="listbox"></ul>',item:'<li><a class="dropdown-item" href="#" role="option"></a></li>',minLength:1,scrollHeight:0,autoSelect:true,afterSelect:$.noop,addItem:false,delay:0,separator:"category",headerHtml:'<li class="dropdown-header"></li>',headerDivider:'<li class="divider" role="separator"></li>'};$.fn.typeahead.Constructor=Typeahead;$.fn.typeahead.noConflict=function(){$.fn.typeahead=old;return this};$(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(e){var $this=$(this);if($this.data("typeahead"))return;$this.typeahead($this.data())})}); From 3be5cca60ad2fb1b51d060a9c1bba8a33365a0ca Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Fri, 6 Jan 2017 13:54:55 +0100 Subject: [PATCH 526/709] Use Crypt in attachment repository [skip ci] --- app/Repositories/Attachment/AttachmentRepository.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Repositories/Attachment/AttachmentRepository.php b/app/Repositories/Attachment/AttachmentRepository.php index b1e7bbbe78..1bc9401624 100644 --- a/app/Repositories/Attachment/AttachmentRepository.php +++ b/app/Repositories/Attachment/AttachmentRepository.php @@ -19,6 +19,7 @@ use FireflyIII\Models\Attachment; use FireflyIII\User; use Illuminate\Support\Collection; use Storage; +use Crypt; /** * Class AttachmentRepository From 16aa78d13c8cd79166090f56a2ca93cf845decdf Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Fri, 6 Jan 2017 14:50:32 +0100 Subject: [PATCH 527/709] These changes fix #528 --- resources/views/accounts/delete.twig | 2 +- resources/views/bills/delete.twig | 4 ++-- resources/views/budgets/delete.twig | 4 ++-- resources/views/categories/delete.twig | 4 ++-- resources/views/tags/delete.twig | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/resources/views/accounts/delete.twig b/resources/views/accounts/delete.twig index 3fe4850f90..7203d40a25 100644 --- a/resources/views/accounts/delete.twig +++ b/resources/views/accounts/delete.twig @@ -33,7 +33,7 @@ {% endif %} </p> {% endif %} - {% if account.transactions|length > 0 %} + {% if account.transactions.count > 0 %} <p class="text-success"> {{ 'save_transactions_by_moving'|_ }} </p> diff --git a/resources/views/bills/delete.twig b/resources/views/bills/delete.twig index 344779bdc7..650242af27 100644 --- a/resources/views/bills/delete.twig +++ b/resources/views/bills/delete.twig @@ -24,8 +24,8 @@ </p> <p> - {% if bill.transactionjournals|length > 0 %} - {{ Lang.choice('form.bill_keep_transactions', bill.transactionjournals|length,{count: bill.transactionjournals|length}) }} + {% if bill.transactionjournals.count > 0 %} + {{ Lang.choice('form.bill_keep_transactions', bill.transactionjournals.count,{count: bill.transactionjournals.count}) }} {% endif %} </p> </div> diff --git a/resources/views/budgets/delete.twig b/resources/views/budgets/delete.twig index 297cef882a..74590ff0c5 100644 --- a/resources/views/budgets/delete.twig +++ b/resources/views/budgets/delete.twig @@ -25,8 +25,8 @@ </p> <p> - {% if budget.transactionjournals|length > 0 %} - {{ Lang.choice('form.budget_keep_transactions', budget.transactionjournals|length, {count: budget.transactionjournals|length}) }} + {% if budget.transactionjournals.count > 0 %} + {{ Lang.choice('form.budget_keep_transactions', budget.transactionjournals.count, {count: budget.transactionjournals.count }) }} {% endif %} </p> diff --git a/resources/views/categories/delete.twig b/resources/views/categories/delete.twig index ffe9a5b0ef..9766ddb6f0 100644 --- a/resources/views/categories/delete.twig +++ b/resources/views/categories/delete.twig @@ -24,8 +24,8 @@ </p> <p> - {% if category.transactionjournals|length > 0 %} - {{ Lang.choice('form.category_keep_transactions', category.transactionjournals|length, {count: category.transactionjournals|length}) }} + {% if category.transactionjournals.count > 0 %} + {{ Lang.choice('form.category_keep_transactions', category.transactionjournals.count, {count: category.transactionjournals.count }) }} {% endif %} </p> </div> diff --git a/resources/views/tags/delete.twig b/resources/views/tags/delete.twig index e6cd27f096..794c6e7f05 100644 --- a/resources/views/tags/delete.twig +++ b/resources/views/tags/delete.twig @@ -24,8 +24,8 @@ </p> <p> - {% if tag.transactionjournals|length == 0 %} - {{ Lang.choice('form.tag_keep_transactions', tag.transactionjournals|length, {count: tag.transactionjournals|length}) }} + {% if tag.transactionjournals.count > 0 %} + {{ Lang.choice('form.tag_keep_transactions', tag.transactionjournals.count, {count: tag.transactionjournals.count}) }} {% endif %} </p> </div> From bd3c8119bae38fac04f93bcfad767c0ce3a3b70a Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Fri, 6 Jan 2017 14:51:04 +0100 Subject: [PATCH 528/709] =?UTF-8?q?Update=20composer.json=20because=20Twig?= =?UTF-8?q?=20and=20Twigbridge=20aren=E2=80=99t=20playing=20nice.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 4fca5041fe..222bdc6cf0 100755 --- a/composer.json +++ b/composer.json @@ -33,6 +33,7 @@ "watson/validating": "3.*", "doctrine/dbal": "^2.5", "league/commonmark": "0.15.*", + "twig/twig": "1.30.0", "rcrowe/twigbridge": "0.9.*", "league/csv": "8.*", "laravelcollective/html": "^5.3", From 6d398a2edf139d68dc8097f13cc4429924b904b3 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 7 Jan 2017 08:08:37 +0100 Subject: [PATCH 529/709] Added shiny new loading styles. [skip ci] --- CHANGELOG.md | 6 +-- public/js/ff/charts.js | 3 +- public/js/ff/reports/default/all.js | 7 ++- public/js/ff/reports/index.js | 12 +++-- resources/views/reports/default/month.twig | 47 +++++++++++++---- .../views/reports/default/multi-year.twig | 46 +++++++++++++---- resources/views/reports/default/year.twig | 50 +++++++++++++------ resources/views/reports/index.twig | 8 ++- 8 files changed, 130 insertions(+), 49 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a7d24c3a0..8f0bcf5952 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -324,7 +324,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Fixed a bug where a migration would check an empty table name. - Fixed various bugs in the import routine. - Fixed various bugs in the piggy banks pages. -- Fixed a bug in the ``firefly:verify`` routine +- Fixed a bug in the `firefly:verify` routine ## [3.10] - 2015-05-25 ### Added @@ -353,11 +353,11 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Bulk update problems, #280, thanks @stickgrinder - Fixed various problems with amount reporting of split transactions. -[3.9.1] +## [3.9.1] ### Fixed - Fixed a bug where removing money from a piggy bank would not work. See issue #265 and #269 -[3.9.0] +## [3.9.0] ### Added - @zjean has added code that allows you to force "https://"-URL's. - @tonicospinelli has added Portuguese (Brazil) translations. diff --git a/public/js/ff/charts.js b/public/js/ff/charts.js index 0e9ff49d21..d402ab67f8 100644 --- a/public/js/ff/charts.js +++ b/public/js/ff/charts.js @@ -244,8 +244,7 @@ function drawAChart(URI, container, chartType, options, colorData) { $.getJSON(URI).done(function (data) { - - + $('#' + container).removeClass('general-chart-error'); if (data.labels.length === 0) { // remove the chart container + parent var holder = $('#' + container).parent().parent(); diff --git a/public/js/ff/reports/default/all.js b/public/js/ff/reports/default/all.js index 06e374dcee..1f838a0ebc 100644 --- a/public/js/ff/reports/default/all.js +++ b/public/js/ff/reports/default/all.js @@ -74,7 +74,8 @@ function loadAjaxPartial(holder, uri) { function displayAjaxPartial(data, holder) { "use strict"; var obj = $('#' + holder); - obj.removeClass('loading').html(data); + obj.html(data); + obj.parent().find('.overlay').remove(); // call some often needed recalculations and what-not: @@ -98,7 +99,9 @@ function displayAjaxPartial(data, holder) { function failAjaxPartial(uri, holder) { "use strict"; - $('#' + holder).removeClass('loading').addClass('general-chart-error'); + var holder = $('#' + holder); + holder.parent().find('.overlay').remove(); + holder.addClass('general-chart-error'); } diff --git a/public/js/ff/reports/index.js b/public/js/ff/reports/index.js index d59c0b3d30..77c15f0578 100644 --- a/public/js/ff/reports/index.js +++ b/public/js/ff/reports/index.js @@ -60,14 +60,18 @@ $(function () { function getReportOptions() { "use strict"; var reportType = $('select[name="report_type"]').val(); - $('#extra-options').empty(); - $('#extra-options').addClass('loading'); + var boxBody = $('#extra-options'); + var box = $('#extra-options-box'); + boxBody.empty(); + box.find('.overlay').show(); $.getJSON('reports/options/' + reportType, function (data) { - $('#extra-options').removeClass('loading').html(data.html); + boxBody.html(data.html); setOptionalFromCookies(); + box.find('.overlay').hide(); }).fail(function () { - $('#extra-options').removeClass('loading').addClass('error'); + boxBody.addClass('error'); + box.find('.overlay').hide(); }); } diff --git a/resources/views/reports/default/month.twig b/resources/views/reports/default/month.twig index 22a7ebe73d..c06a6e75e3 100644 --- a/resources/views/reports/default/month.twig +++ b/resources/views/reports/default/month.twig @@ -20,12 +20,16 @@ </div> <div class="row"> - <div class="col-lg-6 col-md-6 col-sm-6 loading"> + <div class="col-lg-6 col-md-6 col-sm-6"> <div class="box"> <div class="box-header with-border"> <h3 class="box-title">{{ 'accountBalances'|_ }}</h3> </div> - <div class="box-body table-responsive no-padding loading" id="accountReport"> + <div class="box-body table-responsive no-padding" id="accountReport"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> @@ -36,7 +40,11 @@ <div class="box-header with-border"> <h3 class="box-title">{{ 'income'|_ }}</h3> </div> - <div class="box-body table-responsive no-padding loading" id="incomeReport"> + <div class="box-body table-responsive no-padding" id="incomeReport"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> </div> @@ -45,7 +53,11 @@ <div class="box-header with-border"> <h3 class="box-title">{{ 'expenses'|_ }}</h3> </div> - <div class="box-body table-responsive no-padding loading" id="expenseReport"> + <div class="box-body table-responsive no-padding" id="expenseReport"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> </div> @@ -56,14 +68,15 @@ <div class="box-header with-border"> <h3 class="box-title">{{ 'incomeVsExpenses'|_ }}</h3> </div> - <div class="box-body table-responsive no-padding loading" id="incomeVsExpenseReport"> + <div class="box-body table-responsive no-padding" id="incomeVsExpenseReport"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> </div> - <div class="col-lg-6 col-md-6 col-sm-6"> - {% include 'reports/partials/tags' %} - </div> </div> <div class="row"> <div class="col-lg-8 col-md-8 col-sm-12"> @@ -72,7 +85,11 @@ <div class="box-header with-border"> <h3 class="box-title">{{ 'budgets'|_ }}</h3> </div> - <div class="box-body table-responsive no-padding loading" id="budgetReport"> + <div class="box-body table-responsive no-padding" id="budgetReport"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> @@ -84,7 +101,11 @@ <div class="box-header with-border"> <h3 class="box-title">{{ 'categories'|_ }}</h3> </div> - <div class="box-body table-responsive no-padding loading" id="categoryReport"> + <div class="box-body table-responsive no-padding" id="categoryReport"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> </div> @@ -100,7 +121,11 @@ <div class="box-header with-border"> <h3 class="box-title">{{ 'budgets'|_ }} ({{ 'splitByAccount'|_|lower }})</h3> </div> - <div class="box-body table-responsive no-padding loading" id="balanceReport"> + <div class="box-body table-responsive no-padding" id="balanceReport"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> diff --git a/resources/views/reports/default/multi-year.twig b/resources/views/reports/default/multi-year.twig index 7b70416735..a24e2b5f27 100644 --- a/resources/views/reports/default/multi-year.twig +++ b/resources/views/reports/default/multi-year.twig @@ -31,12 +31,16 @@ <div class="row"> - <div class="col-lg-6 col-md-6 col-sm-6 loading"> + <div class="col-lg-6 col-md-6 col-sm-6"> <div class="box"> <div class="box-header with-border"> <h3 class="box-title">{{ 'accountBalances'|_ }}</h3> </div> - <div class="box-body table-responsive no-padding loading" id="accountReport"> + <div class="box-body table-responsive no-padding" id="accountReport"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> </div> @@ -45,7 +49,11 @@ <div class="box-header with-border"> <h3 class="box-title">{{ 'income'|_ }}</h3> </div> - <div class="box-body table-responsive no-padding loading" id="incomeReport"> + <div class="box-body table-responsive no-padding" id="incomeReport"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> </div> @@ -54,7 +62,11 @@ <div class="box-header with-border"> <h3 class="box-title">{{ 'expenses'|_ }}</h3> </div> - <div class="box-body table-responsive no-padding loading" id="expenseReport"> + <div class="box-body table-responsive no-padding" id="expenseReport"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> </div> @@ -65,7 +77,11 @@ <div class="box-header with-border"> <h3 class="box-title">{{ 'incomeVsExpenses'|_ }}</h3> </div> - <div class="box-body table-responsive no-padding loading" id="incomeVsExpenseReport"> + <div class="box-body table-responsive no-padding" id="incomeVsExpenseReport"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> </div> @@ -92,7 +108,11 @@ <div class="box-header with-border"> <h3 class="box-title">{{ 'budgets'|_ }}</h3> </div> - <div class="box-body no-padding table-responsive loading" id="budgetPeriodReport"> + <div class="box-body no-padding table-responsive" id="budgetPeriodReport"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> </div> @@ -108,7 +128,6 @@ </div> <div class="box-body"> <canvas height="400" id="budget_chart" style="width:100%;height:400px;"></canvas> - </div> </div> </div> @@ -121,7 +140,11 @@ <div class="box-header with-border"> <h3 class="box-title">{{ 'categories'|_ }} ({{ 'expenses'|_ }})</h3> </div> - <div class="box-body no-padding table-responsive loading" id="categoryExpense"> + <div class="box-body no-padding table-responsive" id="categoryExpense"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> </div> @@ -134,7 +157,11 @@ <div class="box-header with-border"> <h3 class="box-title">{{ 'categories'|_ }} ({{ 'income'|_ }})</h3> </div> - <div class="box-body no-padding table-responsive loading" id="categoryIncome"> + <div class="box-body no-padding table-responsive" id="categoryIncome"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> </div> @@ -149,7 +176,6 @@ </div> <div class="box-body"> <canvas height="400" id="category_chart" style="width:100%;height:400px;"></canvas> - </div> </div> </div> diff --git a/resources/views/reports/default/year.twig b/resources/views/reports/default/year.twig index 328536ef03..79d9ea29b3 100644 --- a/resources/views/reports/default/year.twig +++ b/resources/views/reports/default/year.twig @@ -35,14 +35,22 @@ <div class="box-header with-border"> <h3 class="box-title">{{ 'accountBalances'|_ }}</h3> </div> - <div class="box-body table-responsive no-padding loading" id="accountReport"> + <div class="box-body table-responsive no-padding" id="accountReport"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> <div class="box"> <div class="box-header with-border"> <h3 class="box-title">{{ 'incomeVsExpenses'|_ }}</h3> </div> - <div class="box-body table-responsive no-padding loading" id="incomeVsExpenseReport"> + <div class="box-body table-responsive no-padding" id="incomeVsExpenseReport"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> </div> @@ -51,7 +59,11 @@ <div class="box-header with-border"> <h3 class="box-title">{{ 'income'|_ }}</h3> </div> - <div class="box-body table-responsive no-padding loading" id="incomeReport"> + <div class="box-body table-responsive no-padding" id="incomeReport"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> </div> @@ -60,17 +72,15 @@ <div class="box-header with-border"> <h3 class="box-title">{{ 'expenses'|_ }}</h3> </div> - <div class="box-body table-responsive no-padding loading" id="expenseReport"> + <div class="box-body table-responsive no-padding" id="expenseReport"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> </div> </div> - <div class="row"> - <div class="col-lg-6 col-md-6 col-sm-6"> - {% include 'reports/partials/tags' %} - </div> - </div> - <div class="row"> <div class="col-lg-12 col-md-12 col-sm-12"> <div class="box"> @@ -91,7 +101,11 @@ <div class="box-header with-border"> <h3 class="box-title">{{ 'budgets'|_ }}</h3> </div> - <div class="box-body no-padding table-responsive loading" id="budgetPeriodReport"> + <div class="box-body no-padding table-responsive" id="budgetPeriodReport"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> </div> @@ -107,7 +121,6 @@ </div> <div class="box-body"> <canvas height="400" id="budget_chart" style="width:100%;height:400px;"></canvas> - </div> </div> </div> @@ -120,7 +133,11 @@ <div class="box-header with-border"> <h3 class="box-title">{{ 'categories'|_ }} ({{ 'expenses'|_ }})</h3> </div> - <div class="box-body no-padding table-responsive loading" id="categoryExpense"> + <div class="box-body no-padding table-responsive" id="categoryExpense"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> </div> @@ -133,7 +150,11 @@ <div class="box-header with-border"> <h3 class="box-title">{{ 'categories'|_ }} ({{ 'income'|_ }})</h3> </div> - <div class="box-body no-padding table-responsive loading" id="categoryIncome"> + <div class="box-body no-padding table-responsive" id="categoryIncome"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> </div> @@ -148,7 +169,6 @@ </div> <div class="box-body"> <canvas height="400" id="category_chart" style="width:100%;height:400px;"></canvas> - </div> </div> </div> diff --git a/resources/views/reports/index.twig b/resources/views/reports/index.twig index 70d8fd86db..9d3ddeffe9 100644 --- a/resources/views/reports/index.twig +++ b/resources/views/reports/index.twig @@ -84,11 +84,15 @@ </div> </div> - <div class="box"> + <div class="box" id="extra-options-box"> <div class="box-header with-border"> <h3 class="box-title">{{ 'reports_extra_options'|_ }}</h3> </div> - <div class="box-body loading" id="extra-options"> + <div class="box-body" id="extra-options"> + </div> + {# loading indicator #} + <div class="overlay"> + <i class="fa fa-refresh fa-spin"></i> </div> </div> From 71e31346e8a46b6055b5b40f0c67c73107c4de80 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 8 Jan 2017 10:19:10 +0100 Subject: [PATCH 530/709] Better views for #475 --- app/Http/Controllers/AccountController.php | 36 ++++---- app/Http/Controllers/BudgetController.php | 95 +++++++++++++-------- app/Http/Controllers/CategoryController.php | 18 ++-- resources/views/accounts/show.twig | 18 ++-- resources/views/budgets/show.twig | 87 ++++++++++--------- resources/views/categories/show.twig | 8 +- 6 files changed, 152 insertions(+), 110 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index af0fd7b24f..42eec4e59b 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -75,12 +75,9 @@ class AccountController extends Controller $defaultCurrency = Amount::getDefaultCurrency(); $subTitleIcon = config('firefly.subIconsByIdentifier.' . $what); $subTitle = trans('firefly.make_new_' . $what . '_account'); - Session::flash( - 'preFilled', - [ - 'currency_id' => $defaultCurrency->id, - ] - ); + + // pre fill some data + Session::flash('preFilled', ['currency_id' => $defaultCurrency->id,]); // put previous url in session if not redirect from store (not "create another"). if (session('accounts.create.fromStore') !== true) { @@ -248,6 +245,7 @@ class AccountController extends Controller $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $chartUri = route('chart.account.single', [$account->id]); + $accountType = $account->accountType->type; // grab those journals: $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page); @@ -257,7 +255,7 @@ class AccountController extends Controller // generate entries for each period (and cache those) $entries = $this->periodEntries($account); - return view('accounts.show', compact('account', 'entries', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end', 'chartUri')); + return view('accounts.show', compact('account', 'accountType', 'entries', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end', 'chartUri')); } /** @@ -298,14 +296,15 @@ class AccountController extends Controller */ public function showByDate(Request $request, Account $account, string $date) { - $carbon = new Carbon($date); - $range = Preferences::get('viewRange', '1M')->data; - $start = Navigation::startOfPeriod($carbon, $range); - $end = Navigation::endOfPeriod($carbon, $range); - $subTitle = $account->name . ' (' . Navigation::periodShow($start, $range) . ')'; - $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); - $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); - $chartUri = route('chart.account.period', [$account->id, $carbon->format('Y-m-d')]); + $carbon = new Carbon($date); + $range = Preferences::get('viewRange', '1M')->data; + $start = Navigation::startOfPeriod($carbon, $range); + $end = Navigation::endOfPeriod($carbon, $range); + $subTitle = $account->name . ' (' . Navigation::periodShow($start, $range) . ')'; + $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); + $chartUri = route('chart.account.period', [$account->id, $carbon->format('Y-m-d')]); + $accountType = $account->accountType->type; // replace with journal collector: /** @var JournalCollectorInterface $collector */ @@ -314,8 +313,11 @@ class AccountController extends Controller $journals = $collector->getPaginatedJournals(); $journals->setPath('accounts/show/' . $account->id . '/' . $date); + // generate entries for each period (and cache those) + $entries = $this->periodEntries($account); + // same call, except "entries". - return view('accounts.show', compact('account', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end', 'chartUri')); + return view('accounts.show', compact('account', 'accountType', 'entries', 'subTitleIcon', 'journals', 'subTitle', 'start', 'end', 'chartUri')); } /** @@ -442,7 +444,7 @@ class AccountController extends Controller $earned = $tasker->amountInInPeriod(new Collection([$account]), $assets, $end, $currentEnd); $dateStr = $end->format('Y-m-d'); $dateName = Navigation::periodShow($end, $range); - $entries->push([$dateStr, $dateName, $spent, $earned]); + $entries->push([$dateStr, $dateName, $spent, $earned, clone $end]); $end = Navigation::subtractPeriod($end, $range, 1); } diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index fce86e46a1..d9b5cc2a27 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -24,6 +24,7 @@ use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Support\CacheProperties; use Illuminate\Http\Request; use Illuminate\Support\Collection; use Preferences; @@ -64,20 +65,19 @@ class BudgetController extends Controller } /** - * @param Request $request - * @param BudgetRepositoryInterface $repository - * @param Budget $budget + * @param Request $request + * @param Budget $budget * * @return \Illuminate\Http\JsonResponse */ - public function amount(Request $request, BudgetRepositoryInterface $repository, Budget $budget) + public function amount(Request $request, Budget $budget) { $amount = intval($request->get('amount')); /** @var Carbon $start */ $start = session('start', Carbon::now()->startOfMonth()); /** @var Carbon $end */ $end = session('end', Carbon::now()->endOfMonth()); - $budgetLimit = $repository->updateLimitAmount($budget, $start, $end, $amount); + $budgetLimit = $this->repository->updateLimitAmount($budget, $start, $end, $amount); if ($amount == 0) { $budgetLimit = null; } @@ -122,17 +122,16 @@ class BudgetController extends Controller } /** - * @param Budget $budget - * @param BudgetRepositoryInterface $repository + * @param Budget $budget * * @return \Illuminate\Http\RedirectResponse */ - public function destroy(Budget $budget, BudgetRepositoryInterface $repository) + public function destroy(Budget $budget) { $name = $budget->name; $budgetId = $budget->id; - $repository->destroy($budget); + $this->repository->destroy($budget); Session::flash('success', strval(trans('firefly.deleted_budget', ['name' => e($name)]))); @@ -238,21 +237,19 @@ class BudgetController extends Controller } /** - * @param Request $request - * @param BudgetRepositoryInterface $repository - * @param AccountRepositoryInterface $accountRepository - * @param Budget $budget + * @param Request $request + * @param Budget $budget * * @return View */ - public function show(Request $request, BudgetRepositoryInterface $repository, AccountRepositoryInterface $accountRepository, Budget $budget) + public function show(Request $request, Budget $budget) { /** @var Carbon $start */ $start = session('first', Carbon::create()->startOfYear()); $end = new Carbon; $page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); - $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]); + $limits = $this->getLimits($budget, $start, $end); $repetition = null; // collector: /** @var JournalCollectorInterface $collector */ @@ -262,15 +259,7 @@ class BudgetController extends Controller $journals->setPath('/budgets/show/' . $budget->id); - $set = $repository->getBudgetLimits($budget, $start, $end); $subTitle = e($budget->name); - $limits = new Collection(); - - /** @var BudgetLimit $entry */ - foreach ($set as $entry) { - $entry->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $entry->start_date, $entry->end_date); - $limits->push($entry); - } return view('budgets.show', compact('limits', 'budget', 'repetition', 'journals', 'subTitle')); } @@ -289,8 +278,6 @@ class BudgetController extends Controller throw new FireflyException('This budget limit is not part of this budget.'); } - /** @var BudgetRepositoryInterface $repository */ - $repository = app(BudgetRepositoryInterface::class); /** @var AccountRepositoryInterface $accountRepository */ $accountRepository = app(AccountRepositoryInterface::class); $page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page')); @@ -313,23 +300,23 @@ class BudgetController extends Controller $journals->setPath('/budgets/show/' . $budget->id . '/' . $budgetLimit->id); - $budgetLimit->spent = $repository->spentInPeriod(new Collection([$budget]), $accounts, $budgetLimit->start_date, $budgetLimit->end_date); - $limits = new Collection([$budgetLimit]); + $start = session('first', Carbon::create()->startOfYear()); + $end = new Carbon; + $limits = $this->getLimits($budget, $start, $end); return view('budgets.show', compact('limits', 'budget', 'budgetLimit', 'journals', 'subTitle')); } /** - * @param BudgetFormRequest $request - * @param BudgetRepositoryInterface $repository + * @param BudgetFormRequest $request * * @return \Illuminate\Http\RedirectResponse */ - public function store(BudgetFormRequest $request, BudgetRepositoryInterface $repository) + public function store(BudgetFormRequest $request) { $data = $request->getBudgetData(); - $budget = $repository->store($data); + $budget = $this->repository->store($data); Session::flash('success', strval(trans('firefly.stored_new_budget', ['name' => e($budget->name)]))); Preferences::mark(); @@ -347,16 +334,15 @@ class BudgetController extends Controller } /** - * @param BudgetFormRequest $request - * @param BudgetRepositoryInterface $repository - * @param Budget $budget + * @param BudgetFormRequest $request + * @param Budget $budget * * @return \Illuminate\Http\RedirectResponse */ - public function update(BudgetFormRequest $request, BudgetRepositoryInterface $repository, Budget $budget) + public function update(BudgetFormRequest $request, Budget $budget) { $data = $request->getBudgetData(); - $repository->update($budget, $data); + $this->repository->update($budget, $data); Session::flash('success', strval(trans('firefly.updated_budget', ['name' => e($budget->name)]))); Preferences::mark(); @@ -430,4 +416,41 @@ class BudgetController extends Controller return $return; } + + /** + * @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(); + } + + /** @var AccountRepositoryInterface $accountRepository */ + $accountRepository = app(AccountRepositoryInterface::class); + $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]); + $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]), $accounts, $entry->start_date, $entry->end_date); + $limits->push($entry); + } + $cache->store($limits); + + return $set; + } + } diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index 3eac58d095..2a52505818 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -193,14 +193,16 @@ class CategoryController extends Controller $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $subTitle = $category->name; $subTitleIcon = 'fa-bar-chart'; + $entries = $this->getGroupedEntries($category); + $method = 'default'; + // get journals $collector->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->setRange($start, $end)->setCategory($category)->withBudgetInformation(); $journals = $collector->getPaginatedJournals(); $journals->setPath('categories/show/' . $category->id); - $entries = $this->getGroupedEntries($category); - return view('categories.show', compact('category', 'journals', 'entries', 'hideCategory', 'subTitle', 'subTitleIcon', 'start', 'end')); + return view('categories.show', compact('category', 'method', 'journals', 'entries', 'hideCategory', 'subTitle', 'subTitleIcon', 'start', 'end')); } /** @@ -223,7 +225,7 @@ class CategoryController extends Controller $hideCategory = true; // used in list. $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); - $showAll = true; + $method = 'all'; /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); @@ -231,7 +233,7 @@ class CategoryController extends Controller $journals = $collector->getPaginatedJournals(); $journals->setPath('categories/show/' . $category->id . '/all'); - return view('categories.show', compact('category', 'journals', 'hideCategory', 'subTitle', 'subTitleIcon', 'start', 'end', 'showAll')); + return view('categories.show', compact('category', 'method', 'journals', 'hideCategory', 'subTitle', 'subTitleIcon', 'start', 'end')); } /** @@ -252,6 +254,8 @@ class CategoryController extends Controller $hideCategory = true; // used in list. $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); + $entries = $this->getGroupedEntries($category); + $method = 'date'; /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); @@ -259,7 +263,7 @@ class CategoryController extends Controller $journals = $collector->getPaginatedJournals(); $journals->setPath('categories/show/' . $category->id . '/' . $date); - return view('categories.show', compact('category', 'journals', 'hideCategory', 'subTitle', 'subTitleIcon', 'start', 'end')); + return view('categories.show', compact('category', 'method', 'entries', 'journals', 'hideCategory', 'subTitle', 'subTitleIcon', 'start', 'end')); } /** @@ -320,7 +324,7 @@ class CategoryController extends Controller private function getGroupedEntries(Category $category): Collection { /** @var CategoryRepositoryInterface $repository */ - $repository = app(CategoryRepositoryInterface::class); + $repository = app(CategoryRepositoryInterface::class); /** @var AccountRepositoryInterface $accountRepository */ $accountRepository = app(AccountRepositoryInterface::class); $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); @@ -350,7 +354,7 @@ class CategoryController extends Controller $earned = $repository->earnedInPeriod(new Collection([$category]), $accounts, $end, $currentEnd); $dateStr = $end->format('Y-m-d'); $dateName = Navigation::periodShow($end, $range); - $entries->push([$dateStr, $dateName, $spent, $earned]); + $entries->push([$dateStr, $dateName, $spent, $earned, clone $end]); $end = Navigation::subtractPeriod($end, $range, 1); } $cache->store($entries); diff --git a/resources/views/accounts/show.twig b/resources/views/accounts/show.twig index e1f7cc7e20..4d9d3444a8 100644 --- a/resources/views/accounts/show.twig +++ b/resources/views/accounts/show.twig @@ -67,6 +67,13 @@ </div> </div> </div> +{% if entries %} + <div class="row"> + <div class="col-lg-offset-10 col-lg-2 col-md-offset-10 col-md-2 col-sm-12 col-xs-12"> + <p class="small text-center"><a href="{{ route('accounts.show.all',[account.id]) }}">{{ 'showEverything'|_ }}</a></p> + </div> + </div> +{% endif %} <div class="row"> <div class="{% if entries %}col-lg-10 col-md-10 col-sm-12{% else %}col-lg-12 col-md-12 col-sm-12{% endif %}"> @@ -96,22 +103,23 @@ </div> {% if entries %} <div class="col-lg-2 col-md-2 col-sm-12 col-xs-12"> + {% for entry in entries %} - {% if entry[2] != 0 or entry[3] != 0 %} - <div class="box"> + {% if (entry[2] != 0 or entry[3] != 0) or (accountType == 'Asset account') %} + <div class="box {% if entry[4] == start %}box-solid box-primary{% endif %}"> <div class="box-header with-border"> <h3 class="box-title"><a href="{{ route('accounts.show.date',[account.id,entry[0]]) }}">{{ entry[1] }}</a> </h3> </div> <div class="box-body no-padding"> <table class="table table-hover"> - {% if entry[2] != 0 %} + {% if entry[2] != 0 or (accountType == 'Asset account') %} <tr> <td style="width:33%;">{{ 'spent'|_ }}</td> <td style="text-align: right;">{{ entry[2]|formatAmount }}</td> </tr> {% endif %} - {% if entry[3] != 0 %} + {% if entry[3] != 0 or (accountType == 'Asset account') %} <tr> <td style="width: 33%;">{{ 'earned'|_ }}</td> <td style="text-align: right;">{{ entry[3]|formatAmount }}</td> @@ -121,8 +129,8 @@ </div> </div> {% endif %} - {% endfor %} + <p class="small text-center"><a href="{{ route('accounts.show.all',[account.id]) }}">{{ 'showEverything'|_ }}</a></p> </div> {% endif %} </div> diff --git a/resources/views/budgets/show.twig b/resources/views/budgets/show.twig index fc7e71c7b5..e1750adff8 100644 --- a/resources/views/budgets/show.twig +++ b/resources/views/budgets/show.twig @@ -6,7 +6,7 @@ {% block content %} <div class="row"> - <div class="col-lg-9 col-md-9 col-sm-7"> + <div class="col-lg-12 col-md-12 col-sm-12"> <div class="box"> <div class="box-header with-border"> <h3 class="box-title">{{ 'overview'|_ }}</h3> @@ -26,6 +26,17 @@ <canvas id="budgetOverview" style="width:100%;" height="400"></canvas> </div> </div> + </div> + </div> + + <div class="row"> + <div class="col-lg-offset-9 col-lg-3 col-md-offset-9 col-md-3 col-sm-12 col-xs-12"> + <p class="small text-center"><a href="{{ route('budgets.show',budget.id) }}">{{ 'showEverything'|_ }}</a></p> + </div> + </div> + + <div class="row"> + <div class="col-lg-9 col-md-9 col-sm-12 col-xs-12"> <div class="box"> <div class="box-header with-border"> @@ -36,13 +47,9 @@ </div> </div> </div> - <div class="col-lg-3 col-md-3 col-sm-5"> - {% if limits|length == 1 %} - <p class="small text-center"><a href="{{ route('budgets.show',budget.id) }}">{{ 'showEverything'|_ }}</a></p> - {% endif %} - + <div class="col-lg-3 col-md-3 col-sm-12 col-xs-12"> {% for limit in limits %} - <div class="box"> + <div class="box {% if limit.start_date == budgetLimit.start_date %}box-primary box-solid{% endif %}"> <div class="box-header with-border"> <h3 class="box-title"><a href="{{ route('budgets.show.limit',[budget.id,limit.id]) }}"> @@ -51,44 +58,42 @@ </a> </h3> </div> - <div class="box-body"> - <div class="row"> - <div class="col-lg-6 col-md-6 col-sm-6"> - {{ 'amount'|_ }}: {{ limit.amount|formatAmount }} - </div> - <div class="col-lg-6 col-md-6 col-sm-6"> - {{ 'spent'|_ }}: {{ limit.spent|formatAmount }} - </div> - </div> - <div class="row"> - <div class="col-lg-12 col-md-12 col-sm-12"> - {% set overspent = limit.amount + limit.spent < 0 %} + <div class="box-body no-padding"> + <table class="table table-hover"> + <tr> + <td style="width:33%;">{{ 'amount'|_ }}</td> + <td>{{ limit.amount|formatAmount }}</td> + </tr> + <tr> + <td style="width:33%;">{{ 'spent'|_ }}</td> + <td>{{ limit.spent|formatAmount }}</td> + </tr> + <tr> + <td colspan="2"> + {% set overspent = limit.amount + limit.spent < 0 %} - {% if overspent %} - {% set pct = (limit.spent != 0 ? (limit.amount / (limit.spent*-1))*100 : 0) %} <!-- must have -1 here --> - <div class="progress progress-striped"> - <div class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="{{ pct|round }}" aria-valuemin="0" - aria-valuemax="100" style="width: {{ pct|round }}%;"></div> - <div class="progress-bar progress-bar-danger" role="progressbar" aria-valuenow="{{ (100-pct)|round }}" - aria-valuemin="0" aria-valuemax="100" style="width: {{ (100-pct)|round }}%;"></div> - </div> - {% else %} - {% set pct = (limit.amount != 0 ? (((limit.spent*-1) / limit.amount)*100) : 0) %} <!-- must have -1 here --> - <div class="progress progress-striped"> - <div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="{{ pct|round }}" aria-valuemin="0" - aria-valuemax="100" style="width: {{ pct|round }}%;"></div> - </div> - {% endif %} - </div> - </div> + {% if overspent %} + {% set pct = (limit.spent != 0 ? (limit.amount / (limit.spent*-1))*100 : 0) %} <!-- must have -1 here --> + <div class="progress progress-striped"> + <div class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="{{ pct|round }}" aria-valuemin="0" + aria-valuemax="100" style="width: {{ pct|round }}%;"></div> + <div class="progress-bar progress-bar-danger" role="progressbar" aria-valuenow="{{ (100-pct)|round }}" + aria-valuemin="0" aria-valuemax="100" style="width: {{ (100-pct)|round }}%;"></div> + </div> + {% else %} + {% set pct = (limit.amount != 0 ? (((limit.spent*-1) / limit.amount)*100) : 0) %} <!-- must have -1 here --> + <div class="progress progress-striped"> + <div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="{{ pct|round }}" aria-valuemin="0" + aria-valuemax="100" style="width: {{ pct|round }}%;"></div> + </div> + {% endif %} + </td> + </tr> + </table> </div> </div> {% endfor %} - - {% if limits|length == 1 %} - <p class="small text-center"><a href="{{ route('budgets.show',budget.id) }}">{{ 'showEverything'|_ }}</a></p> - {% endif %} - + <p class="small text-center"><a href="{{ route('budgets.show',budget.id) }}">{{ 'showEverything'|_ }}</a></p> </div> </div> diff --git a/resources/views/categories/show.twig b/resources/views/categories/show.twig index f824d79b75..50ce16bb6e 100644 --- a/resources/views/categories/show.twig +++ b/resources/views/categories/show.twig @@ -6,7 +6,7 @@ {% block content %} <div class="row"> - {% if entries %} + {% if method == 'default' %} {# both charts #} <div class="col-lg-6 col-md-6 col-sm-12"> <div class="box"> @@ -29,7 +29,7 @@ </div> </div> {% endif %} - {% if not entries and not showAll %} + {% if method == 'date' %} {# single chart #} <div class="col-lg-12 col-md-12 col-sm-12"> <div class="box"> @@ -42,7 +42,7 @@ </div> </div> {% endif %} - {% if not entries and showAll %} + {% if method == 'all' %} {# all chart #} <div class="col-lg-12 col-md-12 col-sm-12"> <div class="box"> @@ -87,7 +87,7 @@ <div class="col-lg-2 col-md-4 col-sm-12 col-xs-12"> {% for entry in entries %} {% if entry[2] != 0 or entry[3] != 0 %} - <div class="box"> + <div class="box {% if entry[4] == start %}box-solid box-primary{% endif %}"> <div class="box-header with-border"> <h3 class="box-title"><a href="{{ route('categories.show.date',[category.id,entry[0]]) }}">{{ entry[1] }}</a> </h3> From 01aba73f5ba03092b274ff6eda9eafe32418a62e Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 8 Jan 2017 10:20:59 +0100 Subject: [PATCH 531/709] =?UTF-8?q?Forgot=20=E2=80=9Cshow=20all=E2=80=9D?= =?UTF-8?q?=20link=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/views/categories/show.twig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/resources/views/categories/show.twig b/resources/views/categories/show.twig index 50ce16bb6e..c798a6b587 100644 --- a/resources/views/categories/show.twig +++ b/resources/views/categories/show.twig @@ -56,6 +56,14 @@ </div> {% endif %} </div> + {% if entries %} + <div class="row"> + <div class="col-lg-offset-10 col-lg-2 col-md-offset-10 col-md-2 col-sm-12 col-xs-12"> + <p class="small text-center"><a href="{{ route('categories.show.all',[category.id]) }}">{{ 'showEverything'|_ }}</a></p> + </div> + </div> + {% endif %} + <div class="row"> <div class="{% if entries %}col-lg-10 col-md-8 col-sm-12 col-xs-12{% else %}col-lg-12 col-md-12 col-sm-12 col-xs-12{% endif %}"> From 8a00101470894a7148a327b23c261359689d1eee Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 8 Jan 2017 11:41:09 +0100 Subject: [PATCH 532/709] Fix tests, remove some logging. --- app/Support/Amount.php | 1 - tests/acceptance/Controllers/BudgetControllerTest.php | 1 + tests/acceptance/Controllers/CategoryControllerTest.php | 6 ++++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/Support/Amount.php b/app/Support/Amount.php index 8294a53021..fa2ae5e236 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -53,7 +53,6 @@ class Amount { $locale = explode(',', trans('config.locale')); $locale = array_map('trim', $locale); - Log::debug('formatAnything(). Will set locale to', $locale); setlocale(LC_MONETARY, $locale); $float = round($amount, 12); $info = localeconv(); diff --git a/tests/acceptance/Controllers/BudgetControllerTest.php b/tests/acceptance/Controllers/BudgetControllerTest.php index 2057ce68f8..25f0f3b91a 100644 --- a/tests/acceptance/Controllers/BudgetControllerTest.php +++ b/tests/acceptance/Controllers/BudgetControllerTest.php @@ -215,6 +215,7 @@ class BudgetControllerTest extends TestCase // 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); diff --git a/tests/acceptance/Controllers/CategoryControllerTest.php b/tests/acceptance/Controllers/CategoryControllerTest.php index 3b546635d3..da294075d4 100644 --- a/tests/acceptance/Controllers/CategoryControllerTest.php +++ b/tests/acceptance/Controllers/CategoryControllerTest.php @@ -193,6 +193,12 @@ class CategoryControllerTest extends TestCase $collector->shouldReceive('setCategory')->andReturnSelf()->once(); $collector->shouldReceive('getPaginatedJournals')->andReturn(new LengthAwarePaginator([], 0, 10))->once(); + // mock category repository + $repository = $this->mock(CategoryRepositoryInterface::class); + $repository->shouldReceive('firstUseDate')->once()->andReturn(new Carbon); + $repository->shouldReceive('spentInPeriod')->andReturn('-1'); + $repository->shouldReceive('earnedInPeriod')->andReturn('1'); + $this->be($this->user()); $this->changeDateRange($this->user(), $range); From 8208d44466f32f00a737c529f7dafbf16e85d998 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 8 Jan 2017 16:55:02 +0100 Subject: [PATCH 533/709] =?UTF-8?q?This=20should=20fix=20locale=20informat?= =?UTF-8?q?ion=20for=20specific=20languages.=20It=E2=80=99s=20not=20perfec?= =?UTF-8?q?t=20yet=20though.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Middleware/Range.php | 36 +++++++++++++++++++++++------ public/js/ff/firefly.js | 4 ++-- resources/views/layout/default.twig | 1 + 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/app/Http/Middleware/Range.php b/app/Http/Middleware/Range.php index 5e3e119098..6b9b250f39 100644 --- a/app/Http/Middleware/Range.php +++ b/app/Http/Middleware/Range.php @@ -23,7 +23,6 @@ use Illuminate\Contracts\Auth\Guard; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Navigation; -use NumberFormatter; use Preferences; use Session; use View; @@ -111,20 +110,43 @@ class Range $monthAndDayFormat = (string)trans('config.month_and_day'); $dateTimeFormat = (string)trans('config.date_time'); $defaultCurrency = Amount::getDefaultCurrency(); + $localeconv = localeconv(); - // change localeconv to a new array: - $numberFormatter = numfmt_create($lang, NumberFormatter::CURRENCY); - $localeconv = [ - 'mon_decimal_point' => $numberFormatter->getSymbol($numberFormatter->getAttribute(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL)), - 'mon_thousands_sep' => $numberFormatter->getSymbol($numberFormatter->getAttribute(NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL)), - 'frac_digits' => $defaultCurrency->decimal_places, + // decimal places is overruled by TransactionCurrency + $localeconv['frac_digits'] = $defaultCurrency->decimal_places; + $positiveSpace = ' '; + $negativeSpace = ' '; + // positive number: + if (!$localeconv['p_sep_by_space']) { + $positiveSpace = ''; + } + // negative number: + if (!$localeconv['n_sep_by_space']) { + $negativeSpace = ''; + } + // by default, put symbols before: + $accounting = [ + 'pos' => '%s' . $positiveSpace . '%v', + 'neg' => '%s' . $negativeSpace . '-%v', + 'zero' => '%s' . $positiveSpace . '%v', ]; + + // but might be after: + if (!$localeconv['n_cs_precedes']) { + $accounting['neg'] = '-%v' . $negativeSpace . '%s'; + } + if (!$localeconv['p_cs_precedes']) { + $accounting['pos'] = '%v' . $negativeSpace . '%s'; + $accounting['zero'] = '%v' . $negativeSpace . '%s'; + } + View::share('monthFormat', $monthFormat); View::share('monthAndDayFormat', $monthAndDayFormat); View::share('dateTimeFormat', $dateTimeFormat); View::share('language', $lang); View::share('localeconv', $localeconv); View::share('defaultCurrency', $defaultCurrency); + View::share('accountingConfig', $accounting); } /** diff --git a/public/js/ff/firefly.js b/public/js/ff/firefly.js index 82a22f16c1..4f33376896 100644 --- a/public/js/ff/firefly.js +++ b/public/js/ff/firefly.js @@ -7,7 +7,7 @@ * * See the LICENSE file for details. */ -/** global: moment, dateRangeConfig, accounting, currencySymbol, mon_decimal_point, frac_digits, showFullList, showOnlyTop, mon_thousands_sep */ +/** global: moment, accountingConfig, dateRangeConfig, accounting, currencySymbol, mon_decimal_point, frac_digits, showFullList, showOnlyTop, mon_thousands_sep */ $(function () { @@ -112,7 +112,7 @@ function currencySelect(e) { accounting.settings = { currency: { symbol: currencySymbol, // default currency symbol is '$' - format: "%s %v", // controls output: %s = symbol, %v = value/number (can be object: see below) + format: accountingConfig, // controls output: %s = symbol, %v = value/number (can be object: see below) decimal: mon_decimal_point, // decimal point separator thousand: mon_thousands_sep, // thousands separator precision: frac_digits // decimal places diff --git a/resources/views/layout/default.twig b/resources/views/layout/default.twig index 48b28f506e..85b8f95e3b 100644 --- a/resources/views/layout/default.twig +++ b/resources/views/layout/default.twig @@ -208,6 +208,7 @@ var noDataForChart = '{{ trans('firefly.no_data_for_chart')|escape }}'; var showFullList = '{{ trans('firefly.show_full_list') }}'; var showOnlyTop = '{{ trans('firefly.show_only_top',{number:listLength}) }}'; + var accountingConfig = {{ accountingConfig|json_encode|raw }}; </script> From 19990f49b0fe4d2565d286afd445f57b84d86f84 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 8 Jan 2017 17:54:52 +0100 Subject: [PATCH 534/709] Update amount thing, simpler code (I hope). Includes config for negative values. --- app/Http/Middleware/Range.php | 26 +--------- app/Support/Amount.php | 94 +++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 25 deletions(-) diff --git a/app/Http/Middleware/Range.php b/app/Http/Middleware/Range.php index 6b9b250f39..4371d473d7 100644 --- a/app/Http/Middleware/Range.php +++ b/app/Http/Middleware/Range.php @@ -111,34 +111,10 @@ class Range $dateTimeFormat = (string)trans('config.date_time'); $defaultCurrency = Amount::getDefaultCurrency(); $localeconv = localeconv(); + $accounting = Amount::getJsConfig($localeconv); // decimal places is overruled by TransactionCurrency $localeconv['frac_digits'] = $defaultCurrency->decimal_places; - $positiveSpace = ' '; - $negativeSpace = ' '; - // positive number: - if (!$localeconv['p_sep_by_space']) { - $positiveSpace = ''; - } - // negative number: - if (!$localeconv['n_sep_by_space']) { - $negativeSpace = ''; - } - // by default, put symbols before: - $accounting = [ - 'pos' => '%s' . $positiveSpace . '%v', - 'neg' => '%s' . $negativeSpace . '-%v', - 'zero' => '%s' . $positiveSpace . '%v', - ]; - - // but might be after: - if (!$localeconv['n_cs_precedes']) { - $accounting['neg'] = '-%v' . $negativeSpace . '%s'; - } - if (!$localeconv['p_cs_precedes']) { - $accounting['pos'] = '%v' . $negativeSpace . '%s'; - $accounting['zero'] = '%v' . $negativeSpace . '%s'; - } View::share('monthFormat', $monthFormat); View::share('monthAndDayFormat', $monthAndDayFormat); diff --git a/app/Support/Amount.php b/app/Support/Amount.php index fa2ae5e236..de7fcce12f 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -27,6 +27,80 @@ use Preferences as Prefs; */ class Amount { + /** + * bool $sepBySpace is $localeconv['n_sep_by_space'] + * int $signPosn = $localeconv['n_sign_posn'] + * string $sign = $localeconv['negative_sign'] + * bool $csPrecedes = $localeconv['n_cs_precedes'] + * + * @param bool $sepBySpace + * @param int $signPosn + * @param string $sign + * @param bool $csPrecedes + * + * @return string + */ + public static function getAmountJsConfig(bool $sepBySpace, int $signPosn, string $sign, bool $csPrecedes): string + { + // negative first: + $space = ' '; + + // require space between symbol and amount? + if (!$sepBySpace) { + $space = ''; // no + } + + // there are five possible positions for the "+" or "-" sign (if it is even used) + // pos_a and pos_e could be the ( and ) symbol. + $pos_a = ''; // before everything + $pos_b = ''; // before currency symbol + $pos_c = ''; // after currency symbol + $pos_d = ''; // before amount + $pos_e = ''; // after everything + + // format would be (currency before amount) + // AB%sC_D%vE + // or: + // AD%v_B%sCE (amount before currency) + // the _ is the optional space + + + // switch on how to display amount: + switch ($signPosn) { + default: + case 0: + // ( and ) around the whole thing + $pos_a = '('; + $pos_e = ')'; + break; + case 1: + // The sign string precedes the quantity and currency_symbol + $pos_a = $sign; + break; + case 2: + // The sign string succeeds the quantity and currency_symbol + $pos_e = $sign; + break; + case 3: + // The sign string immediately precedes the currency_symbol + $pos_b = $sign; + break; + case 4: + // The sign string immediately succeeds the currency_symbol + $pos_c = $sign; + } + + // default: (amount before currency) + $format = $pos_a . $pos_d . '%v' . $space . $pos_b . '%s' . $pos_c . $pos_e; + + if ($csPrecedes) { + // (currency before amount) + $format = $pos_a . $pos_b . '%s' . $pos_c . $space . $pos_d . '%v' . $pos_e; + } + Log::debug(sprintf('Final format: "%s"', $format)); + + return $format; + } /** * @param string $amount @@ -198,4 +272,24 @@ class Amount return $currency; } + + /** + * This method returns the correct format rules required by accounting.js, + * the library used to format amounts in charts. + * + * @param array $config + * + * @return array + */ + public function getJsConfig(array $config): array + { + $negative = self::getAmountJsConfig($config['n_sep_by_space'] === 1, $config['n_sign_posn'], $config['negative_sign'], $config['n_cs_precedes'] === 1); + $positive = self::getAmountJsConfig($config['p_sep_by_space'] === 1, $config['p_sign_posn'], $config['positive_sign'], $config['p_cs_precedes'] === 1); + + return [ + 'pos' => $positive, + 'neg' => $negative, + 'zero' => $positive, + ]; + } } From 78b71e72f1bedc02c2c744bf9e4b8e3eef88c039 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 8 Jan 2017 18:23:07 +0100 Subject: [PATCH 535/709] Fix amount remove from piggy bug. --- app/Http/Controllers/PiggyBankController.php | 2 +- resources/lang/en_US/firefly.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/PiggyBankController.php b/app/Http/Controllers/PiggyBankController.php index f67464f475..47357c5bc0 100644 --- a/app/Http/Controllers/PiggyBankController.php +++ b/app/Http/Controllers/PiggyBankController.php @@ -324,7 +324,7 @@ class PiggyBankController extends Controller $savedSoFar = $piggyBank->currentRelevantRep()->currentamount; - if (bccomp($amount, $savedSoFar) === -1) { + if (bccomp($amount, $savedSoFar) <= 0) { $repetition = $piggyBank->currentRelevantRep(); $repetition->currentamount = bcsub($repetition->currentamount, $amount); $repetition->save(); diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index ec41d10bf2..61367d9b2c 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -805,6 +805,7 @@ return [ 'add_set_amount_to_piggy' => 'Add :amount to fill this piggy bank on :date', 'delete_piggy_bank' => 'Delete piggy bank ":name"', 'cannot_add_amount_piggy' => 'Could not add :amount to ":name".', + 'cannot_remove_from_piggy' => 'Could not remove :amount from ":name".', 'deleted_piggy_bank' => 'Deleted piggy bank ":name"', 'added_amount_to_piggy' => 'Added :amount to ":name"', 'removed_amount_from_piggy' => 'Removed :amount from ":name"', From 373b9cdd9fedaa854a4dc258e9c9cd4f3c0b956a Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 9 Jan 2017 17:57:29 +0100 Subject: [PATCH 536/709] A better tag overview as preparation for #525 --- app/Http/Controllers/TagController.php | 111 +++++++++++++++--- app/Repositories/Tag/TagRepository.php | 68 +++++++++++ .../Tag/TagRepositoryInterface.php | 33 ++++++ resources/views/tags/show.twig | 44 ++++++- routes/web.php | 4 +- 5 files changed, 236 insertions(+), 24 deletions(-) diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index c000fd6fd5..f155623fdc 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -13,13 +13,17 @@ declare(strict_types = 1); namespace FireflyIII\Http\Controllers; +use Carbon\Carbon; +use Exception; use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Http\Requests\TagFormRequest; use FireflyIII\Models\Tag; use FireflyIII\Models\Transaction; use FireflyIII\Repositories\Tag\TagRepositoryInterface; +use FireflyIII\Support\CacheProperties; use Illuminate\Http\Request; use Illuminate\Support\Collection; +use Navigation; use Preferences; use Session; use URL; @@ -41,8 +45,12 @@ use View; class TagController extends Controller { + /** @var array */ public $tagOptions = []; + /** @var TagRepositoryInterface */ + protected $repository; + /** * */ @@ -53,15 +61,19 @@ class TagController extends Controller $this->middleware( function ($request, $next) { - View::share('title', 'Tags'); - View::share('mainTitleIcon', 'fa-tags'); + $this->repository = app(TagRepositoryInterface::class); $this->tagOptions = [ 'nothing' => trans('firefly.regular_tag'), 'balancingAct' => trans('firefly.balancing_act'), 'advancePayment' => trans('firefly.advance_payment'), ]; + + + View::share('title', strval(trans('firefly.tags'))); + View::share('mainTitleIcon', 'fa-tags'); View::share('tagOptions', $this->tagOptions); + return $next($request); } ); @@ -113,16 +125,15 @@ class TagController extends Controller } /** - * @param TagRepositoryInterface $repository - * @param Tag $tag + * @param Tag $tag * * @return \Illuminate\Http\RedirectResponse */ - public function destroy(TagRepositoryInterface $repository, Tag $tag) + public function destroy(Tag $tag) { $tagName = $tag->tag; - $repository->destroy($tag); + $this->repository->destroy($tag); Session::flash('success', strval(trans('firefly.deleted_tag', ['tag' => e($tagName)]))); Preferences::mark(); @@ -216,15 +227,34 @@ class TagController extends Controller * * @return View */ - public function show(Request $request, JournalCollectorInterface $collector, Tag $tag) + public function show(Request $request, JournalCollectorInterface $collector, Tag $tag, string $moment = '') { + $range = Preferences::get('viewRange', '1M')->data; + + if (strlen($moment) > 0) { + try { + $start = new Carbon($moment); + $end = Navigation::endOfPeriod($start, $range); + } catch (Exception $e) { + $start = Navigation::startOfPeriod($this->repository->firstUseDate($tag), $range); + $end = Navigation::startOfPeriod($this->repository->lastUseDate($tag), $range); + } + } + if (strlen($moment) === 0) { + $start = clone session('start', Carbon::now()->startOfMonth()); + $end = clone session('end', Carbon::now()->endOfMonth()); + } + $subTitle = $tag->tag; $subTitleIcon = 'fa-tag'; $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); + $periods = $this->getPeriodOverview($tag); // use collector: - $collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page)->setTag($tag)->withBudgetInformation()->withCategoryInformation(); + $collector->setAllAssetAccounts() + ->setLimit($pageSize)->setPage($page)->setTag($tag) + ->withBudgetInformation()->withCategoryInformation()->setRange($start, $end); $journals = $collector->getPaginatedJournals(); $journals->setPath('tags/show/' . $tag->id); @@ -234,20 +264,18 @@ class TagController extends Controller } ); - return view('tags.show', compact('tag', 'subTitle', 'subTitleIcon', 'journals', 'sum')); + return view('tags.show', compact('tag', 'periods', 'subTitle', 'subTitleIcon', 'journals', 'sum', 'start', 'end')); } /** - * @param TagFormRequest $request - * - * @param TagRepositoryInterface $repository + * @param TagFormRequest $request * * @return \Illuminate\Http\RedirectResponse */ - public function store(TagFormRequest $request, TagRepositoryInterface $repository) + public function store(TagFormRequest $request) { $data = $request->collectTagData(); - $repository->store($data); + $this->repository->store($data); Session::flash('success', strval(trans('firefly.created_tag', ['tag' => e($data['tag'])]))); Preferences::mark(); @@ -265,16 +293,15 @@ class TagController extends Controller } /** - * @param TagFormRequest $request - * @param TagRepositoryInterface $repository - * @param Tag $tag + * @param TagFormRequest $request + * @param Tag $tag * * @return \Illuminate\Http\RedirectResponse */ - public function update(TagFormRequest $request, TagRepositoryInterface $repository, Tag $tag) + public function update(TagFormRequest $request, Tag $tag) { $data = $request->collectTagData(); - $repository->update($tag, $data); + $this->repository->update($tag, $data); Session::flash('success', strval(trans('firefly.updated_tag', ['tag' => e($data['tag'])]))); Preferences::mark(); @@ -289,4 +316,50 @@ class TagController extends Controller // redirect to previous URL. return redirect(session('tags.edit.url')); } + + /** + * @param Tag $tag + * + * @return Collection + */ + private function getPeriodOverview(Tag $tag): Collection + { + // get first and last tag date from tag: + $range = Preferences::get('viewRange', '1M')->data; + $start = Navigation::startOfPeriod($this->repository->firstUseDate($tag), $range); + $end = Navigation::startOfPeriod($this->repository->lastUseDate($tag), $range); + // properties for entries with their amounts. + $cache = new CacheProperties; + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('tag.entries'); + $cache->addProperty($tag->id); + + if ($cache->has()) { + return $cache->get(); + } + + $collection = new Collection; + + // while end larger or equal to start + while ($end >= $start) { + $currentEnd = Navigation::endOfPeriod($end, $range); + + // get expenses and what-not in this period and this tag. + $arr = [ + 'date_string' => $end->format('Y-m-d'), + 'date_name' => Navigation::periodShow($end, $range), + 'date' => $end, + 'spent' => $this->repository->spentInperiod($tag, $end, $currentEnd), + 'earned' => $this->repository->earnedInperiod($tag, $end, $currentEnd), + ]; + $collection->push($arr); + + $end = Navigation::subtractPeriod($end, $range, 1); + } + $cache->store($collection); + + return $collection; + + } } diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index dd6e341328..8145be1151 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -14,6 +14,8 @@ declare(strict_types = 1); namespace FireflyIII\Repositories\Tag; +use Carbon\Carbon; +use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Models\Tag; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; @@ -121,6 +123,21 @@ class TagRepository implements TagRepositoryInterface return new Tag; } + /** + * @param Tag $tag + * + * @return Carbon + */ + public function firstUseDate(Tag $tag): Carbon + { + $journal = $tag->transactionJournals()->orderBy('date', 'ASC')->first(); + if (!is_null($journal)) { + return $journal->date; + } + + return new Carbon; + } + /** * @return Collection */ @@ -137,6 +154,21 @@ class TagRepository implements TagRepositoryInterface return $tags; } + /** + * @param Tag $tag + * + * @return Carbon + */ + public function lastUseDate(Tag $tag): Carbon + { + $journal = $tag->transactionJournals()->orderBy('date', 'DESC')->first(); + if (!is_null($journal)) { + return $journal->date; + } + + return new Carbon; + } + /** * @param array $data * @@ -353,4 +385,40 @@ class TagRepository implements TagRepositoryInterface return false; } + + /** + * @param Tag $tag + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function earnedInPeriod(Tag $tag, Carbon $start, Carbon $end): string + { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class, [$this->user]); + $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAllAssetAccounts()->setTag($tag); + $set = $collector->getJournals(); + $sum = strval($set->sum('transaction_amount')); + + return $sum; + } + + /** + * @param Tag $tag + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function spentInPeriod(Tag $tag, Carbon $start, Carbon $end): string + { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class, [$this->user]); + $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAllAssetAccounts()->setTag($tag); + $set = $collector->getJournals(); + $sum = strval($set->sum('transaction_amount')); + + return $sum; + } } diff --git a/app/Repositories/Tag/TagRepositoryInterface.php b/app/Repositories/Tag/TagRepositoryInterface.php index 3da24ab718..64bf47c44e 100644 --- a/app/Repositories/Tag/TagRepositoryInterface.php +++ b/app/Repositories/Tag/TagRepositoryInterface.php @@ -13,6 +13,7 @@ declare(strict_types = 1); namespace FireflyIII\Repositories\Tag; +use Carbon\Carbon; use FireflyIII\Models\Tag; use FireflyIII\Models\TransactionJournal; use Illuminate\Support\Collection; @@ -44,6 +45,15 @@ interface TagRepositoryInterface */ public function destroy(Tag $tag): bool; + /** + * @param Tag $tag + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function earnedInPeriod(Tag $tag, Carbon $start, Carbon $end): string; + /** * @param int $tagId * @@ -58,6 +68,13 @@ interface TagRepositoryInterface */ public function findByTag(string $tag): Tag; + /** + * @param Tag $tag + * + * @return Carbon + */ + public function firstUseDate(Tag $tag): Carbon; + /** * This method returns all the user's tags. * @@ -65,6 +82,22 @@ interface TagRepositoryInterface */ public function get(): Collection; + /** + * @param Tag $tag + * + * @return Carbon + */ + public function lastUseDate(Tag $tag): Carbon; + + /** + * @param Tag $tag + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function spentInPeriod(Tag $tag, Carbon $start, Carbon $end): string; + /** * This method stores a tag. * diff --git a/resources/views/tags/show.twig b/resources/views/tags/show.twig index e78b69f5b1..720934948e 100644 --- a/resources/views/tags/show.twig +++ b/resources/views/tags/show.twig @@ -76,7 +76,12 @@ </div> </div> <div class="row"> - <div class="col-lg-612 col-md-12 col-sm-12 col-xs-12"> + <div class="col-lg-offset-10 col-lg-2 col-md-offset-10 col-md-2 col-sm-12 col-xs-12"> + <p class="small text-center"><a href="{{ route('tags.show',[tag.id,'all']) }}">{{ 'showEverything'|_ }}</a></p> + </div> + </div> + <div class="row"> + <div class="col-lg-10 col-md-10 col-sm-12 col-xs-12"> <div class="box"> <div class="box-header with-border"> <h3 class="box-title">{{ 'transactions'|_ }}</h3> @@ -99,11 +104,42 @@ </div> </div> </div> + <div class="col-lg-2 col-md-2 col-sm-12 col-xs-12"> + {% for period in periods %} + {% if period.spent != 0 or period.earned != 0 %} + <div class="box {% if period.date == start %}box-solid box-primary{% endif %}"> + <div class="box-header with-border"> + <h3 class="box-title"><a href="{{ route('tags.show',[tag.id, period.date_string]) }}">{{ period.date_name }}</a> + </h3> + </div> + <div class="box-body no-padding"> + <table class="table table-hover"> + {% if period.spent != 0 %} + <tr> + <td style="width:33%;">{{ 'spent'|_ }}</td> + <td style="text-align: right;">{{ period.spent|formatAmount }}</td> + </tr> + {% endif %} + {% if period.earned != 0 %} + <tr> + <td style="width:33%;">{{ 'earned'|_ }}</td> + <td style="text-align: right;">{{ period.earned|formatAmount }}</td> + </tr> + {% endif %} + </table> + </div> + </div> + {% endif %} + + {% endfor %} + </div> + </div> + <div class="row"> + <div class="col-lg-offset-10 col-lg-2 col-md-offset-10 col-md-2 col-sm-12 col-xs-12"> + <p class="small text-center"><a href="{{ route('tags.show',[tag.id,'all']) }}">{{ 'showEverything'|_ }}</a></p> + </div> </div> {% endblock %} {% block scripts %} - <script type="text/javascript"> - var tagID = {{ tag.id }}; - </script> {% endblock %} diff --git a/routes/web.php b/routes/web.php index 8b50c86560..71793df647 100755 --- a/routes/web.php +++ b/routes/web.php @@ -586,7 +586,9 @@ Route::group( Route::get('', ['uses' => 'TagController@index', 'as' => 'index']); Route::get('create', ['uses' => 'TagController@create', 'as' => 'create']); - Route::get('show/{tag}', ['uses' => 'TagController@show', 'as' => 'show']); + + Route::get('show/{tag}/{date?}', ['uses' => 'TagController@show', 'as' => 'show']); + Route::get('edit/{tag}', ['uses' => 'TagController@edit', 'as' => 'edit']); Route::get('delete/{tag}', ['uses' => 'TagController@delete', 'as' => 'delete']); From 6fb9362f7e7186d780bae99400b97ea3df0e9f61 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 9 Jan 2017 18:17:03 +0100 Subject: [PATCH 537/709] Updated changelog [skip ci] --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f0bcf5952..7e278c4994 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,25 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [4.3.2] - 2017-01-09 + +An intermediate release because something in the Twig and Twigbridge libraries is broken and I have to make sure it doesn't affect you guys. But some cool features were on their way so there's that oo. + +### Added +- Some code for issue #475, consistent overviews. +- Better currency display. Make sure you have locale packages installed. + +### Changed +- Uses a new version of Laravel. + +### Fixed +- The password reset routine was broken. +- Issue #522, thanks to @xpfgsyb +- Issue #524, thanks to @worldworm +- Issue #526, thanks to @worldworm +- Issue #528, thanks to @skibbipl +- Various other fixes. + ## [4.3.1] - 2017-01-04 ### Added - Support for Russian and Polish. From 7949c9ad74d138a953e993e62ddf72345c236fdc Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 9 Jan 2017 18:19:18 +0100 Subject: [PATCH 538/709] Updated composer.lock file --- composer.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/composer.lock b/composer.lock index 0caf0c8130..7cc131d34a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "977583861ece20e991f3f68223551012", + "content-hash": "c1354d0797f44315708cc46642aca068", "packages": [ { "name": "bacon/bacon-qr-code", @@ -2886,20 +2886,20 @@ "packages-dev": [ { "name": "barryvdh/laravel-debugbar", - "version": "v2.3.0", + "version": "v2.3.1", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-debugbar.git", - "reference": "0c87981df959c7c1943abe227baf607c92f204f9" + "reference": "65b0465e38a9524c9d5eb2dfc0389aba23090625" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/0c87981df959c7c1943abe227baf607c92f204f9", - "reference": "0c87981df959c7c1943abe227baf607c92f204f9", + "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/65b0465e38a9524c9d5eb2dfc0389aba23090625", + "reference": "65b0465e38a9524c9d5eb2dfc0389aba23090625", "shasum": "" }, "require": { - "illuminate/support": "5.1.*|5.2.*|5.3.*", + "illuminate/support": "5.1.*|5.2.*|5.3.*|5.4.*", "maximebf/debugbar": "~1.13.0", "php": ">=5.5.9", "symfony/finder": "~2.7|~3.0" @@ -2936,7 +2936,7 @@ "profiler", "webprofiler" ], - "time": "2016-09-15T14:05:56+00:00" + "time": "2017-01-05T08:53:44+00:00" }, { "name": "barryvdh/laravel-ide-helper", @@ -3252,16 +3252,16 @@ }, { "name": "maximebf/debugbar", - "version": "v1.13.0", + "version": "1.13.1", "source": { "type": "git", "url": "https://github.com/maximebf/php-debugbar.git", - "reference": "5f49a5ed6cfde81d31d89378806670d77462526e" + "reference": "afee79a236348e39a44cb837106b7c5b4897ac2a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/5f49a5ed6cfde81d31d89378806670d77462526e", - "reference": "5f49a5ed6cfde81d31d89378806670d77462526e", + "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/afee79a236348e39a44cb837106b7c5b4897ac2a", + "reference": "afee79a236348e39a44cb837106b7c5b4897ac2a", "shasum": "" }, "require": { @@ -3309,7 +3309,7 @@ "debug", "debugbar" ], - "time": "2016-09-15T14:01:59+00:00" + "time": "2017-01-05T08:46:19+00:00" }, { "name": "mockery/mockery", From 5a0ae8530cfff8bc6e6288044a200fb88211601f Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 9 Jan 2017 18:19:47 +0100 Subject: [PATCH 539/709] Update version info [skip ci] --- config/firefly.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/firefly.php b/config/firefly.php index 45e813e6eb..73ae9563e8 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -23,7 +23,7 @@ return [ 'is_demo_site' => false, ], 'chart' => 'chartjs', - 'version' => '4.3.1', + 'version' => '4.3.2', 'csv_import_enabled' => true, 'maxUploadSize' => 5242880, 'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'], From e67709e339249b7fc68e72c5645e4aeb0a1127f7 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Tue, 10 Jan 2017 18:25:03 +0100 Subject: [PATCH 540/709] Make index significantly simpler. --- app/Http/Controllers/JavascriptController.php | 117 ++++++++++++++++++ app/Http/Middleware/Range.php | 85 +------------ resources/views/javascript/variables.twig | 29 +++++ resources/views/layout/default.twig | 37 +----- resources/views/preferences/index.twig | 2 +- routes/web.php | 12 +- 6 files changed, 160 insertions(+), 122 deletions(-) create mode 100644 app/Http/Controllers/JavascriptController.php create mode 100644 resources/views/javascript/variables.twig diff --git a/app/Http/Controllers/JavascriptController.php b/app/Http/Controllers/JavascriptController.php new file mode 100644 index 0000000000..17bda42062 --- /dev/null +++ b/app/Http/Controllers/JavascriptController.php @@ -0,0 +1,117 @@ +<?php +/** + * JavascriptController.php + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +declare(strict_types = 1); + +namespace FireflyIII\Http\Controllers; + +use Amount; +use FireflyIII\Exceptions\FireflyException; +use Navigation; +use Preferences; +use Session; + +/** + * Class JavascriptController + * + * @package FireflyIII\Http\Controllers + */ +class JavascriptController extends Controller +{ + + /** + * + */ + public function variables() + { + + $viewRange = Preferences::get('viewRange', '1M')->data; + $start = Session::get('start'); + $end = Session::get('end'); + $linkTitle = sprintf('%s - %s', $start->formatLocalized($this->monthAndDayFormat), $end->formatLocalized($this->monthAndDayFormat)); + $firstDate = session('first')->format('Y-m-d'); + $prevStart = clone $start; + $prevEnd = clone $start; + $nextStart = clone $end; + $nextEnd = clone $end; + if ($viewRange === 'custom') { + $days = $start->diffInDays($end); + $prevStart->subDays($days); + $nextEnd->addDays($days); + unset($days); + } + + if ($viewRange !== 'custom') { + $prevStart = Navigation::subtractPeriod($start, $viewRange);// subtract for previous period + $prevEnd = Navigation::endOfPeriod($prevStart, $viewRange); + $nextStart = Navigation::addPeriod($start, $viewRange, 0); // add for previous period + $nextEnd = Navigation::endOfPeriod($nextStart, $viewRange); + } + + $ranges = []; + $ranges['current'] = [$start->format('Y-m-d'), $end->format('Y-m-d')]; + $ranges['previous'] = [$prevStart->format('Y-m-d'), $prevEnd->format('Y-m-d')]; + $ranges['next'] = [$nextStart->format('Y-m-d'), $nextEnd->format('Y-m-d')]; + + switch ($viewRange) { + default: + throw new FireflyException('The date picker does not yet support "' . $viewRange . '".'); + case '1D': + case 'custom': + $format = (string)trans('config.month_and_day'); + break; + case '3M': + $format = (string)trans('config.quarter_in_year'); + break; + case '6M': + $format = (string)trans('config.half_year'); + break; + case '1Y': + $format = (string)trans('config.year'); + break; + case '1M': + $format = (string)trans('config.month'); + break; + case '1W': + $format = (string)trans('config.week_in_year'); + break; + } + + $current = $start->formatLocalized($format); + $next = $nextStart->formatLocalized($format); + $prev = $prevStart->formatLocalized($format); + $localeconv = localeconv(); + $accounting = Amount::getJsConfig($localeconv); + $localeconv = localeconv(); + $defaultCurrency = Amount::getDefaultCurrency(); + $localeconv['frac_digits'] = $defaultCurrency->decimal_places; + $pref = Preferences::get('language', config('firefly.default_language', 'en_US')); + $lang = $pref->data; + $data = [ + 'dpStart' => $start->format('Y-m-d'), + 'dpEnd' => $end->format('Y-m-d'), + 'dpCurrent' => $current, + 'dpPrevious' => $prev, + 'dpNext' => $next, + 'dpRanges' => $ranges, + 'linkTitle' => $linkTitle, + 'firstDate' => $firstDate, + 'currencyCode' => Amount::getCurrencyCode(), + 'currencySymbol' => Amount::getCurrencySymbol(), + 'accounting' => $accounting, + 'localeconv' => $localeconv, + 'language' => $lang, + ]; + + return response() + ->view('javascript.variables', $data, 200) + ->header('Content-Type', 'text/javascript'); + } + +} \ No newline at end of file diff --git a/app/Http/Middleware/Range.php b/app/Http/Middleware/Range.php index 4371d473d7..fdf5322a4d 100644 --- a/app/Http/Middleware/Range.php +++ b/app/Http/Middleware/Range.php @@ -72,9 +72,6 @@ class Range // set view variables. $this->configureView(); - // get variables for date range: - $this->datePicker(); - // set more view variables: $this->configureList(); } @@ -96,7 +93,6 @@ class Range { $pref = Preferences::get('language', config('firefly.default_language', 'en_US')); $lang = $pref->data; - App::setLocale($lang); Carbon::setLocale(substr($lang, 0, 2)); $locale = explode(',', trans('config.locale')); @@ -105,94 +101,15 @@ class Range setlocale(LC_TIME, $locale); setlocale(LC_MONETARY, $locale); + // save some formats: - $monthFormat = (string)trans('config.month'); $monthAndDayFormat = (string)trans('config.month_and_day'); $dateTimeFormat = (string)trans('config.date_time'); $defaultCurrency = Amount::getDefaultCurrency(); - $localeconv = localeconv(); - $accounting = Amount::getJsConfig($localeconv); - // decimal places is overruled by TransactionCurrency - $localeconv['frac_digits'] = $defaultCurrency->decimal_places; - - View::share('monthFormat', $monthFormat); View::share('monthAndDayFormat', $monthAndDayFormat); View::share('dateTimeFormat', $dateTimeFormat); - View::share('language', $lang); - View::share('localeconv', $localeconv); View::share('defaultCurrency', $defaultCurrency); - View::share('accountingConfig', $accounting); - } - - /** - * @throws FireflyException - */ - private function datePicker() - { - $viewRange = Preferences::get('viewRange', '1M')->data; - /** @var Carbon $start */ - $start = Session::get('start'); - /** @var Carbon $end */ - $end = Session::get('end'); - - $prevStart = clone $start; - $prevEnd = clone $start; - $nextStart = clone $end; - $nextEnd = clone $end; - if ($viewRange === 'custom') { - $days = $start->diffInDays($end); - $prevStart->subDays($days); - $nextEnd->addDays($days); - unset($days); - } - - if ($viewRange !== 'custom') { - $prevStart = Navigation::subtractPeriod($start, $viewRange);// subtract for previous period - $prevEnd = Navigation::endOfPeriod($prevStart, $viewRange); - $nextStart = Navigation::addPeriod($start, $viewRange, 0); // add for previous period - $nextEnd = Navigation::endOfPeriod($nextStart, $viewRange); - } - - $ranges = []; - $ranges['current'] = [$start->format('Y-m-d'), $end->format('Y-m-d')]; - $ranges['previous'] = [$prevStart->format('Y-m-d'), $prevEnd->format('Y-m-d')]; - $ranges['next'] = [$nextStart->format('Y-m-d'), $nextEnd->format('Y-m-d')]; - - switch ($viewRange) { - default: - throw new FireflyException('The date picker does not yet support "' . $viewRange . '".'); - case '1D': - case 'custom': - $format = (string)trans('config.month_and_day'); - break; - case '3M': - $format = (string)trans('config.quarter_in_year'); - break; - case '6M': - $format = (string)trans('config.half_year'); - break; - case '1Y': - $format = (string)trans('config.year'); - break; - case '1M': - $format = (string)trans('config.month'); - break; - case '1W': - $format = (string)trans('config.week_in_year'); - break; - } - - - $current = $start->formatLocalized($format); - $next = $nextStart->formatLocalized($format); - $prev = $prevStart->formatLocalized($format); - View::share('dpStart', $start->format('Y-m-d')); - View::share('dpEnd', $end->format('Y-m-d')); - View::share('dpCurrent', $current); - View::share('dpPrevious', $prev); - View::share('dpNext', $next); - View::share('dpRanges', $ranges); } /** diff --git a/resources/views/javascript/variables.twig b/resources/views/javascript/variables.twig new file mode 100644 index 0000000000..ae6873096d --- /dev/null +++ b/resources/views/javascript/variables.twig @@ -0,0 +1,29 @@ +// date range picker configuration: +var dateRangeConfig = { + startDate: moment("{{ dpStart }}"), + endDate: moment("{{ dpEnd }}"), + linkTitle: "{{ linkTitle }}", + URL: "{{ route('daterange') }}", + firstDate: moment("{{ firstDate }}"), + currentPeriod: "{{ dpCurrent }}", + previousPeriod: "{{ dpPrevious }}", + nextPeriod: "{{ dpNext }}", + everything: '{{ 'everything'|_|escape }}', + customRangeLabel: '{{ 'customRange'|_|escape }}', + applyLabel: '{{ 'apply'|_|escape }}', + cancelLabel: '{{ 'cancel'|_|escape }}', + fromLabel: '{{ 'from'|_|escape }}', + toLabel: '{{ 'to'|_|escape }}', + ranges: {{ dpRanges|json_encode|raw }} +}; + +var language = "{{ language|escape }}"; +var currencyCode = '{{ currencyCode|escape('js') }}'; +var currencySymbol = '{{ currencySymbol|escape('js') }}'; +var mon_decimal_point = "{{ localeconv.mon_decimal_point|escape('js') }}"; +var mon_thousands_sep = "{{ localeconv.mon_thousands_sep|escape('js') }}"; +var frac_digits = {{ localeconv.frac_digits }}; +var noDataForChart = '{{ trans('firefly.no_data_for_chart')|escape }}'; +var showFullList = '{{ trans('firefly.show_full_list') }}'; +var showOnlyTop = '{{ trans('firefly.show_only_top',{number:listLength}) }}'; +var accountingConfig = {{ accounting|json_encode|raw }}; \ No newline at end of file diff --git a/resources/views/layout/default.twig b/resources/views/layout/default.twig index 85b8f95e3b..16a8c2f4e9 100644 --- a/resources/views/layout/default.twig +++ b/resources/views/layout/default.twig @@ -177,42 +177,7 @@ <script src="lib/adminlte/js/app.min.js" type="text/javascript"></script> <script type="text/javascript" src="js/lib/accounting.min.js"></script> <script src="js/lib/bootstrap-tour.min.js" type="text/javascript"></script> - -<script type="text/javascript"> - - // date range picker configuration: - var dateRangeConfig = { - startDate: moment("{{ dpStart }}"), - endDate: moment("{{ dpEnd }}"), - linkTitle: "{{ Session.get('start').formatLocalized(monthAndDayFormat) }} - {{ Session.get('end').formatLocalized(monthAndDayFormat) }}", - URL: "{{ route('daterange') }}", - firstDate: moment("{{ Session.get('first').format('Y-m-d') }}"), - currentPeriod: "{{ dpCurrent }}", - previousPeriod: "{{ dpPrevious }}", - nextPeriod: "{{ dpNext }}", - everything: '{{ 'everything'|_|escape }}', - customRangeLabel: '{{ 'customRange'|_|escape }}', - applyLabel: '{{ 'apply'|_|escape }}', - cancelLabel: '{{ 'cancel'|_|escape }}', - fromLabel: '{{ 'from'|_|escape }}', - toLabel: '{{ 'to'|_|escape }}', - ranges: {{ dpRanges|json_encode|raw }} - }; - - var language = "{{ language|escape }}"; - var currencyCode = '{{ getCurrencyCode()|escape('js') }}'; - var currencySymbol = '{{ getCurrencySymbol()|escape('js') }}'; - var mon_decimal_point = "{{ localeconv.mon_decimal_point|escape('js') }}"; - var mon_thousands_sep = "{{ localeconv.mon_thousands_sep|escape('js') }}"; - var frac_digits = {{ localeconv.frac_digits }}; - var noDataForChart = '{{ trans('firefly.no_data_for_chart')|escape }}'; - var showFullList = '{{ trans('firefly.show_full_list') }}'; - var showOnlyTop = '{{ trans('firefly.show_only_top',{number:listLength}) }}'; - var accountingConfig = {{ accountingConfig|json_encode|raw }}; - - -</script> - +<script src="javascript/variables.js" type="text/javascript"></script> <script type="text/javascript" src="js/ff/firefly.js"></script> <script type="text/javascript" src="js/ff/help.js"></script> {% block scripts %}{% endblock %} diff --git a/resources/views/preferences/index.twig b/resources/views/preferences/index.twig index 65080962ba..bf15783830 100644 --- a/resources/views/preferences/index.twig +++ b/resources/views/preferences/index.twig @@ -24,7 +24,7 @@ <div class="row"> <!-- general settings column A --> <div class="col-lg-6 col-md-6 col-sm-12 col-xs-12"> - <!-- language --> + {# language #} <div class="preferences-box"> <h3>{{ 'pref_languages'|_ }}</h3> <p class="text-info">{{ 'pref_languages_help'|_ }}</p> diff --git a/routes/web.php b/routes/web.php index 71793df647..04835588af 100755 --- a/routes/web.php +++ b/routes/web.php @@ -49,6 +49,7 @@ Route::group( /** * For the two factor routes, the user must be logged in, but NOT 2FA. Account confirmation does not matter here. + * * @deprecated */ Route::group( @@ -364,6 +365,15 @@ Route::group( } ); +/** + * Budget Controller + */ +Route::group( + ['middleware' => 'user-full-auth', 'prefix' => 'javascript', 'as' => 'javascript.'], function () { + Route::get('variables.js', ['uses' => 'JavascriptController@variables', 'as' => 'variables']); +} +); + /** * JSON Controller */ @@ -382,7 +392,7 @@ Route::group( Route::get('trigger', ['uses' => 'JsonController@trigger', 'as' => 'trigger']); Route::get('action', ['uses' => 'JsonController@action', 'as' => 'action']); - Route::post('end-tour', ['uses' => 'JsonController@endTour','as' => 'end-tour']); + Route::post('end-tour', ['uses' => 'JsonController@endTour', 'as' => 'end-tour']); } ); From 9b4fd57f516fa89e557f658938de0cb70d13e589 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Tue, 10 Jan 2017 18:35:00 +0100 Subject: [PATCH 541/709] Optimize new JS code. --- app/Http/Controllers/JavascriptController.php | 75 +++++++++++-------- resources/views/javascript/variables.twig | 12 +-- 2 files changed, 51 insertions(+), 36 deletions(-) diff --git a/app/Http/Controllers/JavascriptController.php b/app/Http/Controllers/JavascriptController.php index 17bda42062..67c3d268d7 100644 --- a/app/Http/Controllers/JavascriptController.php +++ b/app/Http/Controllers/JavascriptController.php @@ -30,12 +30,44 @@ class JavascriptController extends Controller */ public function variables() { + $picker = $this->getDateRangePicker(); + $start = Session::get('start'); + $end = Session::get('end'); + $linkTitle = sprintf('%s - %s', $start->formatLocalized($this->monthAndDayFormat), $end->formatLocalized($this->monthAndDayFormat)); + $firstDate = session('first')->format('Y-m-d'); + $localeconv = localeconv(); + $accounting = Amount::getJsConfig($localeconv); + $localeconv = localeconv(); + $defaultCurrency = Amount::getDefaultCurrency(); + $localeconv['frac_digits'] = $defaultCurrency->decimal_places; + $pref = Preferences::get('language', config('firefly.default_language', 'en_US')); + $lang = $pref->data; + $data = [ + 'picker' => $picker, + 'linkTitle' => $linkTitle, + 'firstDate' => $firstDate, + 'currencyCode' => Amount::getCurrencyCode(), + 'currencySymbol' => Amount::getCurrencySymbol(), + 'accounting' => $accounting, + 'localeconv' => $localeconv, + 'language' => $lang, + ]; + return response() + ->view('javascript.variables', $data, 200) + ->header('Content-Type', 'text/javascript'); + } + + /** + * @return array + * @throws FireflyException + */ + private function getDateRangePicker(): array + { $viewRange = Preferences::get('viewRange', '1M')->data; $start = Session::get('start'); $end = Session::get('end'); - $linkTitle = sprintf('%s - %s', $start->formatLocalized($this->monthAndDayFormat), $end->formatLocalized($this->monthAndDayFormat)); - $firstDate = session('first')->format('Y-m-d'); + $prevStart = clone $start; $prevEnd = clone $start; $nextStart = clone $end; @@ -83,35 +115,18 @@ class JavascriptController extends Controller break; } - $current = $start->formatLocalized($format); - $next = $nextStart->formatLocalized($format); - $prev = $prevStart->formatLocalized($format); - $localeconv = localeconv(); - $accounting = Amount::getJsConfig($localeconv); - $localeconv = localeconv(); - $defaultCurrency = Amount::getDefaultCurrency(); - $localeconv['frac_digits'] = $defaultCurrency->decimal_places; - $pref = Preferences::get('language', config('firefly.default_language', 'en_US')); - $lang = $pref->data; - $data = [ - 'dpStart' => $start->format('Y-m-d'), - 'dpEnd' => $end->format('Y-m-d'), - 'dpCurrent' => $current, - 'dpPrevious' => $prev, - 'dpNext' => $next, - 'dpRanges' => $ranges, - 'linkTitle' => $linkTitle, - 'firstDate' => $firstDate, - 'currencyCode' => Amount::getCurrencyCode(), - 'currencySymbol' => Amount::getCurrencySymbol(), - 'accounting' => $accounting, - 'localeconv' => $localeconv, - 'language' => $lang, - ]; + $current = $start->formatLocalized($format); + $next = $nextStart->formatLocalized($format); + $prev = $prevStart->formatLocalized($format); - return response() - ->view('javascript.variables', $data, 200) - ->header('Content-Type', 'text/javascript'); + return [ + 'start' => $start->format('Y-m-d'), + 'end' => $end->format('Y-m-d'), + 'current' => $current, + 'previous' => $prev, + 'next' => $next, + 'ranges' => $ranges, + ]; } } \ No newline at end of file diff --git a/resources/views/javascript/variables.twig b/resources/views/javascript/variables.twig index ae6873096d..8d188af209 100644 --- a/resources/views/javascript/variables.twig +++ b/resources/views/javascript/variables.twig @@ -1,20 +1,20 @@ // date range picker configuration: var dateRangeConfig = { - startDate: moment("{{ dpStart }}"), - endDate: moment("{{ dpEnd }}"), + startDate: moment("{{ picker.start }}"), + endDate: moment("{{ picker.end }}"), linkTitle: "{{ linkTitle }}", URL: "{{ route('daterange') }}", firstDate: moment("{{ firstDate }}"), - currentPeriod: "{{ dpCurrent }}", - previousPeriod: "{{ dpPrevious }}", - nextPeriod: "{{ dpNext }}", + currentPeriod: "{{ picker.current }}", + previousPeriod: "{{ picker.previous }}", + nextPeriod: "{{ picker.next }}", everything: '{{ 'everything'|_|escape }}', customRangeLabel: '{{ 'customRange'|_|escape }}', applyLabel: '{{ 'apply'|_|escape }}', cancelLabel: '{{ 'cancel'|_|escape }}', fromLabel: '{{ 'from'|_|escape }}', toLabel: '{{ 'to'|_|escape }}', - ranges: {{ dpRanges|json_encode|raw }} + ranges: {{ picker.ranges|json_encode|raw }} }; var language = "{{ language|escape }}"; From a0bb1e3625a64c274f512aa2caebf0dd4bcfeb8a Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Tue, 10 Jan 2017 19:55:52 +0100 Subject: [PATCH 542/709] CSS fix for #533 --- public/css/firefly.css | 13 +++++++++++-- resources/views/list/journals-tasker.twig | 8 +------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/public/css/firefly.css b/public/css/firefly.css index d7958dabc0..cc051e6442 100644 --- a/public/css/firefly.css +++ b/public/css/firefly.css @@ -31,7 +31,7 @@ body.waiting * { } .preferences-box { - border:1px #ddd solid; + border: 1px #ddd solid; border-radius: 4px 4px 0 0; padding: 15px; margin: 15px; @@ -101,4 +101,13 @@ body.waiting * { a[href]:after { content: none !important; } -} \ No newline at end of file +} + +.edit_tr_buttons { + white-space: nowrap; +} + +.edit_tr_buttons .btn { + float: none; + display: inline-block; +} diff --git a/resources/views/list/journals-tasker.twig b/resources/views/list/journals-tasker.twig index a52df725be..184ab9431f 100644 --- a/resources/views/list/journals-tasker.twig +++ b/resources/views/list/journals-tasker.twig @@ -33,13 +33,7 @@ <div class="select_single" style="display:none;"> <input name="select_all_single[]" class="select_all_single" value="{{ transaction.journal_id }}" type="checkbox"/> </div> - <div class="btn-group btn-group-xs edit_buttons"> - {% if sorting %} - <a href="#" class="handle btn btn-default btn-xs"><i class="fa fa-fw fa-arrows-v"></i></a> - {% endif %} - <a href="{{ route('transactions.edit',transaction.journal_id) }}" class="btn btn-xs btn-default"><i class="fa fa-fw fa-pencil"></i></a> - <a href="{{ route('transactions.delete',transaction.journal_id) }}" class="btn btn-xs btn-danger"><i class="fa fa-fw fa-trash-o"></i></a> - </div> + <div class="btn-group btn-group-xs edit_buttons edit_tr_buttons">{% if sorting %}<a href="#" class="handle btn btn-default btn-xs"><i class="fa fa-fw fa-arrows-v"></i></a>{% endif %}<a href="{{ route('transactions.edit',transaction.journal_id) }}" class="btn btn-xs btn-default"><i class="fa fa-fw fa-pencil"></i></a><a href="{{ route('transactions.delete',transaction.journal_id) }}" class="btn btn-xs btn-danger"><i class="fa fa-fw fa-trash-o"></i></a></div> </td> <td class="hidden-xs"> From 2564a41d05f633c8afc7bbfb6ca7e29c9666e46a Mon Sep 17 00:00:00 2001 From: Enno Lohmeier <enno.lohmeier@nerdworks.de> Date: Thu, 12 Jan 2017 00:30:33 +0100 Subject: [PATCH 543/709] add firefly locales to Dockerfile fixes #521 for docker environment --- Dockerfile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 38d92f5bef..2311881871 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,12 +11,16 @@ RUN apt-get update -y && \ libtidy-dev \ libxml2-dev \ libsqlite3-dev \ - libbz2-dev && \ + libbz2-dev \ + locales && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* RUN docker-php-ext-install -j$(nproc) curl gd intl json mcrypt readline tidy zip bcmath xml mbstring pdo_sqlite pdo_mysql bz2 +# Generate locales supported by firefly +RUN echo "en_US.UTF-8 UTF-8\nde_DE.UTF-8 UTF-8\nnl_NL.UTF-8 UTF-8\npt_BR.UTF-8 UTF-8" > /etc/locale.gen && locale-gen + # Enable apache mod rewrite.. RUN a2enmod rewrite From 423bb4bbcd826d565cd0fbd7d9ee9c74f9299580 Mon Sep 17 00:00:00 2001 From: James Cole <JC5@users.noreply.github.com> Date: Thu, 12 Jan 2017 09:37:12 +0100 Subject: [PATCH 544/709] Better layout --- .github/CONTRIBUTING | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING b/.github/CONTRIBUTING index 2a1338d94d..9f52883c5a 100644 --- a/.github/CONTRIBUTING +++ b/.github/CONTRIBUTING @@ -1 +1,8 @@ -If you are requesting a new feature, please check out the list of [often requested features](https://firefly-iii.github.io/requested-features/) first. Thanks! \ No newline at end of file + +==== + +If you are requesting a new feature, please check out the list of often requested features. + +https://firefly-iii.github.io/requested-features/ + +==== From 75e95d6452969846ab6ee4c4f722fb9a251d019f Mon Sep 17 00:00:00 2001 From: James Cole <JC5@users.noreply.github.com> Date: Thu, 12 Jan 2017 09:39:53 +0100 Subject: [PATCH 545/709] With markdown --- .github/CONTRIBUTING.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .github/CONTRIBUTING.md diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000000..2d9bcc0192 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,7 @@ +## Hi there! + +Thank you for taking the time to report an issue or requesting a new feature. + +Please take note that there are NO rules or regulations when you submit an issue. + +If you are requesting a new feature, please check out the list of [often requested features](https://firefly-iii.github.io/requested-features/). From 83edccacc6c01886204c8cf41dcb58ef1eaf259f Mon Sep 17 00:00:00 2001 From: James Cole <JC5@users.noreply.github.com> Date: Thu, 12 Jan 2017 09:40:12 +0100 Subject: [PATCH 546/709] Delete CONTRIBUTING --- .github/CONTRIBUTING | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 .github/CONTRIBUTING diff --git a/.github/CONTRIBUTING b/.github/CONTRIBUTING deleted file mode 100644 index 9f52883c5a..0000000000 --- a/.github/CONTRIBUTING +++ /dev/null @@ -1,8 +0,0 @@ - -==== - -If you are requesting a new feature, please check out the list of often requested features. - -https://firefly-iii.github.io/requested-features/ - -==== From f0028b33e92a98dee78191c394804c5a102c3051 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Fri, 13 Jan 2017 16:12:09 +0100 Subject: [PATCH 547/709] Double check that deleted transaction journals are not included. --- app/Support/Steam.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 65e4b148a6..0acae19e34 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -124,6 +124,7 @@ class Steam ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) ->groupBy('transaction_journals.date') + ->whereNull('transaction_journals.deleted_at') ->get(['transaction_journals.date', DB::raw('SUM(transactions.amount) AS modified')]); $currentBalance = $startBalance; foreach ($set as $entry) { From 2b82fca2cfa9dbf2650c6c8adcfe5443adcb2b22 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Fri, 13 Jan 2017 20:48:51 +0100 Subject: [PATCH 548/709] Small various bugs. --- public/js/ff/accounts/show.js | 2 +- resources/views/currencies/create.twig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/js/ff/accounts/show.js b/public/js/ff/accounts/show.js index a1db23e92a..b3b1093c7a 100644 --- a/public/js/ff/accounts/show.js +++ b/public/js/ff/accounts/show.js @@ -54,7 +54,7 @@ $(function () { ui.placeholder.html('<td colspan="' + cellCount + '"> </td>'); } } - ).disableSelection(); + ); } }); diff --git a/resources/views/currencies/create.twig b/resources/views/currencies/create.twig index c9949b05f2..18854f366c 100644 --- a/resources/views/currencies/create.twig +++ b/resources/views/currencies/create.twig @@ -17,7 +17,7 @@ {{ ExpandedForm.text('name',null,{'maxlength' : 48}) }} {{ ExpandedForm.text('symbol',null,{'maxlength': 8}) }} {{ ExpandedForm.text('code',null,{'maxlength' : 3}) }} - {{ ExpandedForm.integer('decimal_places',3,{'maxlength' : 2,'min': 0,'max': 12}) }} + {{ ExpandedForm.integer('decimal_places',2,{'maxlength' : 2,'min': 0,'max': 12}) }} </div> </div> From c0e578dd471fdae3bf35ed936e9ddfba016643bb Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Fri, 13 Jan 2017 21:12:59 +0100 Subject: [PATCH 549/709] Fix bug found by persistent user who kept mailing me about broken charts. Which turned out to be broken indeed! --- app/Support/Steam.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 0acae19e34..0676290b81 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -124,7 +124,8 @@ class Steam ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) ->groupBy('transaction_journals.date') - ->whereNull('transaction_journals.deleted_at') + ->orderBy('transaction_journals.date') + ->whereNull('transaction_journals.deleted_at') ->get(['transaction_journals.date', DB::raw('SUM(transactions.amount) AS modified')]); $currentBalance = $startBalance; foreach ($set as $entry) { From 4f64f1d754f262eccb60c92f5261df175671e430 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Fri, 13 Jan 2017 21:14:46 +0100 Subject: [PATCH 550/709] Force order by. [skip ci] --- app/Support/Steam.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 0676290b81..0f92fc13ca 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -124,7 +124,7 @@ class Steam ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) ->groupBy('transaction_journals.date') - ->orderBy('transaction_journals.date') + ->orderBy('transaction_journals.date','ASC') ->whereNull('transaction_journals.deleted_at') ->get(['transaction_journals.date', DB::raw('SUM(transactions.amount) AS modified')]); $currentBalance = $startBalance; From 30bc4ccfa710bdacc208a3d8e5843f1ea2d520f2 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Fri, 13 Jan 2017 21:16:54 +0100 Subject: [PATCH 551/709] More date fixes [skip ci] --- app/Support/Steam.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 0f92fc13ca..1761cf0ec6 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -124,7 +124,7 @@ class Steam ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) ->groupBy('transaction_journals.date') - ->orderBy('transaction_journals.date','ASC') + ->orderBy('transaction_journals.date', 'ASC') ->whereNull('transaction_journals.deleted_at') ->get(['transaction_journals.date', DB::raw('SUM(transactions.amount) AS modified')]); $currentBalance = $startBalance; @@ -165,6 +165,8 @@ class Steam ->where('transaction_journals.date', '<=', $date->format('Y-m-d')) ->groupBy('transactions.account_id') ->whereIn('transactions.account_id', $ids) + ->orderBy('transaction_journals.date', 'ASC') + ->whereNull('transaction_journals.deleted_at') ->get(['transactions.account_id', DB::raw('sum(transactions.amount) AS aggregate')]); $result = []; From 5bbaaece3839d189994cd051702dc379922af592 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 14 Jan 2017 17:13:57 +0100 Subject: [PATCH 552/709] Encryption is optional (but on by default) and a command to switch from one to the other --- .env.example | 1 + app/Console/Commands/UseEncryption.php | 66 ++++++++++++++++++++++++++ app/Console/Kernel.php | 3 +- app/Models/Account.php | 8 ++-- app/Models/Bill.php | 14 +++--- app/Models/Budget.php | 5 +- app/Models/Category.php | 5 +- app/Models/PiggyBank.php | 5 +- app/Models/TransactionJournal.php | 5 +- config/firefly.php | 1 + 10 files changed, 94 insertions(+), 19 deletions(-) create mode 100644 app/Console/Commands/UseEncryption.php diff --git a/.env.example b/.env.example index 87540a4f1c..04528da7fc 100755 --- a/.env.example +++ b/.env.example @@ -43,6 +43,7 @@ CACHE_PREFIX=firefly GOOGLE_MAPS_API_KEY= ANALYTICS_ID= SITE_OWNER=mail@example.com +USE_ENCRYPTION=true PUSHER_KEY= PUSHER_SECRET= diff --git a/app/Console/Commands/UseEncryption.php b/app/Console/Commands/UseEncryption.php new file mode 100644 index 0000000000..d3c3e178c0 --- /dev/null +++ b/app/Console/Commands/UseEncryption.php @@ -0,0 +1,66 @@ +<?php + +namespace FireflyIII\Console\Commands; + +use Illuminate\Console\Command; +use Illuminate\Support\Str; + +class UseEncryption extends Command +{ + /** + * The console command description. + * + * @var string + */ + protected $description = 'This command will make sure that entries in the database will be encrypted (or not) according to the settings in .env'; + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'firefly:use-encryption'; + + /** + * Create a new command instance. + * + */ + public function __construct() + { + parent::__construct(); + } + + /** + * Execute the console command. + */ + public function handle() + { + // + $this->handleObjects('Account', 'name', 'encrypted'); + $this->handleObjects('Bill', 'name', 'name_encrypted'); + $this->handleObjects('Bill', 'match', 'match_encrypted'); + $this->handleObjects('Budget', 'name', 'encrypted'); + $this->handleObjects('Category', 'name', 'encrypted'); + $this->handleObjects('PiggyBank', 'name', 'encrypted'); + $this->handleObjects('TransactionJournal', 'description', 'encrypted'); + } + + /** + * @param string $class + * @param string $field + * @param string $indicator + */ + public function handleObjects(string $class, string $field, string $indicator) + { + $fqn = sprintf('FireflyIII\Models\%s', $class); + $encrypt = config('firefly.encryption') ? 0 : 1; + $set = $fqn::where($indicator, $encrypt)->get(); + + foreach ($set as $entry) { + $newName = $entry->$field; + $entry->$field = $newName; + $entry->save(); + } + + $this->line(sprintf('Updated %d %s.', $set->count(), strtolower(Str::plural($class)))); + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 61537ce4e8..8ea0f88014 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -16,10 +16,10 @@ namespace FireflyIII\Console; use FireflyIII\Console\Commands\CreateImport; use FireflyIII\Console\Commands\EncryptFile; use FireflyIII\Console\Commands\Import; -use FireflyIII\Console\Commands\MoveRepository; use FireflyIII\Console\Commands\ScanAttachments; use FireflyIII\Console\Commands\UpgradeDatabase; use FireflyIII\Console\Commands\UpgradeFireflyInstructions; +use FireflyIII\Console\Commands\UseEncryption; use FireflyIII\Console\Commands\VerifyDatabase; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; @@ -63,6 +63,7 @@ class Kernel extends ConsoleKernel EncryptFile::class, ScanAttachments::class, UpgradeDatabase::class, + UseEncryption::class, ]; /** diff --git a/app/Models/Account.php b/app/Models/Account.php index 4810c6006e..11b337ba10 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -85,7 +85,7 @@ class Account extends Model foreach ($search as $name => $value) { $query->where($name, $value); } - $set = $query->get(['accounts.*']); + $set = $query->get(['accounts.*']); // account must have a name. If not set, use IBAN. if (!isset($fields['name'])) { @@ -93,7 +93,6 @@ class Account extends Model } - /** @var Account $account */ foreach ($set as $account) { if ($account->name == $fields['name']) { @@ -316,8 +315,9 @@ class Account extends Model */ public function setNameAttribute($value) { - $this->attributes['name'] = $value; - $this->attributes['encrypted'] = false; + $encrypt = config('firefly.encryption'); + $this->attributes['name'] = $encrypt ? Crypt::encrypt($value) : $value; + $this->attributes['encrypted'] = $encrypt; } /** diff --git a/app/Models/Bill.php b/app/Models/Bill.php index bf67e0f730..54a7429746 100644 --- a/app/Models/Bill.php +++ b/app/Models/Bill.php @@ -35,7 +35,7 @@ class Bill extends Model * @var array */ protected $casts - = [ + = [ 'created_at' => 'date', 'updated_at' => 'date', 'deleted_at' => 'date', @@ -47,7 +47,7 @@ class Bill extends Model 'match_encrypted' => 'boolean', ]; /** @var array */ - protected $dates = ['created_at', 'updated_at', 'deleted_at']; + protected $dates = ['created_at', 'updated_at', 'deleted_at']; protected $fillable = ['name', 'match', 'amount_min', 'match_encrypted', 'name_encrypted', 'user_id', 'amount_max', 'date', 'repeat_freq', 'skip', 'automatch', 'active',]; @@ -120,8 +120,9 @@ class Bill extends Model */ public function setMatchAttribute($value) { - $this->attributes['match'] = Crypt::encrypt($value); - $this->attributes['match_encrypted'] = true; + $encrypt = config('firefly.encryption'); + $this->attributes['match'] = $encrypt ? Crypt::encrypt($value) : $value; + $this->attributes['match_encrypted'] = $encrypt; } /** @@ -129,8 +130,9 @@ class Bill extends Model */ public function setNameAttribute($value) { - $this->attributes['name'] = Crypt::encrypt($value); - $this->attributes['name_encrypted'] = true; + $encrypt = config('firefly.encryption'); + $this->attributes['name'] = $encrypt ? Crypt::encrypt($value) : $value; + $this->attributes['name_encrypted'] = $encrypt; } /** diff --git a/app/Models/Budget.php b/app/Models/Budget.php index d62e195352..504802e05a 100644 --- a/app/Models/Budget.php +++ b/app/Models/Budget.php @@ -121,8 +121,9 @@ class Budget extends Model */ public function setNameAttribute($value) { - $this->attributes['name'] = $value; - $this->attributes['encrypted'] = false; + $encrypt = config('firefly.encryption'); + $this->attributes['name'] = $encrypt ? Crypt::encrypt($value) : $value; + $this->attributes['encrypted'] = $encrypt; } /** diff --git a/app/Models/Category.php b/app/Models/Category.php index e8e665bff2..50349e9b44 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -115,8 +115,9 @@ class Category extends Model */ public function setNameAttribute($value) { - $this->attributes['name'] = $value; - $this->attributes['encrypted'] = false; + $encrypt = config('firefly.encryption'); + $this->attributes['name'] = $encrypt ? Crypt::encrypt($value) : $value; + $this->attributes['encrypted'] = $encrypt; } /** diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index 34c742bd7f..d5b2b00187 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -159,8 +159,9 @@ class PiggyBank extends Model */ public function setNameAttribute($value) { - $this->attributes['name'] = $value; - $this->attributes['encrypted'] = false; + $encrypt = config('firefly.encryption'); + $this->attributes['name'] = $encrypt ? Crypt::encrypt($value) : $value; + $this->attributes['encrypted'] = $encrypt; } /** diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index 1cf5faf17f..1808955271 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -371,8 +371,9 @@ class TransactionJournal extends TransactionJournalSupport */ public function setDescriptionAttribute($value) { - $this->attributes['description'] = $value; - $this->attributes['encrypted'] = false; + $encrypt = config('firefly.encryption'); + $this->attributes['description'] = $encrypt ? Crypt::encrypt($value) : $value; + $this->attributes['encrypted'] = $encrypt; } /** diff --git a/config/firefly.php b/config/firefly.php index 73ae9563e8..67f9ccdd15 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -22,6 +22,7 @@ return [ 'single_user_mode' => true, 'is_demo_site' => false, ], + 'encryption' => (is_null(env('USE_ENCRYPTION')) || env('USE_ENCRYPTION') === true), 'chart' => 'chartjs', 'version' => '4.3.2', 'csv_import_enabled' => true, From 1ce49b814bc18c29ccd58c2e122029c895207a2b Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 14 Jan 2017 17:23:47 +0100 Subject: [PATCH 553/709] Fix rounding. [skip ci] --- app/Models/PiggyBankRepetition.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/PiggyBankRepetition.php b/app/Models/PiggyBankRepetition.php index 1d9dca1eb0..00c8221582 100644 --- a/app/Models/PiggyBankRepetition.php +++ b/app/Models/PiggyBankRepetition.php @@ -89,7 +89,7 @@ class PiggyBankRepetition extends Model */ public function setCurrentamountAttribute($value) { - $this->attributes['currentamount'] = strval(round($value, 2)); + $this->attributes['currentamount'] = strval(round($value, 12)); } } From cc0057cc56cb6408c3231dde8f0ce431620e5320 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 14 Jan 2017 17:24:30 +0100 Subject: [PATCH 554/709] Rename command [skip ci] --- app/Console/Commands/EncryptFile.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Console/Commands/EncryptFile.php b/app/Console/Commands/EncryptFile.php index 415649f9a4..1ff271a9e5 100644 --- a/app/Console/Commands/EncryptFile.php +++ b/app/Console/Commands/EncryptFile.php @@ -35,7 +35,7 @@ class EncryptFile extends Command * * @var string */ - protected $signature = 'firefly:encrypt {file} {key}'; + protected $signature = 'firefly:encrypt-file {file} {key}'; /** * Create a new command instance. From 0b5cab99cf2d2e49a77066ef020ea7caa9f74163 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 14 Jan 2017 18:52:52 +0100 Subject: [PATCH 555/709] Fix some scrutiniser issues. --- app/Http/Controllers/BudgetController.php | 3 --- app/Http/Controllers/TagController.php | 2 ++ app/Repositories/ExportJob/ExportJobRepositoryInterface.php | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index d9b5cc2a27..0d8ce65937 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -278,8 +278,6 @@ class BudgetController extends Controller throw new FireflyException('This budget limit is not part of this budget.'); } - /** @var AccountRepositoryInterface $accountRepository */ - $accountRepository = app(AccountRepositoryInterface::class); $page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page')); $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); $subTitle = trans( @@ -289,7 +287,6 @@ class BudgetController extends Controller 'end' => $budgetLimit->end_date->formatLocalized($this->monthAndDayFormat), ] ); - $accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::CASH]); // collector: /** @var JournalCollectorInterface $collector */ diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index f155623fdc..260adc97e3 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -230,6 +230,8 @@ class TagController extends Controller public function show(Request $request, JournalCollectorInterface $collector, Tag $tag, string $moment = '') { $range = Preferences::get('viewRange', '1M')->data; + $start = new Carbon; + $end = new Carbon; if (strlen($moment) > 0) { try { diff --git a/app/Repositories/ExportJob/ExportJobRepositoryInterface.php b/app/Repositories/ExportJob/ExportJobRepositoryInterface.php index dbb4a04418..6f3c7a30b1 100644 --- a/app/Repositories/ExportJob/ExportJobRepositoryInterface.php +++ b/app/Repositories/ExportJob/ExportJobRepositoryInterface.php @@ -50,7 +50,7 @@ interface ExportJobRepositoryInterface /** * @param string $key * - * @return ExportJob|null + * @return ExportJob */ public function findByKey(string $key): ExportJob; From 08ac27cccff3d413c230b0907ea48f50d3501abd Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 14 Jan 2017 19:43:33 +0100 Subject: [PATCH 556/709] Fix some scrutiniser issues. --- app/Exceptions/Handler.php | 4 ++-- app/Helpers/Attachments/AttachmentHelper.php | 8 +++---- app/Helpers/Collector/JournalCollector.php | 1 + app/Http/Controllers/AttachmentController.php | 7 +++++- app/Http/Controllers/ExportController.php | 11 ++++++---- app/Http/Controllers/ImportController.php | 22 +++++++++++-------- .../Transaction/SingleController.php | 2 ++ .../Transaction/SplitController.php | 1 + .../Account/AccountRepository.php | 1 - .../RuleGroup/RuleGroupRepository.php | 4 ++-- .../RuleGroupRepositoryInterface.php | 4 ++-- app/Rules/Triggers/AbstractTrigger.php | 2 ++ app/Support/Amount.php | 4 ++-- app/Support/ExpandedForm.php | 1 - 14 files changed, 44 insertions(+), 28 deletions(-) diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index c61dac5629..00b359d148 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -111,9 +111,9 @@ class Handler extends ExceptionHandler /** * Convert an authentication exception into an unauthenticated response. * - * @param \Illuminate\Http\Request $request + * @param $request * - * @return \Illuminate\Http\Response + * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse */ protected function unauthenticated($request) { diff --git a/app/Helpers/Attachments/AttachmentHelper.php b/app/Helpers/Attachments/AttachmentHelper.php index 501842a7ce..c7f7902109 100644 --- a/app/Helpers/Attachments/AttachmentHelper.php +++ b/app/Helpers/Attachments/AttachmentHelper.php @@ -32,9 +32,9 @@ class AttachmentHelper implements AttachmentHelperInterface /** @var MessageBag */ public $messages; /** @var array */ - protected $allowedMimes; + protected $allowedMimes = []; /** @var int */ - protected $maxUploadSize; + protected $maxUploadSize = 0; /** @var \Illuminate\Contracts\Filesystem\Filesystem */ protected $uploadDisk; @@ -44,8 +44,8 @@ class AttachmentHelper implements AttachmentHelperInterface */ public function __construct() { - $this->maxUploadSize = config('firefly.maxUploadSize'); - $this->allowedMimes = config('firefly.allowedMimes'); + $this->maxUploadSize = intval(config('firefly.maxUploadSize')); + $this->allowedMimes = (array) config('firefly.allowedMimes'); $this->errors = new MessageBag; $this->messages = new MessageBag; $this->uploadDisk = Storage::disk('upload'); diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index d5d881196a..c198da7764 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -167,6 +167,7 @@ class JournalCollector implements JournalCollectorInterface public function getJournals(): Collection { $this->run = true; + /** @var Collection $set */ $set = $this->query->get(array_values($this->fields)); Log::debug(sprintf('Count of set is %d', $set->count())); $set = $this->filterTransfers($set); diff --git a/app/Http/Controllers/AttachmentController.php b/app/Http/Controllers/AttachmentController.php index 8febccf3e4..2b154defea 100644 --- a/app/Http/Controllers/AttachmentController.php +++ b/app/Http/Controllers/AttachmentController.php @@ -18,6 +18,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Requests\AttachmentFormRequest; use FireflyIII\Models\Attachment; use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface; +use Illuminate\Http\Response as LaravelResponse; use Preferences; use Response; use Session; @@ -100,7 +101,9 @@ class AttachmentController extends Controller $content = $repository->getContent($attachment); $quoted = sprintf('"%s"', addcslashes(basename($attachment->filename), '"\\')); - return response($content, 200) + /** @var LaravelResponse $response */ + $response = response($content, 200); + $response ->header('Content-Description', 'File Transfer') ->header('Content-Type', 'application/octet-stream') ->header('Content-Disposition', 'attachment; filename=' . $quoted) @@ -110,6 +113,8 @@ class AttachmentController extends Controller ->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0') ->header('Pragma', 'public') ->header('Content-Length', strlen($content)); + + return $response; } throw new FireflyException('Could not find the indicated attachment. The file is no longer there.'); } diff --git a/app/Http/Controllers/ExportController.php b/app/Http/Controllers/ExportController.php index 7f9d29eb7f..1ace8118c1 100644 --- a/app/Http/Controllers/ExportController.php +++ b/app/Http/Controllers/ExportController.php @@ -24,6 +24,7 @@ use FireflyIII\Models\ExportJob; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface; use FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface as EJRI; +use Illuminate\Http\Response as LaravelResponse; use Preferences; use Response; use View; @@ -73,8 +74,9 @@ class ExportController extends Controller $job->change('export_downloaded'); - - return response($content, 200) + /** @var LaravelResponse $response */ + $response = response($content, 200); + $response ->header('Content-Description', 'File Transfer') ->header('Content-Type', 'application/octet-stream') ->header('Content-Disposition', 'attachment; filename=' . $quoted) @@ -85,6 +87,8 @@ class ExportController extends Controller ->header('Pragma', 'public') ->header('Content-Length', strlen($content)); + return $response; + } /** @@ -128,8 +132,7 @@ class ExportController extends Controller * @param AccountRepositoryInterface $repository * @param EJRI $jobs * - * @return string - * + * @return \Illuminate\Http\JsonResponse */ public function postIndex(ExportFormRequest $request, AccountRepositoryInterface $repository, EJRI $jobs) { diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index 12fc16c3af..ef812a8a1b 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -21,6 +21,7 @@ use FireflyIII\Models\ImportJob; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use Illuminate\Http\Request; +use Illuminate\Http\Response as LaravelResponse; use Log; use Response; use Session; @@ -120,15 +121,18 @@ class ImportController extends Controller $result = json_encode($config, JSON_PRETTY_PRINT); $name = sprintf('"%s"', addcslashes('import-configuration-' . date('Y-m-d') . '.json', '"\\')); - return response($result, 200) - ->header('Content-disposition', 'attachment; filename=' . $name) - ->header('Content-Type', 'application/json') - ->header('Content-Description', 'File Transfer') - ->header('Connection', 'Keep-Alive') - ->header('Expires', '0') - ->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0') - ->header('Pragma', 'public') - ->header('Content-Length', strlen($result)); + /** @var LaravelResponse $response */ + $response = response($result, 200); + $response->header('Content-disposition', 'attachment; filename=' . $name) + ->header('Content-Type', 'application/json') + ->header('Content-Description', 'File Transfer') + ->header('Connection', 'Keep-Alive') + ->header('Expires', '0') + ->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0') + ->header('Pragma', 'public') + ->header('Content-Length', strlen($result)); + + return $response; } diff --git a/app/Http/Controllers/Transaction/SingleController.php b/app/Http/Controllers/Transaction/SingleController.php index b1133686f3..bdb4226d84 100644 --- a/app/Http/Controllers/Transaction/SingleController.php +++ b/app/Http/Controllers/Transaction/SingleController.php @@ -265,6 +265,7 @@ class SingleController extends Controller return redirect(route('transactions.create', [$request->input('what')]))->withInput(); } + /** @var array $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; $this->attachments->saveAttachmentsForModel($journal, $files); @@ -315,6 +316,7 @@ class SingleController extends Controller $data = $request->getJournalData(); $journal = $repository->update($journal, $data); + /** @var array $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; $this->attachments->saveAttachmentsForModel($journal, $files); diff --git a/app/Http/Controllers/Transaction/SplitController.php b/app/Http/Controllers/Transaction/SplitController.php index 7d4cd17761..555935fe01 100644 --- a/app/Http/Controllers/Transaction/SplitController.php +++ b/app/Http/Controllers/Transaction/SplitController.php @@ -138,6 +138,7 @@ class SplitController extends Controller $data = $this->arrayFromInput($request); $journal = $repository->updateSplitJournal($journal, $data); + /** @var array $files */ $files = $request->hasFile('attachments') ? $request->file('attachments') : null; // save attachments: $this->attachments->saveAttachmentsForModel($journal, $files); diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index ab2cc761bd..3a8d8a3ecc 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -477,7 +477,6 @@ class AccountRepository implements AccountRepositoryInterface } /** - * @param float $amount * @param string $name * * @return Account diff --git a/app/Repositories/RuleGroup/RuleGroupRepository.php b/app/Repositories/RuleGroup/RuleGroupRepository.php index 247078221e..b294e19937 100644 --- a/app/Repositories/RuleGroup/RuleGroupRepository.php +++ b/app/Repositories/RuleGroup/RuleGroupRepository.php @@ -49,8 +49,8 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface } /** - * @param RuleGroup $ruleGroup - * @param RuleGroup $moveTo + * @param RuleGroup $ruleGroup + * @param RuleGroup|null $moveTo * * @return bool */ diff --git a/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php b/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php index a321b04e98..985a121b92 100644 --- a/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php +++ b/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php @@ -35,8 +35,8 @@ interface RuleGroupRepositoryInterface public function count(): int; /** - * @param RuleGroup $ruleGroup - * @param RuleGroup $moveTo + * @param RuleGroup $ruleGroup + * @param RuleGroup|null $moveTo * * @return bool */ diff --git a/app/Rules/Triggers/AbstractTrigger.php b/app/Rules/Triggers/AbstractTrigger.php index f3be138f8a..0fd9a3bfd8 100644 --- a/app/Rules/Triggers/AbstractTrigger.php +++ b/app/Rules/Triggers/AbstractTrigger.php @@ -74,6 +74,8 @@ class AbstractTrigger return $self; } + + /** * @param RuleTrigger $trigger * @param TransactionJournal $journal diff --git a/app/Support/Amount.php b/app/Support/Amount.php index de7fcce12f..9434352b80 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -90,11 +90,11 @@ class Amount $pos_c = $sign; } - // default: (amount before currency) + // default is amount before currency $format = $pos_a . $pos_d . '%v' . $space . $pos_b . '%s' . $pos_c . $pos_e; if ($csPrecedes) { - // (currency before amount) + // alternative is currency before amount $format = $pos_a . $pos_b . '%s' . $pos_c . $space . $pos_d . '%v' . $pos_e; } Log::debug(sprintf('Final format: "%s"', $format)); diff --git a/app/Support/ExpandedForm.php b/app/Support/ExpandedForm.php index 28b6972ed7..85b85fd5d2 100644 --- a/app/Support/ExpandedForm.php +++ b/app/Support/ExpandedForm.php @@ -285,7 +285,6 @@ class ExpandedForm /** * @param $name - * @param null $value * @param array $options * * @return string From c212d5c5eac81af36890b16a5f10345ac4d4a9ad Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 14 Jan 2017 21:00:43 +0100 Subject: [PATCH 557/709] Order by date does not matter in this context. --- app/Support/Steam.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 1761cf0ec6..0e06913bca 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -165,7 +165,6 @@ class Steam ->where('transaction_journals.date', '<=', $date->format('Y-m-d')) ->groupBy('transactions.account_id') ->whereIn('transactions.account_id', $ids) - ->orderBy('transaction_journals.date', 'ASC') ->whereNull('transaction_journals.deleted_at') ->get(['transactions.account_id', DB::raw('sum(transactions.amount) AS aggregate')]); From ac0d4a75b50005a3d573b0432463cf90c54ba072 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 15 Jan 2017 15:46:58 +0100 Subject: [PATCH 558/709] Removed forgotten twig comment [skip ci] --- resources/views/transactions/create.twig | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/views/transactions/create.twig b/resources/views/transactions/create.twig index ac232f8b0b..f978027d70 100644 --- a/resources/views/transactions/create.twig +++ b/resources/views/transactions/create.twig @@ -193,7 +193,6 @@ </div> </div> </div> - #} </div> </form> From b118635abdebda316c0190148309e76e15db3328 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 15 Jan 2017 19:00:06 +0100 Subject: [PATCH 559/709] Update views and JS for edit single transaction. --- .../Transaction/SingleController.php | 2 +- public/js/ff/transactions/single/edit.js | 96 +++++++++++++++++++ .../views/transactions/{ => single}/edit.twig | 3 +- 3 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 public/js/ff/transactions/single/edit.js rename resources/views/transactions/{ => single}/edit.twig (98%) diff --git a/app/Http/Controllers/Transaction/SingleController.php b/app/Http/Controllers/Transaction/SingleController.php index bdb4226d84..83f3b93dc9 100644 --- a/app/Http/Controllers/Transaction/SingleController.php +++ b/app/Http/Controllers/Transaction/SingleController.php @@ -241,7 +241,7 @@ class SingleController extends Controller Session::forget('transactions.edit.fromUpdate'); return view( - 'transactions.edit', + 'transactions.single.edit', compact('journal', 'optionalFields', 'assetAccounts', 'what', 'budgetList', 'subTitle') )->with('data', $preFilled); } diff --git a/public/js/ff/transactions/single/edit.js b/public/js/ff/transactions/single/edit.js new file mode 100644 index 0000000000..e122ca0e04 --- /dev/null +++ b/public/js/ff/transactions/single/edit.js @@ -0,0 +1,96 @@ +/* + * edit.js + * Copyright (C) 2016 thegrumpydictator@gmail.com + * + * This software may be modified and distributed under the terms of the + * Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +$(document).ready(function () { + "use strict"; + // no special JS for edit transaction. + + // the destination account name is always an expense account name. + if ($('input[name="destination_account_name"]').length > 0) { + $.getJSON('json/expense-accounts').done(function (data) { + $('input[name="destination_account_name"]').typeahead({source: data}); + }); + } + + // also for multi input + if ($('input[name="destination_account_name[]"]').length > 0) { + $.getJSON('json/expense-accounts').done(function (data) { + $('input[name="destination_account_name[]"]').typeahead({source: data}); + }); + } + + if ($('input[name="tags"]').length > 0) { + $.getJSON('json/tags').done(function (data) { + + var opt = { + typeahead: { + source: data, + afterSelect: function(val) { this.$element.val(""); } + } + }; + $('input[name="tags"]').tagsinput( + opt + ); + }); + } + + // the source account name is always a revenue account name. + if ($('input[name="source_account_name"]').length > 0) { + $.getJSON('json/revenue-accounts').done(function (data) { + $('input[name="source_account_name"]').typeahead({source: data}); + }); + } + // also for multi-input: + if ($('input[name="source_account_name[]"]').length > 0) { + $.getJSON('json/revenue-accounts').done(function (data) { + $('input[name="source_account_name[]"]').typeahead({source: data}); + }); + } + // and for split: + if ($('input[name="journal_source_account_name"]').length > 0) { + $.getJSON('json/revenue-accounts').done(function (data) { + $('input[name="journal_source_account_name"]').typeahead({source: data}); + }); + } + + + if ($('input[name="description"]').length > 0 && !(typeof what === "undefined")) { + $.getJSON('json/transaction-journals/' + what).done(function (data) { + $('input[name="description"]').typeahead({source: data}); + }); + } + // also for multi input: + if ($('input[name="description[]"]').length > 0 && !(typeof what === "undefined")) { + $.getJSON('json/transaction-journals/' + what).done(function (data) { + $('input[name="description[]"]').typeahead({source: data}); + }); + } + // and for the (rare) journal_description: + if ($('input[name="journal_description"]').length > 0 && !(typeof what === "undefined")) { + $.getJSON('json/transaction-journals/' + what).done(function (data) { + $('input[name="journal_description"]').typeahead({source: data}); + }); + } + + if ($('input[name="category"]').length > 0) { + $.getJSON('json/categories').done(function (data) { + $('input[name="category"]').typeahead({source: data}); + }); + } + + // also for multi input: + if ($('input[name^="category["]').length > 0) { + $.getJSON('json/categories').done(function (data) { + $('input[name^="category["]').typeahead({source: data}); + }); + } + + +}); diff --git a/resources/views/transactions/edit.twig b/resources/views/transactions/single/edit.twig similarity index 98% rename from resources/views/transactions/edit.twig rename to resources/views/transactions/single/edit.twig index b3e3a37326..ce096966b5 100644 --- a/resources/views/transactions/edit.twig +++ b/resources/views/transactions/single/edit.twig @@ -233,8 +233,7 @@ </script> <script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js"></script> <script type="text/javascript" src="js/lib/bootstrap-tagsinput.min.js"></script> - <script type="text/javascript" src="js/ff/transactions/create-edit.js"></script> - <script type="text/javascript" src="js/ff/transactions/edit.js"></script> + <script type="text/javascript" src="js/ff/transactions/single/edit.js"></script> {% endblock %} {% block styles %} <link href="css/bootstrap-tagsinput.css" type="text/css" rel="stylesheet" media="all"> From 582398e7f66ac2f2fc02fbe46875b7eb0092eddd Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 15 Jan 2017 19:07:31 +0100 Subject: [PATCH 560/709] Update views and JS for create (single) transaction. --- .../Transaction/SingleController.php | 6 ++- .../js/ff/transactions/{ => single}/create.js | 53 +++++++++++++++++-- .../transactions/{ => single}/create.twig | 3 +- 3 files changed, 54 insertions(+), 8 deletions(-) rename public/js/ff/transactions/{ => single}/create.js (72%) rename resources/views/transactions/{ => single}/create.twig (98%) diff --git a/app/Http/Controllers/Transaction/SingleController.php b/app/Http/Controllers/Transaction/SingleController.php index 83f3b93dc9..b887c26d07 100644 --- a/app/Http/Controllers/Transaction/SingleController.php +++ b/app/Http/Controllers/Transaction/SingleController.php @@ -113,7 +113,9 @@ class SingleController extends Controller asort($piggies); - return view('transactions.create', compact('assetAccounts', 'subTitleIcon', 'uploadSize', 'budgets', 'what', 'piggies', 'subTitle', 'optionalFields')); + return view( + 'transactions.single.create', compact('assetAccounts', 'subTitleIcon', 'uploadSize', 'budgets', 'what', 'piggies', 'subTitle', 'optionalFields') + ); } /** @@ -317,7 +319,7 @@ class SingleController extends Controller $data = $request->getJournalData(); $journal = $repository->update($journal, $data); /** @var array $files */ - $files = $request->hasFile('attachments') ? $request->file('attachments') : null; + $files = $request->hasFile('attachments') ? $request->file('attachments') : null; $this->attachments->saveAttachmentsForModel($journal, $files); // flash errors diff --git a/public/js/ff/transactions/create.js b/public/js/ff/transactions/single/create.js similarity index 72% rename from public/js/ff/transactions/create.js rename to public/js/ff/transactions/single/create.js index aaed65207a..a4b8f5e551 100644 --- a/public/js/ff/transactions/create.js +++ b/public/js/ff/transactions/single/create.js @@ -1,9 +1,7 @@ /* * create.js - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. * * See the LICENSE file for details. */ @@ -19,11 +17,57 @@ $(document).ready(function () { updateButtons(); updateForm(); updateLayout(); + updateDescription(); } + // get JSON things: + getJSONautocomplete(); }); +function updateDescription() { + $.getJSON('json/transaction-journals/' + what).done(function (data) { + $('input[name="description"]').typeahead('destroy'); + $('input[name="description"]').typeahead({source: data}); + }); +} + +function getJSONautocomplete() { + + // for withdrawals + $.getJSON('json/expense-accounts').done(function (data) { + $('input[name="destination_account_name"]').typeahead({source: data}); + }); + + // for tags: + if ($('input[name="tags"]').length > 0) { + $.getJSON('json/tags').done(function (data) { + + var opt = { + typeahead: { + source: data, + afterSelect: function () { + this.$element.val(""); + } + } + }; + $('input[name="tags"]').tagsinput( + opt + ); + }); + } + + // for deposits + $.getJSON('json/revenue-accounts').done(function (data) { + $('input[name="source_account_name"]').typeahead({source: data}); + }); + + $.getJSON('json/categories').done(function (data) { + $('input[name="category"]').typeahead({source: data}); + }); + +} + function updateLayout() { "use strict"; $('#subTitle').text(title[what]); @@ -131,6 +175,7 @@ function clickButton(e) { updateButtons(); updateForm(); updateLayout(); + updateDescription(); } return false; } \ No newline at end of file diff --git a/resources/views/transactions/create.twig b/resources/views/transactions/single/create.twig similarity index 98% rename from resources/views/transactions/create.twig rename to resources/views/transactions/single/create.twig index f978027d70..2b4f3aaaff 100644 --- a/resources/views/transactions/create.twig +++ b/resources/views/transactions/single/create.twig @@ -223,8 +223,7 @@ </script> <script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js"></script> <script type="text/javascript" src="js/lib/bootstrap-tagsinput.min.js"></script> - <script type="text/javascript" src="js/ff/transactions/create-edit.js"></script> - <script type="text/javascript" src="js/ff/transactions/create.js"></script> + <script type="text/javascript" src="js/ff/transactions/single/create.js"></script> {% endblock %} {% block styles %} From 9c09f9390805315580af134008575087a2478f34 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 15 Jan 2017 19:08:16 +0100 Subject: [PATCH 561/709] Update views and JS for delete (single) transaction. --- app/Http/Controllers/Transaction/SingleController.php | 2 +- resources/views/transactions/{ => single}/delete.twig | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename resources/views/transactions/{ => single}/delete.twig (100%) diff --git a/app/Http/Controllers/Transaction/SingleController.php b/app/Http/Controllers/Transaction/SingleController.php index b887c26d07..4ba6653d25 100644 --- a/app/Http/Controllers/Transaction/SingleController.php +++ b/app/Http/Controllers/Transaction/SingleController.php @@ -139,7 +139,7 @@ class SingleController extends Controller Session::flash('gaEventCategory', 'transactions'); Session::flash('gaEventAction', 'delete-' . $what); - return view('transactions.delete', compact('journal', 'subTitle', 'what')); + return view('transactions.single.delete', compact('journal', 'subTitle', 'what')); } diff --git a/resources/views/transactions/delete.twig b/resources/views/transactions/single/delete.twig similarity index 100% rename from resources/views/transactions/delete.twig rename to resources/views/transactions/single/delete.twig From e336a45f798da63b84d8696fdd92065360a0897e Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 15 Jan 2017 19:16:46 +0100 Subject: [PATCH 562/709] Edit JS file for split transaction --- .../Transaction/SplitController.php | 2 +- public/js/ff/transactions/create-edit.js | 6 +-- public/js/ff/transactions/edit.js | 45 ++++++++++++++++++- .../split/edit.js} | 43 +++++++++++++----- .../{edit-split.twig => split/edit.twig} | 3 +- 5 files changed, 82 insertions(+), 17 deletions(-) rename public/js/ff/{split/journal/from-store.js => transactions/split/edit.js} (83%) rename resources/views/transactions/{edit-split.twig => split/edit.twig} (98%) diff --git a/app/Http/Controllers/Transaction/SplitController.php b/app/Http/Controllers/Transaction/SplitController.php index 555935fe01..91b35ddfce 100644 --- a/app/Http/Controllers/Transaction/SplitController.php +++ b/app/Http/Controllers/Transaction/SplitController.php @@ -112,7 +112,7 @@ class SplitController extends Controller Session::forget('transactions.edit-split.fromUpdate'); return view( - 'transactions.edit-split', + 'transactions.split.edit', compact( 'subTitleIcon', 'currencies', 'optionalFields', 'preFilled', 'subTitle', 'amount', 'sourceAccounts', 'uploadSize', 'destinationAccounts', 'assetAccounts', diff --git a/public/js/ff/transactions/create-edit.js b/public/js/ff/transactions/create-edit.js index 4684f71a45..08b876979e 100644 --- a/public/js/ff/transactions/create-edit.js +++ b/public/js/ff/transactions/create-edit.js @@ -33,7 +33,9 @@ $(document).ready(function () { var opt = { typeahead: { source: data, - afterSelect: function(val) { this.$element.val(""); } + afterSelect: function () { + this.$element.val(""); + } } }; $('input[name="tags"]').tagsinput( @@ -94,6 +96,4 @@ $(document).ready(function () { } - - }); \ No newline at end of file diff --git a/public/js/ff/transactions/edit.js b/public/js/ff/transactions/edit.js index 63390bc3e6..ee5149b3b3 100644 --- a/public/js/ff/transactions/edit.js +++ b/public/js/ff/transactions/edit.js @@ -10,5 +10,48 @@ $(document).ready(function () { "use strict"; - // no special JS for edit transaction. + + // withdrawal specific fields + if (what == 'withdrawal') { + + $.getJSON('json/expense-accounts').done(function (data) { + $('input[name="destination_account_name"]').typeahead({source: data}); + }); + } + + // deposit specific fields: + if (what == 'deposit') { + $.getJSON('json/revenue-accounts').done(function (data) { + $('input[name="source_account_name"]').typeahead({source: data}); + }); + } + + // tags are always present: + if ($('input[name="tags"]').length > 0) { + $.getJSON('json/tags').done(function (data) { + + var opt = { + typeahead: { + source: data, + afterSelect: function () { + this.$element.val(""); + } + } + }; + $('input[name="tags"]').tagsinput( + opt + ); + }); + } + + // description + $.getJSON('json/transaction-journals/' + what).done(function (data) { + $('input[name="description"]').typeahead({source: data}); + }); + + // category (always there) + $.getJSON('json/categories').done(function (data) { + $('input[name="category"]').typeahead({source: data}); + }); + }); diff --git a/public/js/ff/split/journal/from-store.js b/public/js/ff/transactions/split/edit.js similarity index 83% rename from public/js/ff/split/journal/from-store.js rename to public/js/ff/transactions/split/edit.js index a7323a12a6..83ef63d57d 100644 --- a/public/js/ff/split/journal/from-store.js +++ b/public/js/ff/transactions/split/edit.js @@ -1,19 +1,20 @@ /* - * from-store.js - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. + * edit.js + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. * * See the LICENSE file for details. */ + /** global: originalSum, accounting */ var destAccounts = {}; var srcAccounts = {}; var categories = {}; -$(function () { +var descriptions = {}; + +$(document).ready(function () { "use strict"; $('.btn-do-split').click(cloneRow); $('.remove-current-split').click(removeRow); @@ -33,13 +34,32 @@ $(function () { $('input[name$="category]"]').typeahead({source: categories}); }); + $.getJSON('json/transaction-journals/' + what).done(function (data) { + descriptions = data; + $('input[name="journal_description"]').typeahead({source: descriptions}); + $('input[name$="description]"]').typeahead({source: descriptions}); + }); + + $.getJSON('json/tags').done(function (data) { + + var opt = { + typeahead: { + source: data, + afterSelect: function () { + this.$element.val(""); + } + } + }; + $('input[name="tags"]').tagsinput( + opt + ); + }); + + $('input[name$="][amount]"]').on('input', calculateSum); - - // add auto complete: - - }); + function removeRow(e) { "use strict"; var rows = $('table.split-table tbody tr'); @@ -75,6 +95,9 @@ function cloneRow() { if (categories.length > 0) { source.find('input[name$="category]"]').typeahead({source: categories}); } + if (descriptions.length > 0) { + source.find('input[name$="description]"]').typeahead({source: descriptions}); + } $('.split-table tbody').append(source); diff --git a/resources/views/transactions/edit-split.twig b/resources/views/transactions/split/edit.twig similarity index 98% rename from resources/views/transactions/edit-split.twig rename to resources/views/transactions/split/edit.twig index f47ec11af3..d811f4798e 100644 --- a/resources/views/transactions/edit-split.twig +++ b/resources/views/transactions/split/edit.twig @@ -307,6 +307,5 @@ </script> <script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js"></script> <script type="text/javascript" src="js/lib/bootstrap-tagsinput.min.js"></script> - <script type="text/javascript" src="js/ff/transactions/create-edit.js"></script> - <script type="text/javascript" src="js/ff/split/journal/from-store.js"></script> + <script type="text/javascript" src="js/ff/transactions/split/edit.js"></script> {% endblock %} From 161e9e1e11763956303e6f285ed56b68765b9ded Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 15 Jan 2017 19:28:54 +0100 Subject: [PATCH 563/709] Final JS cleanup. --- .../Transaction/MassController.php | 6 +- public/js/ff/transactions/create-edit.js | 99 ------------------- public/js/ff/transactions/edit.js | 57 ----------- public/js/ff/transactions/mass/edit.js | 32 ++++++ public/js/ff/transactions/split/edit.js | 2 +- .../{mass-edit.twig => mass/edit.twig} | 5 +- 6 files changed, 40 insertions(+), 161 deletions(-) delete mode 100644 public/js/ff/transactions/create-edit.js delete mode 100644 public/js/ff/transactions/edit.js create mode 100644 public/js/ff/transactions/mass/edit.js rename resources/views/transactions/{mass-edit.twig => mass/edit.twig} (97%) diff --git a/app/Http/Controllers/Transaction/MassController.php b/app/Http/Controllers/Transaction/MassController.php index 5676e902a8..d139f0f414 100644 --- a/app/Http/Controllers/Transaction/MassController.php +++ b/app/Http/Controllers/Transaction/MassController.php @@ -163,11 +163,11 @@ class MassController extends Controller $journal->transaction_count = $journal->transactions()->count(); if (!is_null($sources->first())) { $journal->source_account_id = $sources->first()->id; - $journal->source_account_name = $sources->first()->name; + $journal->source_account_name = $sources->first()->editname; } if (!is_null($destinations->first())) { $journal->destination_account_id = $destinations->first()->id; - $journal->destination_account_name = $destinations->first()->name; + $journal->destination_account_name = $destinations->first()->editname; } } ); @@ -178,7 +178,7 @@ class MassController extends Controller $journals = $filtered; - return view('transactions.mass-edit', compact('journals', 'subTitle', 'accountList')); + return view('transactions.mass.edit', compact('journals', 'subTitle', 'accountList')); } /** diff --git a/public/js/ff/transactions/create-edit.js b/public/js/ff/transactions/create-edit.js deleted file mode 100644 index 08b876979e..0000000000 --- a/public/js/ff/transactions/create-edit.js +++ /dev/null @@ -1,99 +0,0 @@ -/* - * create-edit.js - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -/** global: what */ - -$(document).ready(function () { - "use strict"; - - // the destination account name is always an expense account name. - if ($('input[name="destination_account_name"]').length > 0) { - $.getJSON('json/expense-accounts').done(function (data) { - $('input[name="destination_account_name"]').typeahead({source: data}); - }); - } - - // also for multi input - if ($('input[name="destination_account_name[]"]').length > 0) { - $.getJSON('json/expense-accounts').done(function (data) { - $('input[name="destination_account_name[]"]').typeahead({source: data}); - }); - } - - if ($('input[name="tags"]').length > 0) { - $.getJSON('json/tags').done(function (data) { - - var opt = { - typeahead: { - source: data, - afterSelect: function () { - this.$element.val(""); - } - } - }; - $('input[name="tags"]').tagsinput( - opt - ); - }); - } - - // the source account name is always a revenue account name. - if ($('input[name="source_account_name"]').length > 0) { - $.getJSON('json/revenue-accounts').done(function (data) { - $('input[name="source_account_name"]').typeahead({source: data}); - }); - } - // also for multi-input: - if ($('input[name="source_account_name[]"]').length > 0) { - $.getJSON('json/revenue-accounts').done(function (data) { - $('input[name="source_account_name[]"]').typeahead({source: data}); - }); - } - // and for split: - if ($('input[name="journal_source_account_name"]').length > 0) { - $.getJSON('json/revenue-accounts').done(function (data) { - $('input[name="journal_source_account_name"]').typeahead({source: data}); - }); - } - - - if ($('input[name="description"]').length > 0 && !(typeof what === "undefined")) { - $.getJSON('json/transaction-journals/' + what).done(function (data) { - $('input[name="description"]').typeahead({source: data}); - }); - } - // also for multi input: - if ($('input[name="description[]"]').length > 0 && !(typeof what === "undefined")) { - $.getJSON('json/transaction-journals/' + what).done(function (data) { - $('input[name="description[]"]').typeahead({source: data}); - }); - } - // and for the (rare) journal_description: - if ($('input[name="journal_description"]').length > 0 && !(typeof what === "undefined")) { - $.getJSON('json/transaction-journals/' + what).done(function (data) { - $('input[name="journal_description"]').typeahead({source: data}); - }); - } - - if ($('input[name="category"]').length > 0) { - $.getJSON('json/categories').done(function (data) { - $('input[name="category"]').typeahead({source: data}); - }); - } - - // also for multi input: - if ($('input[name^="category["]').length > 0) { - $.getJSON('json/categories').done(function (data) { - $('input[name^="category["]').typeahead({source: data}); - }); - } - - -}); \ No newline at end of file diff --git a/public/js/ff/transactions/edit.js b/public/js/ff/transactions/edit.js deleted file mode 100644 index ee5149b3b3..0000000000 --- a/public/js/ff/transactions/edit.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * edit.js - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -$(document).ready(function () { - "use strict"; - - // withdrawal specific fields - if (what == 'withdrawal') { - - $.getJSON('json/expense-accounts').done(function (data) { - $('input[name="destination_account_name"]').typeahead({source: data}); - }); - } - - // deposit specific fields: - if (what == 'deposit') { - $.getJSON('json/revenue-accounts').done(function (data) { - $('input[name="source_account_name"]').typeahead({source: data}); - }); - } - - // tags are always present: - if ($('input[name="tags"]').length > 0) { - $.getJSON('json/tags').done(function (data) { - - var opt = { - typeahead: { - source: data, - afterSelect: function () { - this.$element.val(""); - } - } - }; - $('input[name="tags"]').tagsinput( - opt - ); - }); - } - - // description - $.getJSON('json/transaction-journals/' + what).done(function (data) { - $('input[name="description"]').typeahead({source: data}); - }); - - // category (always there) - $.getJSON('json/categories').done(function (data) { - $('input[name="category"]').typeahead({source: data}); - }); - -}); diff --git a/public/js/ff/transactions/mass/edit.js b/public/js/ff/transactions/mass/edit.js new file mode 100644 index 0000000000..436cf88a67 --- /dev/null +++ b/public/js/ff/transactions/mass/edit.js @@ -0,0 +1,32 @@ +/* + * edit.js + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +/** global: what */ + +$(document).ready(function () { + "use strict"; + + // destination account names: + if ($('input[name^="destination_account_name["]').length > 0) { + console.log('multi dest account'); + $.getJSON('json/expense-accounts').done(function (data) { + $('input[name^="destination_account_name["]').typeahead({source: data}); + }); + } + + // source account name + if ($('input[name^="source_account_name["]').length > 0) { + $.getJSON('json/revenue-accounts').done(function (data) { + $('input[name^="source_account_name["]').typeahead({source: data}); + }); + } + + $.getJSON('json/categories').done(function (data) { + $('input[name^="category["]').typeahead({source: data}); + }); +}); \ No newline at end of file diff --git a/public/js/ff/transactions/split/edit.js b/public/js/ff/transactions/split/edit.js index 83ef63d57d..159be4f745 100644 --- a/public/js/ff/transactions/split/edit.js +++ b/public/js/ff/transactions/split/edit.js @@ -7,7 +7,7 @@ */ -/** global: originalSum, accounting */ +/** global: originalSum, accounting, what */ var destAccounts = {}; var srcAccounts = {}; diff --git a/resources/views/transactions/mass-edit.twig b/resources/views/transactions/mass/edit.twig similarity index 97% rename from resources/views/transactions/mass-edit.twig rename to resources/views/transactions/mass/edit.twig index 1399aea9d4..f4dfb503a9 100644 --- a/resources/views/transactions/mass-edit.twig +++ b/resources/views/transactions/mass/edit.twig @@ -101,6 +101,9 @@ </form> {% endblock %} {% block scripts %} + <script type="text/javascript"> + var what = ""; + </script> <script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js"></script> - <script type="text/javascript" src="js/ff/transactions/create-edit.js"></script> + <script type="text/javascript" src="js/ff/transactions/mass/edit.js"></script> {% endblock %} From e9f2121667c423cf7ba263be2bff4024bae3258a Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 15 Jan 2017 19:45:57 +0100 Subject: [PATCH 564/709] Fix menu in Firefox. --- resources/views/partials/menu-sidebar.twig | 24 ++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/resources/views/partials/menu-sidebar.twig b/resources/views/partials/menu-sidebar.twig index d9e9ffba2b..8de86ca538 100644 --- a/resources/views/partials/menu-sidebar.twig +++ b/resources/views/partials/menu-sidebar.twig @@ -9,7 +9,9 @@ <a href="#"> <i class="fa fa-credit-card fa-fw"></i> <span>{{ 'accounts'|_ }}</span> - <i class="fa fa-angle-left pull-right"></i> + <span class="pull-right-container"> + <i class="fa fa-angle-left pull-right"></i> + </span> </a> <!-- submenu for accounts --> <ul class="treeview-menu"> @@ -63,11 +65,11 @@ <!-- transactions --> <li class="{{ activeRoutePartial('transactions') }} treeview" id="transaction-menu"> <a href="#"> - <i class="fa fa-repeat fa-fw"></i> - <span> - {{ 'transactions'|_ }}<span class="fa arrow"></span> + <i class="fa fa-repeat"></i> + <span>{{ 'transactions'|_ }}</span> + <span class="pull-right-container"> + <i class="fa fa-angle-left pull-right"></i> </span> - <i class="fa fa-angle-left pull-right"></i> </a> <ul class="treeview-menu"> <li class="{{ activeRoutePartialWhat('transactions','withdrawal') }}"> @@ -90,7 +92,9 @@ <a href="#"> <i class="fa fa-euro fa-fw"></i> <span>{{ 'moneyManagement'|_ }}</span> - <i class="fa fa-angle-left pull-right"></i> + <span class="pull-right-container"> + <i class="fa fa-angle-left pull-right"></i> + </span> </a> <ul class="treeview-menu"> <li class="{{ activeRoutePartial('piggy-banks') }}"> @@ -116,7 +120,9 @@ <span> {{ 'import_and_export'|_ }} </span> - <i class="fa fa-angle-left pull-right"></i> + <span class="pull-right-container"> + <i class="fa fa-angle-left pull-right"></i> + </span> </a> <ul class="treeview-menu"> <li class="{{ activeRoutePartial('import') }}"> @@ -135,7 +141,9 @@ <a href="#"> <i class="fa fa-gears fa-fw"></i> <span>{{ 'options'|_ }}</span> - <i class="fa fa-angle-left pull-right"></i> + <span class="pull-right-container"> + <i class="fa fa-angle-left pull-right"></i> + </span> </a> <ul class="treeview-menu"> From c3fdd3b5f7d2d013fb40932598d2d296b587804b Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 15 Jan 2017 20:05:40 +0100 Subject: [PATCH 565/709] Allow date picker for browsers that do not support it natively. See issue #535 --- .../jquery-ui/images/ui-icons_444444_256x240.png | Bin 0 -> 6992 bytes .../jquery-ui/images/ui-icons_555555_256x240.png | Bin 0 -> 6988 bytes .../jquery-ui/images/ui-icons_777620_256x240.png | Bin 0 -> 4549 bytes .../jquery-ui/images/ui-icons_777777_256x240.png | Bin 0 -> 6999 bytes .../jquery-ui/images/ui-icons_cc0000_256x240.png | Bin 0 -> 4549 bytes .../jquery-ui/images/ui-icons_ffffff_256x240.png | Bin 0 -> 6299 bytes public/css/jquery-ui/jquery-ui.structure.min.css | 5 +++++ public/css/jquery-ui/jquery-ui.theme.min.css | 5 +++++ public/js/ff/transactions/single/create.js | 8 ++++++++ public/js/ff/transactions/single/edit.js | 14 ++++++++++++-- public/js/lib/jquery-ui.min.js | 9 +++++---- public/js/lib/modernizr-custom.js | 3 +++ resources/views/transactions/single/create.twig | 4 ++++ resources/views/transactions/single/edit.twig | 5 +++++ 14 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 public/css/jquery-ui/images/ui-icons_444444_256x240.png create mode 100644 public/css/jquery-ui/images/ui-icons_555555_256x240.png create mode 100644 public/css/jquery-ui/images/ui-icons_777620_256x240.png create mode 100644 public/css/jquery-ui/images/ui-icons_777777_256x240.png create mode 100644 public/css/jquery-ui/images/ui-icons_cc0000_256x240.png create mode 100644 public/css/jquery-ui/images/ui-icons_ffffff_256x240.png create mode 100644 public/css/jquery-ui/jquery-ui.structure.min.css create mode 100644 public/css/jquery-ui/jquery-ui.theme.min.css create mode 100644 public/js/lib/modernizr-custom.js diff --git a/public/css/jquery-ui/images/ui-icons_444444_256x240.png b/public/css/jquery-ui/images/ui-icons_444444_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..19f664d970194372c3228494e34ac01d611a4d45 GIT binary patch literal 6992 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 BEO!6^ literal 0 HcmV?d00001 diff --git a/public/css/jquery-ui/images/ui-icons_555555_256x240.png b/public/css/jquery-ui/images/ui-icons_555555_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..e965f6d97c6e39e711dbba68889a7d1f3d95eb45 GIT binary patch literal 6988 zcmZvBbzGF)w*NCjNW&l!Lxb=F5<?5p-5`zBfOI2+^e`YD5+bRTNQX2?42X1>beAX+ z(lK~>?>YDPJLlZ{$9~p+KA*Mrex9}0`tH5HJ6cCenV0}d00027s)~Xx0D$hgz#$&? z-BU69h0L7*TdON800z}@5vq4@_#P@IUUyREznfCOvlIY89aR-%^?m1my$^P=Rrc<c z_Ni6>)}nev#FJE^KgXEw7+V@d`y^D~bgP-i_Wna_cf}GegPJ!E=3$vK1!@mU6ht2U zuzbkL;UpS*N{L?pd})D%hu?mBszca0;{P^PZhSjZ-^Ji#klR({E0N8!glp&R<xB5_ zpwWXlpY}%w0SDQaK_l-np4e||9D*b=<0N14qI%(LIJs+wV5aABiPf@H)=$=OWWG}W z?C^i1kUIm0UEB&P8H;U;!(MA)C#T%A>}8On1Zu%Vlfmq>R|!%`>K&;<;^%`;tgP#y z3VSP2iXADxc_$$Gz=<5_^^6L0bk<4N@8bJZV6X^^7z~yl@c!`_fj8leDu_{)9%BxZ zJC2H)z~}1L_RruAHeI$Fu4yTBDcc~CD+CP@j6Eona$uCoU)1ClD$=cT&x~N=-}3jj z>my6Cz!y4a(PJEf);q0K7s2<lwrjGy;i)y6qk9}F!Gw6+dy}G?iMM400R*%?*^VPm zCXuD$QJe?Rv#w=^BGC&^&kss8BGmTggUb)MI%ldQzt;nMh$%=8JwBRz*25R%@VFHa zqO$;?BzypIBo!*6i%_O(?Hp@!gXHtgLY%-gXz?ZXY#jgd9t^Nex3^bP%h=&Yk}%L- z7FMu`1VjnboAFPHpfiC!nhQNh(;q*ZwS#`N*on$`O3PrkRkPyT)->+!Hq7!~gET%0 z?zknmD^VA{58Nl{y9Vj6*t$G+1bI^j{6Wl-!hZz2*-pl4J1n{CuouyXXaL~d9EO*W zG2!Z1|K+Gg+l=bUc=LJ}i0187QG4uA=ay(#I)tRvv&XoqJiC{uXVshl&6sQC>BDg& zr_=S1<sSQAzqmq~t^By79(843q?Q)!-u6AnF-$g<sV+75GiM)z7a*J#i6V<@KvMky ztZ>v!^oP3)Z=+5__#d=*=*Ryu#7fg)Ti;It&z2s%sMKbZ_HbZI;|jy|s#3oMV`<)U zrzxj1(AU<Znj`E$1U%K9-=aVMj+I9_pL(}=@5NJ`$aF?3Iip=ED)d5O@$?)e*88Pq z$@?Gq<Q;OeCP$p?_i?Q`u{fUK6Vdx8@T9~d41aF84H47nit_idEtT@Xe}wWnE-d8A z@Et?bOCG1`IAxmP%(KnDi_q-;T;gC#TC=q;5M55t61A|ai$CB1h)$chr_w%l%0Ry5 zhjAG<BCsN1238D(!&tcj`xctHomOLPv;CJwQ9q5qwEq?Z|D7)X@ZV-vUUcnLXXid? zYmSE_h$_~h-nXH4ys|k(jveUlm!$GJn1<&16sM+EY`XElUkGQJZ1kx0rjqXL{)8De z<9spW1f8zw9Mecq#h#_Fn;a;}q@`v#;{{zVJ(Ef6HQx9AWn40ALhJ%9ULHUB0y4jJ z$&G#Dy#R4}FKFBNUg(Vc`$Tn%M`POJFmv0ywT_Kz;)ey2RQsW)@3o0OrVQggf8Qz2 z5yANcf67Rf0UZ;>p|W8#jcsCPaQibx_xqrZ==txmeG_hT+NK|H`kn_X<s5$j-q4Qa z8~g>0v;mu4{Xujk-ghs?gpz)|O1n_bKTP%PxX@D0t-XuS7`#^4C`Th>#}x1zyGK9B zYfZyI=p(b?aQiRyZg2~b>GUbdi>dmlIF4xgY)g_wz=omP06r>Uw9%5ritF949<tuj zx0R8xcZ!MhyrkSS-}IG}Yy8MWh*8a~hixy(s%!b}+V~x3IvI%|YxD}fLd5@>jySn9 z)CFQK;NRCE7uZ~xS*eua<nogH6rVZVWRIcdGu1g(I=m*m<=vn<_sb2Edm5+30Zjve z5)z*ng6jp1sa8_1P>Cy~WKf=oN=h%i8(=9kcA?q)jpX=pU0zb>oUU7O?lva8Z1P(U zgUClISn&?<S@WJ<fI-g%R~BVHZi9Q)#5D}hv;Gu^ExYu$0b$fHn`0f8`@o!z@iGg& zd29I!DO_I0Zm{enN}Nz{21l_za7k|J=xF?&4VdZKWNF{0VW_=m!#9KYkX+d*sNAi7 z_;*b{!!0J~guL<l>tu=XLtHovoEHu)X;9ibq~Yx`Yze><1aEM-IA6JtO$1P>%Hgt= zSVxu8I$eS5g*}#ZWT~E)aA_cfThs%ye280;g`0IsHmtt3mW?7a;;v<CTy+R#E>)A& zn=GhJ&2@|S^S>x!|44P~TQvgP?#WPod-Xq9_b+iCp$$Cis!^FI+QjJ?PIVQS3)9j! zkD^?1^?N{D#~79;E9P_LR+v5x-P%pYQyrwPEFHWeYWa?PD4vMZ(W>TZ@k`6!JGN36 z`wJpZKj<Z&F6M({R3$s|cSWY2_c8u9?%yt33V>utn(YWkGDXdyONIu^=UJ!G{l#m0 zF3Bz8!G6lJQV&GWA@TCogngMqdq?wj{c5w8L3Aq!ftli2zEmE|TN^=Br4>b^ju}}N zKy@<aPZ^o+OirWg$+ulnvGYKh%~-M+HZyH`wvR4T5FJSY=iFHv|9h-wrhH~qZ^y=2 zA$WwxQD4c*&j$F?ij3U0Xi)<0pa$3Yvqu>kC*vveIH92ocwvhMSV?};NqbUXSQRrG zf#X81tT%#L2d{@ye*A{gJeE>l-IvO@@<7ABh$x)kZi~s#3mxvE2_uGO!|W$So?p0t z7#0ZrWlyQj!iKY$JEnhEzY3UR{6pJN;zg_ghS7^Il25+X^B=O_ZDigouQSc%zW<l8 zD!Pi}x%L!!LR)L6t4npaYik}w2)^@f!q+L*Oo<bOsm6uOevVjVih9zAzQo98qB>&= zuzi)y<nA#IuS4?4$n>z6VQo4$3!`-^6%OSWx}A?b=KbM0Huc!%ym_Sv<t8qS)PVZL z0cNoW%GyEcitPJ2PpPwGb<EBiiT{q%I;woj)mtNN<i2V<QAds41GlHlOoJMEU9E)x z>uzHO*fpeR{CRj^wL+M;oM8c-VS0)JTtK;Tqg3Z}aoFXBbDRfRdK8YVkyg6C&f=)S z>CeM*j&(=1XrR11XI+G-x-Tt+vBuV{aEUQEdpcs)K1#AT_MOO>J$4>JMiSydM=2Ay zbU8;l$JC}z%0eBzQo!9M>&CK*%1b$?cCaIM5b3=aqd$P}`{j0Jo$+h$obk8%hllbF z1Xi0YcTROu(VTJbC<Luu>SN2<`C33&AR#Q*taUGPx+j)gxE-@{JVXz@=Lasz5@QlS zW51~`%;Y9A#wgv5_FRDyG;>)VgXzY_uFa3R8W~i})Wmwof{MU=ZT|3nlYTg8yE?~c z!@ZHK_+GPImT>OygJr{XtWBO014&@^K^xCk#>4P0wNaR3I_!3WM55LGsu0DqokzYX zEIN1?)-yIg$r&%n+#gnilxJPkqk>&Tqs|RpTAw<FCSCO3?Iauy9-))uE_<576K5PI z?MRs$sT)(YoYI3;E+(y{MP}s!EsnU!uWDPv`M|L57YfC<N~Y!OQNkmnM9i*GcpP!m z^=(A{<zEj!t6?Mg&okWfi__|&?G$RVeV;fpwNjOji|lcfepqdEiqJXO_2sOz6p~h- zb0-Hp*-iDu70>>N@u{I>U}Xx&ba36HOrr>r!?Q*E_UQb}ZVN+#D%9LVlfdMh!Z~)o zL)*2gC^5>ggK4?%N4i|=;OofUFH!ibGhr)yPVBlEMLdGDbAeO5SNs8tYo$ZCR^&iJ z2wZdyEWy?B_aW>50ne=Y1$GVxl`JJHwX4%b(#&lc(G@n4FU2`q6YG9C88(65rNbaz zS3X0KS8OR?$=tgjRKG@i*LQ2Ay`*N`P*N{8vCGgzMpcFIeAWOzifOx4U!<>+d2f}} z#m&Y2O}lo$1fGqrrGE0l=a#)8NbKg}N^Fi&$x@Q(RrKB({s`rI%lyWIOx@vRfIm4q zVD}M20??;Ye<X~X7)Do3<`WJb3l)hG4jkVf^#ostpsgI1=6v}bU)x$haG0!vZ8RUn z$WTA=pBBw=t$>MZUsz?YcS&@2NmM%K;u~hzRl_ImCm>5-cr>?Tp0s8S((UA2c*iHa z5;4NVe{5*HJNO+Ha!Wol+Y4cnbi5UV=h+alPhCze@=j<|#T_0Gyq$KW%-8@g*>uG_ zkZ7gq8KswR7Ar13%59)q?iZI{U-5lQB`ng3Z<xLo`mMn9Uhrs3lR}pIV!yPLyrT22 zE33IhZHlL6wGoHx#5HBg)YFn_#1p=$79KZmU+^Ir3`&~7BT_5YVd5wp6t3FUKteAv zSD>Dz`D?8*c-!Zay*BXe<(CK&m05J3f@Nw*a2B6ea+PN?ABU*hB5wMKGW!V%^|S6I zXm7?PFyk$Cj^*IN*F9}5?U(AqI1FoVCJwZJt1u5SlZ=BH>_fyxl+DO@0Oqe=z*}%5 zDF`%&N5!>!VE6uhV4Ba7`Le;MXMgP(og+Qz&4r7i=!OvC#GHOtX&1ogp=|crtH5wQ zC>6P@jBteg$<K?7q{`Ph@okXo!C$g_pHjYYmF7qgI&9TBd6F=ZFe^evzG~-hRfJq+ z&)7x~(#04wBBgUl-h}gOQ38V3TQTzu3NBnZxA^z`60`ggv2JLJ9?n0OevPi^&QT6f z8=Vcd(A(bf#Zz%gwLdHrQlOYy0k1A};9H+&4@yI?)3zM^h?&mT13zRz!X3JYEs@e1 z`ks>4ZyHh9rH!cZDMig{l5JIMfto+D)d=tJpu7-FDS0WKqasVLKb9|P+69?5R?+1n zt=~T6-l;eYg@bZZZiS$)JkbW!kNFJX7BoFC*<wr1t2`o^z&;dvf0Ucm(p;|m^kvh6 zcdWdidXZB0w$d6~7Fe;f6bcdv&Not);8n-ZW_n@1bm@b=-op17V=aRvuHs2HrxDsP zLLCa(yZT`5u;HIY&Oh@{NE=|O^;DMKEht=Epqm(<yLy?fZ2c5?{gK$C{DuEC25U+& zHqq<o+-u&PIH5-*>SHM04ZHfgb&7*k`Wuub=hwJrJ0FhtjtV#Mdi@xgeO~;<e6Zom z+&&kih!mAaO3Np?5%Qb&$rt|hK-Cf}p}I#P7@vDgZ8*DI!OTqc%zoyYo3O6=3;#EY zSgxldgp0k+zHR1c24s2~XC(!L1{&R|{lGboP-2k9west3pY!QUFJJ4GpIpTK+x0U# zR`h$@a@e~{84toAtt38WR&p8{5$b>rYIug6Pg?7FlG_~`1K-kZiQmc!n>cIFRtYpS z#7IvUexJGIG<7-BTh^J#*#g5d=TFkqXI43uPSoP^8C1?>cM;#&y{an=T-}K`^x0*~ z!riUjdG=gZZORsc<m{R{#I~(fMGx%9ZTa%!7{-=#nAX#oQn`q|wP4k}9+n~5jYV4l zD&J^Gyy_FL?+0R|Y{he@WY(j5c|PKf?Jx?EA2dOq7Lqxlc)zkY8xf@ad<cbuAD1Dv z=_l++57`qLT;EiLK&}t;{0RuWR!V6C5W@$dk9Oyp#I^u$#)3A>VWj7%JhB2qv#jw8 z%gC>W?9T_YhZ#?bjZV)wjaJP)VVE2I!{Zn-Mi4xShElA248%NFJ!Ku43Ea~9zLC5E z^LeoQb336lpLtI5jF)`cB7Bb5NUU8*=K@{zy0N;FMDuCcp<_aNeN`?_sjH}87~vaF zDF@?r2v^{a`9TQ8xFt#bKIVcHFp#;kfnku_z1#aJM*E7ak~R?@5x-Q&)bAV(3buk4 z^`q`bC0q)L*Y<^_FN|X|(t@axK1okN{_@qzXWz@QPLY`lt~zwwUJq70>w*$W5WQp0 zbfl>6XaCb{+N9+d8a%NM-@tWSwRKzVxd;~2w}0j8g)*J%8eSY4?)aq|<$@P=W2wNW z`YW#vGu-VCN;A<DlKFb;?!|bpiap+d2Z3Ct5${^sg&Y9r0=RU?j`6l<s$Vj%SMC^? zti<GjVYn-|GQm!Q{d(8w+#kVavI>3Whv;Ln#{S~QAr1-FlzLo}-@;>YluP<Y(!J5| zrX}Y0?N*$_8g9lZyxw%V^i1|kilxCkq*rzfMCvLi$P0FGeoqczTHn0&RbPJiCv)MM zc*U$dXUpDTB!~Ek-WA{BHXP!<L3HS;$(L=L5^h}n?1oR;h>Rb?1tM#sIeWh1h4VKt zk!eqOn7I1SEZxL`Kl{md!!S~mT5znCR?P4O@ApHGB*#^`HF}EMbnUHdiN2zH=DrD6 zWWpW&P3PKP0#rY;Q!sM`OGPQq;NfGd>_=P1uMp*1HIcK(a=dRRzs^n<M5Dkk(l`ON zBja99TeIm&eAr_rNs3@z6Se85F0-h)rX&0MBNt|u6P+ff{JwS7;dA<U7re*)GySh4 zmWJ6%tc0owwf7!{@N{=-()9DI`mMb*{GIG)0U4#VPQ}ss&9lFr%#I@(x!(ZY=dgh? z{Wz`gjrX&-zx?uezm?f71xa<<pQd$HVvTO?ln-i`nlI#swpi+nYyT#;*r9zlRR9q6 zUM-$6Lr_{0w?5CarV-swFIG0B=|6X5S|?|41kqocdxmKK`Y3W(v1pH*))sfagR87u zE~oM%6d(twC~cKTF7!AGr{b5FXK}gMMa{PBXJKHPgC0lI=FOu-yp1tAYyu5hGC1cn z@-Zra`0ha}+-@ja>SJ7)|9th(5f-@HB6ahY`Tq5e(8^X3AwrK&Lh*`ic@Vig$o8u0 z7ye%ZmE%Bvg%#B4=8~bkB==TppU6Koxt(u!xc_X~-KPdlX!RkoT!3*DY$zm%DHRPN z;$#8otkgDg33Xxu*_DG(qg?9<V*xIYNhH{Gajm100-###Mzr!kvNuDmS*D4E3;~Ik zY^}nd?&!P!<a7KG+{-sU=~#HQ3Lw0<hQ0-4ZJ)`lbsjsLbRST=GQE$XOlFQK2Z?i= zgv-Qh_&%Gb5a;erjUZ9Sfh2by)P*9Ssj0Gs;{2fsCx5qHmxtWn^O>8jW7VMpmlDX5 zyT9Q~B_N(+wu$BxA>M7-GBa1X|NKp6Kl9yI=I8Xu6cOT2GG^89pVr8`Og`>}o>i7# zE6MOvB@oxQM~-*NdhbnvbU^h0x1eq7fJvlnVLFRbE~nM71X*_Gu;iWE?7BPY%Ov4k z4q)&BtyuJob#%#>hD2L%p>e*g$_md;>DHSdvf>iGo+cLIa*yX<bmZFk;2DBFg~Cf5 zup8J)F(mH|iQ!hs6Y^zfx^g5NR_yQ;J>K6{mnFW-e=>Wld_ZAsBSQ;W22f{hRMPmO ztY-#kV;`U|e>$6_f}N`<Dyip_UYXqQTIidcFzMZ-!{W{>_P(WZ^+aS+Qb>g|bcDaR z$TuocSxmkd5lNTV%|c%$IG10xe&as()Ed3Z0962w<qtx8LlTa8QkykQy`G$c6OIVf z5kgeyN^AP<rOMVmKdzg4+C!y(C1&}?XJJSFbh>hW7JLP9eRQkmw|B0?^nx;oq6v>I zAPKFK^&y;LYWp$akY_s0=b<Bol6T9QNWGQYAo-i3_CN-=xIfD=^9;dQFzBSR<gt0u z3;>yeGT}E5QS78oewH_MMp?c^2BUMoT&tgtF--tj+>=%!QbLmIS+>LR6d*zF7g6@a zf9)Bh?_4*plr5;;DP>?Qom-WACIvWtXy<2IAco-()E3WP`^?eIFA*NiHz{vWbrMR5 zr%);Ro`8_jcc*w)(Eq1R@35*5yW=IJjQN$hMAAgg9ct`}wym7f3bnB$Hv$VmfcLJ5 zs28U2yaoBH&`9YaaDLcGFn@jV?52WuY1^S@<peq7K6V0)ia-q=qkUa|q^xH}Nnk=w zoaXMmI0*<pc`DIn^tLsEK|d;3jmFYI+7#3MJ1qgtMXxd0Ejr$<TimQm?q^gjyOyS5 z^gsbY79f3Sva5P9;S=qyZAtW01c|yrnHaD8a1`hk{DA<$6|U&bT~CYLzapm;K#9M8 zc7tcX_cvw5_x`~BCY&gVk;%w>gtfPug_DcC#@;=4I$7|Og$HY6!`(3tv2Qdk&=PWG zN-%ke*A{HyG8g6K(ugQ$!8bvDGA-{1$*%)KEl5GN9e$;xpi_g^_P}n6(h?87D>vxZ z!aT&~%7|?=Msf@oe>nD%5k-qZ!b8F@vQ(%n`{cSOM+x+4=~?O7%z1;2eXmj!s>jP3 zv~|&+1TZ+!m>*{9Eaa$IT@bmMe&GG2qF{E{j?vBUmXHbMqmxIao5Clb<ZfaIE=sTF ze(<HDU2a;BOKT7^y0=c1DPO%0uzjQdtYX9lumZ&}7HPMvw~;MgC-*LI8pf~9(Xx}y zOyoe*kU&=J6Xeyy;DLOKN?wd#doOL>hGlRUD>$Ix)$+j=ai#?gQeLHNts>;#*8hJX zO5u6p_112e>Ul9R2YsB3T+ofER6nN{r-(%PmoL;5>QwFE*6ST6CQ<>kSdFs+6Zg-F z#Am4;T6!eQ9e&<LA|JpCw^_wwRr24=lTRQ!j=-M`=Yy~4tUV>~W`-BYalLTyA~?OO z(nLw9$z@~D_x$;cNXUD|0f<bC?;#Qh4hd!d&kuW93B+CE<XOm2qON|rFy9Z;IB8pa zUM{$NZUzNRfSwJsY}dCI)-!ja&*?3o8lb#4_*Hm?Pr%tZStXRoru@)|#A+=E3JEy1 zPXDwr3i1cfQP*j8eBO5ZwQeb<As((aEA3X`ejz4P#YBiw#fmE`SG^tJTzmWFO+HyX z{%)Cqh8>y_+aF~$0c2yaP-VEqq}MnkTeH%I_&ZMtDxvTv@Cjb7vuiOK(lKY^u`2VH z=!wx_|EVx;fc9wRka;Z7f9mT<J&x}Q_8-y?Z?l${%xzEaZlBl?ipB^VD}=51^B1;v z0uX@li}1iidH4nO`C;M${Nlo5k6<uy7>pJa9s6$vuI@IDc7FeA0231z5D^y^`iH^s za4y-M0nOht=(*b=e5_vB0<t#l*0xNlu2%N8y0%s}exCid5_d{~s-l)cr5rr;zW~XK BC`<qV literal 0 HcmV?d00001 diff --git a/public/css/jquery-ui/images/ui-icons_777620_256x240.png b/public/css/jquery-ui/images/ui-icons_777620_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..9785948a293a095a65e34ffb775dfc252bb11c7b GIT binary patch literal 4549 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 Lfku@Y`pN$Q2%9)j literal 0 HcmV?d00001 diff --git a/public/css/jquery-ui/images/ui-icons_777777_256x240.png b/public/css/jquery-ui/images/ui-icons_777777_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..323c4564a74caa26eca81548d184610bcaedfb1d GIT binary patch literal 6999 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 B6afGL literal 0 HcmV?d00001 diff --git a/public/css/jquery-ui/images/ui-icons_cc0000_256x240.png b/public/css/jquery-ui/images/ui-icons_cc0000_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..45ac7787cd2bb4d6c3eea7e6e4a893034ddf75d8 GIT binary patch literal 4549 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 LG}fuoLO=c=v^XOP literal 0 HcmV?d00001 diff --git a/public/css/jquery-ui/images/ui-icons_ffffff_256x240.png b/public/css/jquery-ui/images/ui-icons_ffffff_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..fe41d2d0fdd40f87538d2312fa537a799994e55b GIT binary patch literal 6299 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 literal 0 HcmV?d00001 diff --git a/public/css/jquery-ui/jquery-ui.structure.min.css b/public/css/jquery-ui/jquery-ui.structure.min.css new file mode 100644 index 0000000000..882d445bde --- /dev/null +++ b/public/css/jquery-ui/jquery-ui.structure.min.css @@ -0,0 +1,5 @@ +/*! jQuery UI - v1.12.1 - 2017-01-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +.ui-sortable-handle{-ms-touch-action:none;touch-action:none}.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:45%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-datepicker .ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat;left:.5em;top:.3em} \ No newline at end of file diff --git a/public/css/jquery-ui/jquery-ui.theme.min.css b/public/css/jquery-ui/jquery-ui.theme.min.css new file mode 100644 index 0000000000..4c88d3c36b --- /dev/null +++ b/public/css/jquery-ui/jquery-ui.theme.min.css @@ -0,0 +1,5 @@ +/*! jQuery UI - v1.12.1 - 2017-01-15 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +.ui-widget{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget.ui-widget-content{border:1px solid #c5c5c5}.ui-widget-content{border:1px solid #ddd;background:#fff;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #ddd;background:#e9e9e9;color:#333;font-weight:bold}.ui-widget-header a{color:#333}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default,.ui-button,html .ui-button.ui-state-disabled:hover,html .ui-button.ui-state-disabled:active{border:1px solid #c5c5c5;background:#f6f6f6;font-weight:normal;color:#454545}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited,a.ui-button,a:link.ui-button,a:visited.ui-button,.ui-button{color:#454545;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus,.ui-button:hover,.ui-button:focus{border:1px solid #ccc;background:#ededed;font-weight:normal;color:#2b2b2b}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited,a.ui-button:hover,a.ui-button:focus{color:#2b2b2b;text-decoration:none}.ui-visual-focus{box-shadow:0 0 3px 1px rgb(94,158,214)}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active,a.ui-button:active,.ui-button:active,.ui-button.ui-state-active:hover{border:1px solid #003eff;background:#007fff;font-weight:normal;color:#fff}.ui-icon-background,.ui-state-active .ui-icon-background{border:#003eff;background-color:#fff}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#fff;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #dad55e;background:#fffa90;color:#777620}.ui-state-checked{border:1px solid #dad55e;background:#fffa90}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#777620}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #f1a899;background:#fddfdf;color:#5f3f3f}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#5f3f3f}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#5f3f3f}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon,.ui-button:hover .ui-icon,.ui-button:focus .ui-icon{background-image:url("images/ui-icons_555555_256x240.png")}.ui-state-active .ui-icon,.ui-button:active .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-state-highlight .ui-icon,.ui-button .ui-state-highlight.ui-icon{background-image:url("images/ui-icons_777620_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cc0000_256x240.png")}.ui-button .ui-icon{background-image:url("images/ui-icons_777777_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-caret-1-n{background-position:0 0}.ui-icon-caret-1-ne{background-position:-16px 0}.ui-icon-caret-1-e{background-position:-32px 0}.ui-icon-caret-1-se{background-position:-48px 0}.ui-icon-caret-1-s{background-position:-65px 0}.ui-icon-caret-1-sw{background-position:-80px 0}.ui-icon-caret-1-w{background-position:-96px 0}.ui-icon-caret-1-nw{background-position:-112px 0}.ui-icon-caret-2-n-s{background-position:-128px 0}.ui-icon-caret-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-65px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-65px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:1px -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:3px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:3px}.ui-widget-overlay{background:#aaa;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{-webkit-box-shadow:0 0 5px #666;box-shadow:0 0 5px #666} \ No newline at end of file diff --git a/public/js/ff/transactions/single/create.js b/public/js/ff/transactions/single/create.js index a4b8f5e551..0a9ce44fe7 100644 --- a/public/js/ff/transactions/single/create.js +++ b/public/js/ff/transactions/single/create.js @@ -20,6 +20,14 @@ $(document).ready(function () { updateDescription(); } + if (!Modernizr.inputtypes.date) { + $('input[type="date"]').datepicker( + { + dateFormat: 'yy-mm-dd' + } + ); + } + // get JSON things: getJSONautocomplete(); diff --git a/public/js/ff/transactions/single/edit.js b/public/js/ff/transactions/single/edit.js index e122ca0e04..8e6552758c 100644 --- a/public/js/ff/transactions/single/edit.js +++ b/public/js/ff/transactions/single/edit.js @@ -10,7 +10,15 @@ $(document).ready(function () { "use strict"; - // no special JS for edit transaction. + // give date a datepicker if not natively supported. + if (!Modernizr.inputtypes.date) { + $('input[type="date"]').datepicker( + { + dateFormat: 'yy-mm-dd' + } + ); + } + // the destination account name is always an expense account name. if ($('input[name="destination_account_name"]').length > 0) { @@ -32,7 +40,9 @@ $(document).ready(function () { var opt = { typeahead: { source: data, - afterSelect: function(val) { this.$element.val(""); } + afterSelect: function (val) { + this.$element.val(""); + } } }; $('input[name="tags"]').tagsinput( diff --git a/public/js/lib/jquery-ui.min.js b/public/js/lib/jquery-ui.min.js index cf8c80b490..6b5cb48a34 100644 --- a/public/js/lib/jquery-ui.min.js +++ b/public/js/lib/jquery-ui.min.js @@ -1,7 +1,8 @@ -/*! jQuery UI - v1.12.1 - 2017-01-01 +/*! jQuery UI - v1.12.1 - 2017-01-15 * http://jqueryui.com -* Includes: widget.js, data.js, scroll-parent.js, widgets/sortable.js, widgets/mouse.js +* Includes: widget.js, data.js, keycode.js, scroll-parent.js, widgets/sortable.js, widgets/datepicker.js, widgets/mouse.js * Copyright jQuery Foundation and other contributors; Licensed MIT */ -(function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)})(function(t){t.ui=t.ui||{},t.ui.version="1.12.1";var e=0,i=Array.prototype.slice;t.cleanData=function(e){return function(i){var s,n,o;for(o=0;null!=(n=i[o]);o++)try{s=t._data(n,"events"),s&&s.remove&&t(n).triggerHandler("remove")}catch(a){}e(i)}}(t.cleanData),t.widget=function(e,i,s){var n,o,a,r={},h=e.split(".")[0];e=e.split(".")[1];var l=h+"-"+e;return s||(s=i,i=t.Widget),t.isArray(s)&&(s=t.extend.apply(null,[{}].concat(s))),t.expr[":"][l.toLowerCase()]=function(e){return!!t.data(e,l)},t[h]=t[h]||{},n=t[h][e],o=t[h][e]=function(t,e){return this._createWidget?(arguments.length&&this._createWidget(t,e),void 0):new o(t,e)},t.extend(o,n,{version:s.version,_proto:t.extend({},s),_childConstructors:[]}),a=new i,a.options=t.widget.extend({},a.options),t.each(s,function(e,s){return t.isFunction(s)?(r[e]=function(){function t(){return i.prototype[e].apply(this,arguments)}function n(t){return i.prototype[e].apply(this,t)}return function(){var e,i=this._super,o=this._superApply;return this._super=t,this._superApply=n,e=s.apply(this,arguments),this._super=i,this._superApply=o,e}}(),void 0):(r[e]=s,void 0)}),o.prototype=t.widget.extend(a,{widgetEventPrefix:n?a.widgetEventPrefix||e:e},r,{constructor:o,namespace:h,widgetName:e,widgetFullName:l}),n?(t.each(n._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete n._childConstructors):i._childConstructors.push(o),t.widget.bridge(e,o),o},t.widget.extend=function(e){for(var s,n,o=i.call(arguments,1),a=0,r=o.length;r>a;a++)for(s in o[a])n=o[a][s],o[a].hasOwnProperty(s)&&void 0!==n&&(e[s]=t.isPlainObject(n)?t.isPlainObject(e[s])?t.widget.extend({},e[s],n):t.widget.extend({},n):n);return e},t.widget.bridge=function(e,s){var n=s.prototype.widgetFullName||e;t.fn[e]=function(o){var a="string"==typeof o,r=i.call(arguments,1),h=this;return a?this.length||"instance"!==o?this.each(function(){var i,s=t.data(this,n);return"instance"===o?(h=s,!1):s?t.isFunction(s[o])&&"_"!==o.charAt(0)?(i=s[o].apply(s,r),i!==s&&void 0!==i?(h=i&&i.jquery?h.pushStack(i.get()):i,!1):void 0):t.error("no such method '"+o+"' for "+e+" widget instance"):t.error("cannot call methods on "+e+" prior to initialization; "+"attempted to call method '"+o+"'")}):h=void 0:(r.length&&(o=t.widget.extend.apply(null,[o].concat(r))),this.each(function(){var e=t.data(this,n);e?(e.option(o||{}),e._init&&e._init()):t.data(this,n,new s(o,this))})),h}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{classes:{},disabled:!1,create:null},_createWidget:function(i,s){s=t(s||this.defaultElement||this)[0],this.element=t(s),this.uuid=e++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),this.classesElementLookup={},s!==this&&(t.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===s&&this.destroy()}}),this.document=t(s.style?s.ownerDocument:s.document||s),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),i),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){var e=this;this._destroy(),t.each(this.classesElementLookup,function(t,i){e._removeClass(i,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,n,o,a=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(a={},s=e.split("."),e=s.shift(),s.length){for(n=a[e]=t.widget.extend({},this.options[e]),o=0;s.length-1>o;o++)n[s[o]]=n[s[o]]||{},n=n[s[o]];if(e=s.pop(),1===arguments.length)return void 0===n[e]?null:n[e];n[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];a[e]=i}return this._setOptions(a),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(e){var i,s,n;for(i in e)n=this.classesElementLookup[i],e[i]!==this.options.classes[i]&&n&&n.length&&(s=t(n.get()),this._removeClass(n,i),s.addClass(this._classes({element:s,keys:i,classes:e,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(e){function i(i,o){var a,r;for(r=0;i.length>r;r++)a=n.classesElementLookup[i[r]]||t(),a=e.add?t(t.unique(a.get().concat(e.element.get()))):t(a.not(e.element).get()),n.classesElementLookup[i[r]]=a,s.push(i[r]),o&&e.classes[i[r]]&&s.push(e.classes[i[r]])}var s=[],n=this;return e=t.extend({element:this.element,classes:this.options.classes||{}},e),this._on(e.element,{remove:"_untrackClassesElement"}),e.keys&&i(e.keys.match(/\S+/g)||[],!0),e.extra&&i(e.extra.match(/\S+/g)||[]),s.join(" ")},_untrackClassesElement:function(e){var i=this;t.each(i.classesElementLookup,function(s,n){-1!==t.inArray(e.target,n)&&(i.classesElementLookup[s]=t(n.not(e.target).get()))})},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){s="boolean"==typeof s?s:i;var n="string"==typeof t||null===t,o={extra:n?e:i,keys:n?t:e,element:n?this.element:t,add:s};return o.element.toggleClass(this._classes(o),s),this},_on:function(e,i,s){var n,o=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=n=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),t.each(s,function(s,a){function r(){return e||o.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof a?o[a]:a).apply(o,arguments):void 0}"string"!=typeof a&&(r.guid=a.guid=a.guid||r.guid||t.guid++);var h=s.match(/^([\w:-]*)\s*(.*)$/),l=h[1]+o.eventNamespace,c=h[2];c?n.on(l,c,r):i.on(l,r)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.off(i).off(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){this._addClass(t(e.currentTarget),null,"ui-state-hover")},mouseleave:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){this._addClass(t(e.currentTarget),null,"ui-state-focus")},focusout:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}}),t.widget,t.extend(t.expr[":"],{data:t.expr.createPseudo?t.expr.createPseudo(function(e){return function(i){return!!t.data(i,e)}}):function(e,i,s){return!!t.data(e,s[3])}}),t.fn.scrollParent=function(e){var i=this.css("position"),s="absolute"===i,n=e?/(auto|scroll|hidden)/:/(auto|scroll)/,o=this.parents().filter(function(){var e=t(this);return s&&"static"===e.css("position")?!1:n.test(e.css("overflow")+e.css("overflow-y")+e.css("overflow-x"))}).eq(0);return"fixed"!==i&&o.length?o:t(this[0].ownerDocument||document)},t.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase());var s=!1;t(document).on("mouseup",function(){s=!1}),t.widget("ui.mouse",{version:"1.12.1",options:{cancel:"input, textarea, button, select, option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.on("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).on("click."+this.widgetName,function(i){return!0===t.data(i.target,e.widgetName+".preventClickEvent")?(t.removeData(i.target,e.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.off("."+this.widgetName),this._mouseMoveDelegate&&this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(e){if(!s){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(e),this._mouseDownEvent=e;var i=this,n=1===e.which,o="string"==typeof this.options.cancel&&e.target.nodeName?t(e.target).closest(this.options.cancel).length:!1;return n&&!o&&this._mouseCapture(e)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(e)!==!1,!this._mouseStarted)?(e.preventDefault(),!0):(!0===t.data(e.target,this.widgetName+".preventClickEvent")&&t.removeData(e.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return i._mouseMove(t)},this._mouseUpDelegate=function(t){return i._mouseUp(t)},this.document.on("mousemove."+this.widgetName,this._mouseMoveDelegate).on("mouseup."+this.widgetName,this._mouseUpDelegate),e.preventDefault(),s=!0,!0)):!0}},_mouseMove:function(e){if(this._mouseMoved){if(t.ui.ie&&(!document.documentMode||9>document.documentMode)&&!e.button)return this._mouseUp(e);if(!e.which)if(e.originalEvent.altKey||e.originalEvent.ctrlKey||e.originalEvent.metaKey||e.originalEvent.shiftKey)this.ignoreMissingWhich=!0;else if(!this.ignoreMissingWhich)return this._mouseUp(e)}return(e.which||e.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(e),e.preventDefault()):(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,e)!==!1,this._mouseStarted?this._mouseDrag(e):this._mouseUp(e)),!this._mouseStarted)},_mouseUp:function(e){this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,e.target===this._mouseDownEvent.target&&t.data(e.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(e)),this._mouseDelayTimer&&(clearTimeout(this._mouseDelayTimer),delete this._mouseDelayTimer),this.ignoreMissingWhich=!1,s=!1,e.preventDefault()},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),t.widget("ui.sortable",t.ui.mouse,{version:"1.12.1",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_isOverAxis:function(t,e,i){return t>=e&&e+i>t},_isFloating:function(t){return/left|right/.test(t.css("float"))||/inline|table-cell/.test(t.css("display"))},_create:function(){this.containerCache={},this._addClass("ui-sortable"),this.refresh(),this.offset=this.element.offset(),this._mouseInit(),this._setHandleClassName(),this.ready=!0},_setOption:function(t,e){this._super(t,e),"handle"===t&&this._setHandleClassName()},_setHandleClassName:function(){var e=this;this._removeClass(this.element.find(".ui-sortable-handle"),"ui-sortable-handle"),t.each(this.items,function(){e._addClass(this.instance.options.handle?this.item.find(this.instance.options.handle):this.item,"ui-sortable-handle")})},_destroy:function(){this._mouseDestroy();for(var t=this.items.length-1;t>=0;t--)this.items[t].item.removeData(this.widgetName+"-item");return this},_mouseCapture:function(e,i){var s=null,n=!1,o=this;return this.reverting?!1:this.options.disabled||"static"===this.options.type?!1:(this._refreshItems(e),t(e.target).parents().each(function(){return t.data(this,o.widgetName+"-item")===o?(s=t(this),!1):void 0}),t.data(e.target,o.widgetName+"-item")===o&&(s=t(e.target)),s?!this.options.handle||i||(t(this.options.handle,s).find("*").addBack().each(function(){this===e.target&&(n=!0)}),n)?(this.currentItem=s,this._removeCurrentsFromItems(),!0):!1:!1)},_mouseStart:function(e,i,s){var n,o,a=this.options;if(this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(e),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},t.extend(this.offset,{click:{left:e.pageX-this.offset.left,top:e.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),this.originalPosition=this._generatePosition(e),this.originalPageX=e.pageX,this.originalPageY=e.pageY,a.cursorAt&&this._adjustOffsetFromHelper(a.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!==this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),a.containment&&this._setContainment(),a.cursor&&"auto"!==a.cursor&&(o=this.document.find("body"),this.storedCursor=o.css("cursor"),o.css("cursor",a.cursor),this.storedStylesheet=t("<style>*{ cursor: "+a.cursor+" !important; }</style>").appendTo(o)),a.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",a.opacity)),a.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",a.zIndex)),this.scrollParent[0]!==this.document[0]&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",e,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions(),!s)for(n=this.containers.length-1;n>=0;n--)this.containers[n]._trigger("activate",e,this._uiHash(this));return t.ui.ddmanager&&(t.ui.ddmanager.current=this),t.ui.ddmanager&&!a.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this.dragging=!0,this._addClass(this.helper,"ui-sortable-helper"),this._mouseDrag(e),!0},_mouseDrag:function(e){var i,s,n,o,a=this.options,r=!1;for(this.position=this._generatePosition(e),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs),this.options.scroll&&(this.scrollParent[0]!==this.document[0]&&"HTML"!==this.scrollParent[0].tagName?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-e.pageY<a.scrollSensitivity?this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop+a.scrollSpeed:e.pageY-this.overflowOffset.top<a.scrollSensitivity&&(this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop-a.scrollSpeed),this.overflowOffset.left+this.scrollParent[0].offsetWidth-e.pageX<a.scrollSensitivity?this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft+a.scrollSpeed:e.pageX-this.overflowOffset.left<a.scrollSensitivity&&(this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft-a.scrollSpeed)):(e.pageY-this.document.scrollTop()<a.scrollSensitivity?r=this.document.scrollTop(this.document.scrollTop()-a.scrollSpeed):this.window.height()-(e.pageY-this.document.scrollTop())<a.scrollSensitivity&&(r=this.document.scrollTop(this.document.scrollTop()+a.scrollSpeed)),e.pageX-this.document.scrollLeft()<a.scrollSensitivity?r=this.document.scrollLeft(this.document.scrollLeft()-a.scrollSpeed):this.window.width()-(e.pageX-this.document.scrollLeft())<a.scrollSensitivity&&(r=this.document.scrollLeft(this.document.scrollLeft()+a.scrollSpeed))),r!==!1&&t.ui.ddmanager&&!a.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e)),this.positionAbs=this._convertPositionTo("absolute"),this.options.axis&&"y"===this.options.axis||(this.helper[0].style.left=this.position.left+"px"),this.options.axis&&"x"===this.options.axis||(this.helper[0].style.top=this.position.top+"px"),i=this.items.length-1;i>=0;i--)if(s=this.items[i],n=s.item[0],o=this._intersectsWithPointer(s),o&&s.instance===this.currentContainer&&n!==this.currentItem[0]&&this.placeholder[1===o?"next":"prev"]()[0]!==n&&!t.contains(this.placeholder[0],n)&&("semi-dynamic"===this.options.type?!t.contains(this.element[0],n):!0)){if(this.direction=1===o?"down":"up","pointer"!==this.options.tolerance&&!this._intersectsWithSides(s))break;this._rearrange(e,s),this._trigger("change",e,this._uiHash());break}return this._contactContainers(e),t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),this._trigger("sort",e,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(e,i){if(e){if(t.ui.ddmanager&&!this.options.dropBehaviour&&t.ui.ddmanager.drop(this,e),this.options.revert){var s=this,n=this.placeholder.offset(),o=this.options.axis,a={};o&&"x"!==o||(a.left=n.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]===this.document[0].body?0:this.offsetParent[0].scrollLeft)),o&&"y"!==o||(a.top=n.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]===this.document[0].body?0:this.offsetParent[0].scrollTop)),this.reverting=!0,t(this.helper).animate(a,parseInt(this.options.revert,10)||500,function(){s._clear(e)})}else this._clear(e,i);return!1}},cancel:function(){if(this.dragging){this._mouseUp(new t.Event("mouseup",{target:null})),"original"===this.options.helper?(this.currentItem.css(this._storedCSS),this._removeClass(this.currentItem,"ui-sortable-helper")):this.currentItem.show();for(var e=this.containers.length-1;e>=0;e--)this.containers[e]._trigger("deactivate",null,this._uiHash(this)),this.containers[e].containerCache.over&&(this.containers[e]._trigger("out",null,this._uiHash(this)),this.containers[e].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),"original"!==this.options.helper&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),t.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?t(this.domPosition.prev).after(this.currentItem):t(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},t(i).each(function(){var i=(t(e.item||this).attr(e.attribute||"id")||"").match(e.expression||/(.+)[\-=_](.+)/);i&&s.push((e.key||i[1]+"[]")+"="+(e.key&&e.expression?i[1]:i[2]))}),!s.length&&e.key&&s.push(e.key+"="),s.join("&")},toArray:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},i.each(function(){s.push(t(e.item||this).attr(e.attribute||"id")||"")}),s},_intersectsWith:function(t){var e=this.positionAbs.left,i=e+this.helperProportions.width,s=this.positionAbs.top,n=s+this.helperProportions.height,o=t.left,a=o+t.width,r=t.top,h=r+t.height,l=this.offset.click.top,c=this.offset.click.left,u="x"===this.options.axis||s+l>r&&h>s+l,d="y"===this.options.axis||e+c>o&&a>e+c,p=u&&d;return"pointer"===this.options.tolerance||this.options.forcePointerForContainers||"pointer"!==this.options.tolerance&&this.helperProportions[this.floating?"width":"height"]>t[this.floating?"width":"height"]?p:e+this.helperProportions.width/2>o&&a>i-this.helperProportions.width/2&&s+this.helperProportions.height/2>r&&h>n-this.helperProportions.height/2},_intersectsWithPointer:function(t){var e,i,s="x"===this.options.axis||this._isOverAxis(this.positionAbs.top+this.offset.click.top,t.top,t.height),n="y"===this.options.axis||this._isOverAxis(this.positionAbs.left+this.offset.click.left,t.left,t.width),o=s&&n;return o?(e=this._getDragVerticalDirection(),i=this._getDragHorizontalDirection(),this.floating?"right"===i||"down"===e?2:1:e&&("down"===e?2:1)):!1},_intersectsWithSides:function(t){var e=this._isOverAxis(this.positionAbs.top+this.offset.click.top,t.top+t.height/2,t.height),i=this._isOverAxis(this.positionAbs.left+this.offset.click.left,t.left+t.width/2,t.width),s=this._getDragVerticalDirection(),n=this._getDragHorizontalDirection();return this.floating&&n?"right"===n&&i||"left"===n&&!i:s&&("down"===s&&e||"up"===s&&!e)},_getDragVerticalDirection:function(){var t=this.positionAbs.top-this.lastPositionAbs.top;return 0!==t&&(t>0?"down":"up")},_getDragHorizontalDirection:function(){var t=this.positionAbs.left-this.lastPositionAbs.left;return 0!==t&&(t>0?"right":"left")},refresh:function(t){return this._refreshItems(t),this._setHandleClassName(),this.refreshPositions(),this},_connectWith:function(){var t=this.options;return t.connectWith.constructor===String?[t.connectWith]:t.connectWith},_getItemsAsjQuery:function(e){function i(){r.push(this)}var s,n,o,a,r=[],h=[],l=this._connectWith();if(l&&e)for(s=l.length-1;s>=0;s--)for(o=t(l[s],this.document[0]),n=o.length-1;n>=0;n--)a=t.data(o[n],this.widgetFullName),a&&a!==this&&!a.options.disabled&&h.push([t.isFunction(a.options.items)?a.options.items.call(a.element):t(a.options.items,a.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),a]);for(h.push([t.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):t(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]),s=h.length-1;s>=0;s--)h[s][0].each(i);return t(r)},_removeCurrentsFromItems:function(){var e=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=t.grep(this.items,function(t){for(var i=0;e.length>i;i++)if(e[i]===t.item[0])return!1;return!0})},_refreshItems:function(e){this.items=[],this.containers=[this];var i,s,n,o,a,r,h,l,c=this.items,u=[[t.isFunction(this.options.items)?this.options.items.call(this.element[0],e,{item:this.currentItem}):t(this.options.items,this.element),this]],d=this._connectWith();if(d&&this.ready)for(i=d.length-1;i>=0;i--)for(n=t(d[i],this.document[0]),s=n.length-1;s>=0;s--)o=t.data(n[s],this.widgetFullName),o&&o!==this&&!o.options.disabled&&(u.push([t.isFunction(o.options.items)?o.options.items.call(o.element[0],e,{item:this.currentItem}):t(o.options.items,o.element),o]),this.containers.push(o));for(i=u.length-1;i>=0;i--)for(a=u[i][1],r=u[i][0],s=0,l=r.length;l>s;s++)h=t(r[s]),h.data(this.widgetName+"-item",a),c.push({item:h,instance:a,width:0,height:0,left:0,top:0})},refreshPositions:function(e){this.floating=this.items.length?"x"===this.options.axis||this._isFloating(this.items[0].item):!1,this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());var i,s,n,o;for(i=this.items.length-1;i>=0;i--)s=this.items[i],s.instance!==this.currentContainer&&this.currentContainer&&s.item[0]!==this.currentItem[0]||(n=this.options.toleranceElement?t(this.options.toleranceElement,s.item):s.item,e||(s.width=n.outerWidth(),s.height=n.outerHeight()),o=n.offset(),s.left=o.left,s.top=o.top);if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(i=this.containers.length-1;i>=0;i--)o=this.containers[i].element.offset(),this.containers[i].containerCache.left=o.left,this.containers[i].containerCache.top=o.top,this.containers[i].containerCache.width=this.containers[i].element.outerWidth(),this.containers[i].containerCache.height=this.containers[i].element.outerHeight();return this},_createPlaceholder:function(e){e=e||this;var i,s=e.options;s.placeholder&&s.placeholder.constructor!==String||(i=s.placeholder,s.placeholder={element:function(){var s=e.currentItem[0].nodeName.toLowerCase(),n=t("<"+s+">",e.document[0]);return e._addClass(n,"ui-sortable-placeholder",i||e.currentItem[0].className)._removeClass(n,"ui-sortable-helper"),"tbody"===s?e._createTrPlaceholder(e.currentItem.find("tr").eq(0),t("<tr>",e.document[0]).appendTo(n)):"tr"===s?e._createTrPlaceholder(e.currentItem,n):"img"===s&&n.attr("src",e.currentItem.attr("src")),i||n.css("visibility","hidden"),n},update:function(t,n){(!i||s.forcePlaceholderSize)&&(n.height()||n.height(e.currentItem.innerHeight()-parseInt(e.currentItem.css("paddingTop")||0,10)-parseInt(e.currentItem.css("paddingBottom")||0,10)),n.width()||n.width(e.currentItem.innerWidth()-parseInt(e.currentItem.css("paddingLeft")||0,10)-parseInt(e.currentItem.css("paddingRight")||0,10)))}}),e.placeholder=t(s.placeholder.element.call(e.element,e.currentItem)),e.currentItem.after(e.placeholder),s.placeholder.update(e,e.placeholder)},_createTrPlaceholder:function(e,i){var s=this;e.children().each(function(){t("<td> </td>",s.document[0]).attr("colspan",t(this).attr("colspan")||1).appendTo(i)})},_contactContainers:function(e){var i,s,n,o,a,r,h,l,c,u,d=null,p=null;for(i=this.containers.length-1;i>=0;i--)if(!t.contains(this.currentItem[0],this.containers[i].element[0]))if(this._intersectsWith(this.containers[i].containerCache)){if(d&&t.contains(this.containers[i].element[0],d.element[0]))continue;d=this.containers[i],p=i}else this.containers[i].containerCache.over&&(this.containers[i]._trigger("out",e,this._uiHash(this)),this.containers[i].containerCache.over=0);if(d)if(1===this.containers.length)this.containers[p].containerCache.over||(this.containers[p]._trigger("over",e,this._uiHash(this)),this.containers[p].containerCache.over=1);else{for(n=1e4,o=null,c=d.floating||this._isFloating(this.currentItem),a=c?"left":"top",r=c?"width":"height",u=c?"pageX":"pageY",s=this.items.length-1;s>=0;s--)t.contains(this.containers[p].element[0],this.items[s].item[0])&&this.items[s].item[0]!==this.currentItem[0]&&(h=this.items[s].item.offset()[a],l=!1,e[u]-h>this.items[s][r]/2&&(l=!0),n>Math.abs(e[u]-h)&&(n=Math.abs(e[u]-h),o=this.items[s],this.direction=l?"up":"down"));if(!o&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[p])return this.currentContainer.containerCache.over||(this.containers[p]._trigger("over",e,this._uiHash()),this.currentContainer.containerCache.over=1),void 0;o?this._rearrange(e,o,null,!0):this._rearrange(e,null,this.containers[p].element,!0),this._trigger("change",e,this._uiHash()),this.containers[p]._trigger("change",e,this._uiHash(this)),this.currentContainer=this.containers[p],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[p]._trigger("over",e,this._uiHash(this)),this.containers[p].containerCache.over=1}},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper)?t(i.helper.apply(this.element[0],[e,this.currentItem])):"clone"===i.helper?this.currentItem.clone():this.currentItem;return s.parents("body").length||t("parent"!==i.appendTo?i.appendTo:this.currentItem[0].parentNode)[0].appendChild(s[0]),s[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!s[0].style.width||i.forceHelperSize)&&s.width(this.currentItem.width()),(!s[0].style.height||i.forceHelperSize)&&s.height(this.currentItem.height()),s},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var e=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===this.document[0].body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&t.ui.ie)&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var t=this.currentItem.position();return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:t.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options;"parent"===n.containment&&(n.containment=this.helper[0].parentNode),("document"===n.containment||"window"===n.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,"document"===n.containment?this.document.width():this.window.width()-this.helperProportions.width-this.margins.left,("document"===n.containment?this.document.height()||document.body.parentNode.scrollHeight:this.window.height()||this.document[0].body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(n.containment)||(e=t(n.containment)[0],i=t(n.containment).offset(),s="hidden"!==t(e).css("overflow"),this.containment=[i.left+(parseInt(t(e).css("borderLeftWidth"),10)||0)+(parseInt(t(e).css("paddingLeft"),10)||0)-this.margins.left,i.top+(parseInt(t(e).css("borderTopWidth"),10)||0)+(parseInt(t(e).css("paddingTop"),10)||0)-this.margins.top,i.left+(s?Math.max(e.scrollWidth,e.offsetWidth):e.offsetWidth)-(parseInt(t(e).css("borderLeftWidth"),10)||0)-(parseInt(t(e).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,i.top+(s?Math.max(e.scrollHeight,e.offsetHeight):e.offsetHeight)-(parseInt(t(e).css("borderTopWidth"),10)||0)-(parseInt(t(e).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(e,i){i||(i=this.position);var s="absolute"===e?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,o=/(html|body)/i.test(n[0].tagName);return{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():o?0:n.scrollTop())*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():o?0:n.scrollLeft())*s} -},_generatePosition:function(e){var i,s,n=this.options,o=e.pageX,a=e.pageY,r="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=/(html|body)/i.test(r[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(e.pageX-this.offset.click.left<this.containment[0]&&(o=this.containment[0]+this.offset.click.left),e.pageY-this.offset.click.top<this.containment[1]&&(a=this.containment[1]+this.offset.click.top),e.pageX-this.offset.click.left>this.containment[2]&&(o=this.containment[2]+this.offset.click.left),e.pageY-this.offset.click.top>this.containment[3]&&(a=this.containment[3]+this.offset.click.top)),n.grid&&(i=this.originalPageY+Math.round((a-this.originalPageY)/n.grid[1])*n.grid[1],a=this.containment?i-this.offset.click.top>=this.containment[1]&&i-this.offset.click.top<=this.containment[3]?i:i-this.offset.click.top>=this.containment[1]?i-n.grid[1]:i+n.grid[1]:i,s=this.originalPageX+Math.round((o-this.originalPageX)/n.grid[0])*n.grid[0],o=this.containment?s-this.offset.click.left>=this.containment[0]&&s-this.offset.click.left<=this.containment[2]?s:s-this.offset.click.left>=this.containment[0]?s-n.grid[0]:s+n.grid[0]:s)),{top:a-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():h?0:r.scrollTop()),left:o-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():h?0:r.scrollLeft())}},_rearrange:function(t,e,i,s){i?i[0].appendChild(this.placeholder[0]):e.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?e.item[0]:e.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter;this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(t,e){function i(t,e,i){return function(s){i._trigger(t,s,e._uiHash(e))}}this.reverting=!1;var s,n=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(s in this._storedCSS)("auto"===this._storedCSS[s]||"static"===this._storedCSS[s])&&(this._storedCSS[s]="");this.currentItem.css(this._storedCSS),this._removeClass(this.currentItem,"ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!e&&n.push(function(t){this._trigger("receive",t,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||e||n.push(function(t){this._trigger("update",t,this._uiHash())}),this!==this.currentContainer&&(e||(n.push(function(t){this._trigger("remove",t,this._uiHash())}),n.push(function(t){return function(e){t._trigger("receive",e,this._uiHash(this))}}.call(this,this.currentContainer)),n.push(function(t){return function(e){t._trigger("update",e,this._uiHash(this))}}.call(this,this.currentContainer)))),s=this.containers.length-1;s>=0;s--)e||n.push(i("deactivate",this,this.containers[s])),this.containers[s].containerCache.over&&(n.push(i("out",this,this.containers[s])),this.containers[s].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,e||this._trigger("beforeStop",t,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.cancelHelperRemoval||(this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null),!e){for(s=0;n.length>s;s++)n[s].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!this.cancelHelperRemoval},_trigger:function(){t.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(e){var i=e||this;return{helper:i.helper,placeholder:i.placeholder||t([]),position:i.position,originalPosition:i.originalPosition,offset:i.positionAbs,item:i.currentItem,sender:e?e.element:null}}})}); \ No newline at end of file +(function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)})(function(t){function e(t){for(var e,i;t.length&&t[0]!==document;){if(e=t.css("position"),("absolute"===e||"relative"===e||"fixed"===e)&&(i=parseInt(t.css("zIndex"),10),!isNaN(i)&&0!==i))return i;t=t.parent()}return 0}function i(){this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},t.extend(this._defaults,this.regional[""]),this.regional.en=t.extend(!0,{},this.regional[""]),this.regional["en-US"]=t.extend(!0,{},this.regional.en),this.dpDiv=s(t("<div id='"+this._mainDivId+"' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"))}function s(e){var i="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return e.on("mouseout",i,function(){t(this).removeClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&t(this).removeClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&t(this).removeClass("ui-datepicker-next-hover")}).on("mouseover",i,n)}function n(){t.datepicker._isDisabledDatepicker(h.inline?h.dpDiv.parent()[0]:h.input[0])||(t(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),t(this).addClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&t(this).addClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&t(this).addClass("ui-datepicker-next-hover"))}function o(e,i){t.extend(e,i);for(var s in i)null==i[s]&&(e[s]=i[s]);return e}t.ui=t.ui||{},t.ui.version="1.12.1";var a=0,r=Array.prototype.slice;t.cleanData=function(e){return function(i){var s,n,o;for(o=0;null!=(n=i[o]);o++)try{s=t._data(n,"events"),s&&s.remove&&t(n).triggerHandler("remove")}catch(a){}e(i)}}(t.cleanData),t.widget=function(e,i,s){var n,o,a,r={},l=e.split(".")[0];e=e.split(".")[1];var h=l+"-"+e;return s||(s=i,i=t.Widget),t.isArray(s)&&(s=t.extend.apply(null,[{}].concat(s))),t.expr[":"][h.toLowerCase()]=function(e){return!!t.data(e,h)},t[l]=t[l]||{},n=t[l][e],o=t[l][e]=function(t,e){return this._createWidget?(arguments.length&&this._createWidget(t,e),void 0):new o(t,e)},t.extend(o,n,{version:s.version,_proto:t.extend({},s),_childConstructors:[]}),a=new i,a.options=t.widget.extend({},a.options),t.each(s,function(e,s){return t.isFunction(s)?(r[e]=function(){function t(){return i.prototype[e].apply(this,arguments)}function n(t){return i.prototype[e].apply(this,t)}return function(){var e,i=this._super,o=this._superApply;return this._super=t,this._superApply=n,e=s.apply(this,arguments),this._super=i,this._superApply=o,e}}(),void 0):(r[e]=s,void 0)}),o.prototype=t.widget.extend(a,{widgetEventPrefix:n?a.widgetEventPrefix||e:e},r,{constructor:o,namespace:l,widgetName:e,widgetFullName:h}),n?(t.each(n._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete n._childConstructors):i._childConstructors.push(o),t.widget.bridge(e,o),o},t.widget.extend=function(e){for(var i,s,n=r.call(arguments,1),o=0,a=n.length;a>o;o++)for(i in n[o])s=n[o][i],n[o].hasOwnProperty(i)&&void 0!==s&&(e[i]=t.isPlainObject(s)?t.isPlainObject(e[i])?t.widget.extend({},e[i],s):t.widget.extend({},s):s);return e},t.widget.bridge=function(e,i){var s=i.prototype.widgetFullName||e;t.fn[e]=function(n){var o="string"==typeof n,a=r.call(arguments,1),l=this;return o?this.length||"instance"!==n?this.each(function(){var i,o=t.data(this,s);return"instance"===n?(l=o,!1):o?t.isFunction(o[n])&&"_"!==n.charAt(0)?(i=o[n].apply(o,a),i!==o&&void 0!==i?(l=i&&i.jquery?l.pushStack(i.get()):i,!1):void 0):t.error("no such method '"+n+"' for "+e+" widget instance"):t.error("cannot call methods on "+e+" prior to initialization; "+"attempted to call method '"+n+"'")}):l=void 0:(a.length&&(n=t.widget.extend.apply(null,[n].concat(a))),this.each(function(){var e=t.data(this,s);e?(e.option(n||{}),e._init&&e._init()):t.data(this,s,new i(n,this))})),l}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{classes:{},disabled:!1,create:null},_createWidget:function(e,i){i=t(i||this.defaultElement||this)[0],this.element=t(i),this.uuid=a++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),this.classesElementLookup={},i!==this&&(t.data(i,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===i&&this.destroy()}}),this.document=t(i.style?i.ownerDocument:i.document||i),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),e),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){var e=this;this._destroy(),t.each(this.classesElementLookup,function(t,i){e._removeClass(i,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,n,o,a=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(a={},s=e.split("."),e=s.shift(),s.length){for(n=a[e]=t.widget.extend({},this.options[e]),o=0;s.length-1>o;o++)n[s[o]]=n[s[o]]||{},n=n[s[o]];if(e=s.pop(),1===arguments.length)return void 0===n[e]?null:n[e];n[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];a[e]=i}return this._setOptions(a),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(e){var i,s,n;for(i in e)n=this.classesElementLookup[i],e[i]!==this.options.classes[i]&&n&&n.length&&(s=t(n.get()),this._removeClass(n,i),s.addClass(this._classes({element:s,keys:i,classes:e,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(e){function i(i,o){var a,r;for(r=0;i.length>r;r++)a=n.classesElementLookup[i[r]]||t(),a=e.add?t(t.unique(a.get().concat(e.element.get()))):t(a.not(e.element).get()),n.classesElementLookup[i[r]]=a,s.push(i[r]),o&&e.classes[i[r]]&&s.push(e.classes[i[r]])}var s=[],n=this;return e=t.extend({element:this.element,classes:this.options.classes||{}},e),this._on(e.element,{remove:"_untrackClassesElement"}),e.keys&&i(e.keys.match(/\S+/g)||[],!0),e.extra&&i(e.extra.match(/\S+/g)||[]),s.join(" ")},_untrackClassesElement:function(e){var i=this;t.each(i.classesElementLookup,function(s,n){-1!==t.inArray(e.target,n)&&(i.classesElementLookup[s]=t(n.not(e.target).get()))})},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){s="boolean"==typeof s?s:i;var n="string"==typeof t||null===t,o={extra:n?e:i,keys:n?t:e,element:n?this.element:t,add:s};return o.element.toggleClass(this._classes(o),s),this},_on:function(e,i,s){var n,o=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=n=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),t.each(s,function(s,a){function r(){return e||o.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof a?o[a]:a).apply(o,arguments):void 0}"string"!=typeof a&&(r.guid=a.guid=a.guid||r.guid||t.guid++);var l=s.match(/^([\w:-]*)\s*(.*)$/),h=l[1]+o.eventNamespace,c=l[2];c?n.on(h,c,r):i.on(h,r)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.off(i).off(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){this._addClass(t(e.currentTarget),null,"ui-state-hover")},mouseleave:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){this._addClass(t(e.currentTarget),null,"ui-state-focus")},focusout:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}}),t.widget,t.extend(t.expr[":"],{data:t.expr.createPseudo?t.expr.createPseudo(function(e){return function(i){return!!t.data(i,e)}}):function(e,i,s){return!!t.data(e,s[3])}}),t.ui.keyCode={BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38},t.fn.scrollParent=function(e){var i=this.css("position"),s="absolute"===i,n=e?/(auto|scroll|hidden)/:/(auto|scroll)/,o=this.parents().filter(function(){var e=t(this);return s&&"static"===e.css("position")?!1:n.test(e.css("overflow")+e.css("overflow-y")+e.css("overflow-x"))}).eq(0);return"fixed"!==i&&o.length?o:t(this[0].ownerDocument||document)},t.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase());var l=!1;t(document).on("mouseup",function(){l=!1}),t.widget("ui.mouse",{version:"1.12.1",options:{cancel:"input, textarea, button, select, option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.on("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).on("click."+this.widgetName,function(i){return!0===t.data(i.target,e.widgetName+".preventClickEvent")?(t.removeData(i.target,e.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.off("."+this.widgetName),this._mouseMoveDelegate&&this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(e){if(!l){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(e),this._mouseDownEvent=e;var i=this,s=1===e.which,n="string"==typeof this.options.cancel&&e.target.nodeName?t(e.target).closest(this.options.cancel).length:!1;return s&&!n&&this._mouseCapture(e)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(e)!==!1,!this._mouseStarted)?(e.preventDefault(),!0):(!0===t.data(e.target,this.widgetName+".preventClickEvent")&&t.removeData(e.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return i._mouseMove(t)},this._mouseUpDelegate=function(t){return i._mouseUp(t)},this.document.on("mousemove."+this.widgetName,this._mouseMoveDelegate).on("mouseup."+this.widgetName,this._mouseUpDelegate),e.preventDefault(),l=!0,!0)):!0}},_mouseMove:function(e){if(this._mouseMoved){if(t.ui.ie&&(!document.documentMode||9>document.documentMode)&&!e.button)return this._mouseUp(e);if(!e.which)if(e.originalEvent.altKey||e.originalEvent.ctrlKey||e.originalEvent.metaKey||e.originalEvent.shiftKey)this.ignoreMissingWhich=!0;else if(!this.ignoreMissingWhich)return this._mouseUp(e)}return(e.which||e.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(e),e.preventDefault()):(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,e)!==!1,this._mouseStarted?this._mouseDrag(e):this._mouseUp(e)),!this._mouseStarted)},_mouseUp:function(e){this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,e.target===this._mouseDownEvent.target&&t.data(e.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(e)),this._mouseDelayTimer&&(clearTimeout(this._mouseDelayTimer),delete this._mouseDelayTimer),this.ignoreMissingWhich=!1,l=!1,e.preventDefault()},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),t.widget("ui.sortable",t.ui.mouse,{version:"1.12.1",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_isOverAxis:function(t,e,i){return t>=e&&e+i>t},_isFloating:function(t){return/left|right/.test(t.css("float"))||/inline|table-cell/.test(t.css("display"))},_create:function(){this.containerCache={},this._addClass("ui-sortable"),this.refresh(),this.offset=this.element.offset(),this._mouseInit(),this._setHandleClassName(),this.ready=!0},_setOption:function(t,e){this._super(t,e),"handle"===t&&this._setHandleClassName()},_setHandleClassName:function(){var e=this;this._removeClass(this.element.find(".ui-sortable-handle"),"ui-sortable-handle"),t.each(this.items,function(){e._addClass(this.instance.options.handle?this.item.find(this.instance.options.handle):this.item,"ui-sortable-handle")})},_destroy:function(){this._mouseDestroy();for(var t=this.items.length-1;t>=0;t--)this.items[t].item.removeData(this.widgetName+"-item");return this},_mouseCapture:function(e,i){var s=null,n=!1,o=this;return this.reverting?!1:this.options.disabled||"static"===this.options.type?!1:(this._refreshItems(e),t(e.target).parents().each(function(){return t.data(this,o.widgetName+"-item")===o?(s=t(this),!1):void 0}),t.data(e.target,o.widgetName+"-item")===o&&(s=t(e.target)),s?!this.options.handle||i||(t(this.options.handle,s).find("*").addBack().each(function(){this===e.target&&(n=!0)}),n)?(this.currentItem=s,this._removeCurrentsFromItems(),!0):!1:!1)},_mouseStart:function(e,i,s){var n,o,a=this.options;if(this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(e),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},t.extend(this.offset,{click:{left:e.pageX-this.offset.left,top:e.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),this.originalPosition=this._generatePosition(e),this.originalPageX=e.pageX,this.originalPageY=e.pageY,a.cursorAt&&this._adjustOffsetFromHelper(a.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!==this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),a.containment&&this._setContainment(),a.cursor&&"auto"!==a.cursor&&(o=this.document.find("body"),this.storedCursor=o.css("cursor"),o.css("cursor",a.cursor),this.storedStylesheet=t("<style>*{ cursor: "+a.cursor+" !important; }</style>").appendTo(o)),a.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",a.opacity)),a.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",a.zIndex)),this.scrollParent[0]!==this.document[0]&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",e,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions(),!s)for(n=this.containers.length-1;n>=0;n--)this.containers[n]._trigger("activate",e,this._uiHash(this));return t.ui.ddmanager&&(t.ui.ddmanager.current=this),t.ui.ddmanager&&!a.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this.dragging=!0,this._addClass(this.helper,"ui-sortable-helper"),this._mouseDrag(e),!0},_mouseDrag:function(e){var i,s,n,o,a=this.options,r=!1;for(this.position=this._generatePosition(e),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs),this.options.scroll&&(this.scrollParent[0]!==this.document[0]&&"HTML"!==this.scrollParent[0].tagName?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-e.pageY<a.scrollSensitivity?this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop+a.scrollSpeed:e.pageY-this.overflowOffset.top<a.scrollSensitivity&&(this.scrollParent[0].scrollTop=r=this.scrollParent[0].scrollTop-a.scrollSpeed),this.overflowOffset.left+this.scrollParent[0].offsetWidth-e.pageX<a.scrollSensitivity?this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft+a.scrollSpeed:e.pageX-this.overflowOffset.left<a.scrollSensitivity&&(this.scrollParent[0].scrollLeft=r=this.scrollParent[0].scrollLeft-a.scrollSpeed)):(e.pageY-this.document.scrollTop()<a.scrollSensitivity?r=this.document.scrollTop(this.document.scrollTop()-a.scrollSpeed):this.window.height()-(e.pageY-this.document.scrollTop())<a.scrollSensitivity&&(r=this.document.scrollTop(this.document.scrollTop()+a.scrollSpeed)),e.pageX-this.document.scrollLeft()<a.scrollSensitivity?r=this.document.scrollLeft(this.document.scrollLeft()-a.scrollSpeed):this.window.width()-(e.pageX-this.document.scrollLeft())<a.scrollSensitivity&&(r=this.document.scrollLeft(this.document.scrollLeft()+a.scrollSpeed))),r!==!1&&t.ui.ddmanager&&!a.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e)),this.positionAbs=this._convertPositionTo("absolute"),this.options.axis&&"y"===this.options.axis||(this.helper[0].style.left=this.position.left+"px"),this.options.axis&&"x"===this.options.axis||(this.helper[0].style.top=this.position.top+"px"),i=this.items.length-1;i>=0;i--)if(s=this.items[i],n=s.item[0],o=this._intersectsWithPointer(s),o&&s.instance===this.currentContainer&&n!==this.currentItem[0]&&this.placeholder[1===o?"next":"prev"]()[0]!==n&&!t.contains(this.placeholder[0],n)&&("semi-dynamic"===this.options.type?!t.contains(this.element[0],n):!0)){if(this.direction=1===o?"down":"up","pointer"!==this.options.tolerance&&!this._intersectsWithSides(s))break;this._rearrange(e,s),this._trigger("change",e,this._uiHash());break}return this._contactContainers(e),t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),this._trigger("sort",e,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(e,i){if(e){if(t.ui.ddmanager&&!this.options.dropBehaviour&&t.ui.ddmanager.drop(this,e),this.options.revert){var s=this,n=this.placeholder.offset(),o=this.options.axis,a={};o&&"x"!==o||(a.left=n.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]===this.document[0].body?0:this.offsetParent[0].scrollLeft)),o&&"y"!==o||(a.top=n.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]===this.document[0].body?0:this.offsetParent[0].scrollTop)),this.reverting=!0,t(this.helper).animate(a,parseInt(this.options.revert,10)||500,function(){s._clear(e)})}else this._clear(e,i);return!1}},cancel:function(){if(this.dragging){this._mouseUp(new t.Event("mouseup",{target:null})),"original"===this.options.helper?(this.currentItem.css(this._storedCSS),this._removeClass(this.currentItem,"ui-sortable-helper")):this.currentItem.show();for(var e=this.containers.length-1;e>=0;e--)this.containers[e]._trigger("deactivate",null,this._uiHash(this)),this.containers[e].containerCache.over&&(this.containers[e]._trigger("out",null,this._uiHash(this)),this.containers[e].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),"original"!==this.options.helper&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),t.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?t(this.domPosition.prev).after(this.currentItem):t(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},t(i).each(function(){var i=(t(e.item||this).attr(e.attribute||"id")||"").match(e.expression||/(.+)[\-=_](.+)/);i&&s.push((e.key||i[1]+"[]")+"="+(e.key&&e.expression?i[1]:i[2]))}),!s.length&&e.key&&s.push(e.key+"="),s.join("&")},toArray:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},i.each(function(){s.push(t(e.item||this).attr(e.attribute||"id")||"")}),s},_intersectsWith:function(t){var e=this.positionAbs.left,i=e+this.helperProportions.width,s=this.positionAbs.top,n=s+this.helperProportions.height,o=t.left,a=o+t.width,r=t.top,l=r+t.height,h=this.offset.click.top,c=this.offset.click.left,u="x"===this.options.axis||s+h>r&&l>s+h,d="y"===this.options.axis||e+c>o&&a>e+c,p=u&&d;return"pointer"===this.options.tolerance||this.options.forcePointerForContainers||"pointer"!==this.options.tolerance&&this.helperProportions[this.floating?"width":"height"]>t[this.floating?"width":"height"]?p:e+this.helperProportions.width/2>o&&a>i-this.helperProportions.width/2&&s+this.helperProportions.height/2>r&&l>n-this.helperProportions.height/2},_intersectsWithPointer:function(t){var e,i,s="x"===this.options.axis||this._isOverAxis(this.positionAbs.top+this.offset.click.top,t.top,t.height),n="y"===this.options.axis||this._isOverAxis(this.positionAbs.left+this.offset.click.left,t.left,t.width),o=s&&n;return o?(e=this._getDragVerticalDirection(),i=this._getDragHorizontalDirection(),this.floating?"right"===i||"down"===e?2:1:e&&("down"===e?2:1)):!1},_intersectsWithSides:function(t){var e=this._isOverAxis(this.positionAbs.top+this.offset.click.top,t.top+t.height/2,t.height),i=this._isOverAxis(this.positionAbs.left+this.offset.click.left,t.left+t.width/2,t.width),s=this._getDragVerticalDirection(),n=this._getDragHorizontalDirection();return this.floating&&n?"right"===n&&i||"left"===n&&!i:s&&("down"===s&&e||"up"===s&&!e)},_getDragVerticalDirection:function(){var t=this.positionAbs.top-this.lastPositionAbs.top;return 0!==t&&(t>0?"down":"up")},_getDragHorizontalDirection:function(){var t=this.positionAbs.left-this.lastPositionAbs.left;return 0!==t&&(t>0?"right":"left")},refresh:function(t){return this._refreshItems(t),this._setHandleClassName(),this.refreshPositions(),this},_connectWith:function(){var t=this.options;return t.connectWith.constructor===String?[t.connectWith]:t.connectWith},_getItemsAsjQuery:function(e){function i(){r.push(this)}var s,n,o,a,r=[],l=[],h=this._connectWith();if(h&&e)for(s=h.length-1;s>=0;s--)for(o=t(h[s],this.document[0]),n=o.length-1;n>=0;n--)a=t.data(o[n],this.widgetFullName),a&&a!==this&&!a.options.disabled&&l.push([t.isFunction(a.options.items)?a.options.items.call(a.element):t(a.options.items,a.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),a]);for(l.push([t.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):t(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]),s=l.length-1;s>=0;s--)l[s][0].each(i);return t(r)},_removeCurrentsFromItems:function(){var e=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=t.grep(this.items,function(t){for(var i=0;e.length>i;i++)if(e[i]===t.item[0])return!1;return!0})},_refreshItems:function(e){this.items=[],this.containers=[this];var i,s,n,o,a,r,l,h,c=this.items,u=[[t.isFunction(this.options.items)?this.options.items.call(this.element[0],e,{item:this.currentItem}):t(this.options.items,this.element),this]],d=this._connectWith();if(d&&this.ready)for(i=d.length-1;i>=0;i--)for(n=t(d[i],this.document[0]),s=n.length-1;s>=0;s--)o=t.data(n[s],this.widgetFullName),o&&o!==this&&!o.options.disabled&&(u.push([t.isFunction(o.options.items)?o.options.items.call(o.element[0],e,{item:this.currentItem}):t(o.options.items,o.element),o]),this.containers.push(o));for(i=u.length-1;i>=0;i--)for(a=u[i][1],r=u[i][0],s=0,h=r.length;h>s;s++)l=t(r[s]),l.data(this.widgetName+"-item",a),c.push({item:l,instance:a,width:0,height:0,left:0,top:0})},refreshPositions:function(e){this.floating=this.items.length?"x"===this.options.axis||this._isFloating(this.items[0].item):!1,this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());var i,s,n,o;for(i=this.items.length-1;i>=0;i--)s=this.items[i],s.instance!==this.currentContainer&&this.currentContainer&&s.item[0]!==this.currentItem[0]||(n=this.options.toleranceElement?t(this.options.toleranceElement,s.item):s.item,e||(s.width=n.outerWidth(),s.height=n.outerHeight()),o=n.offset(),s.left=o.left,s.top=o.top);if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(i=this.containers.length-1;i>=0;i--)o=this.containers[i].element.offset(),this.containers[i].containerCache.left=o.left,this.containers[i].containerCache.top=o.top,this.containers[i].containerCache.width=this.containers[i].element.outerWidth(),this.containers[i].containerCache.height=this.containers[i].element.outerHeight();return this},_createPlaceholder:function(e){e=e||this;var i,s=e.options;s.placeholder&&s.placeholder.constructor!==String||(i=s.placeholder,s.placeholder={element:function(){var s=e.currentItem[0].nodeName.toLowerCase(),n=t("<"+s+">",e.document[0]);return e._addClass(n,"ui-sortable-placeholder",i||e.currentItem[0].className)._removeClass(n,"ui-sortable-helper"),"tbody"===s?e._createTrPlaceholder(e.currentItem.find("tr").eq(0),t("<tr>",e.document[0]).appendTo(n)):"tr"===s?e._createTrPlaceholder(e.currentItem,n):"img"===s&&n.attr("src",e.currentItem.attr("src")),i||n.css("visibility","hidden"),n},update:function(t,n){(!i||s.forcePlaceholderSize)&&(n.height()||n.height(e.currentItem.innerHeight()-parseInt(e.currentItem.css("paddingTop")||0,10)-parseInt(e.currentItem.css("paddingBottom")||0,10)),n.width()||n.width(e.currentItem.innerWidth()-parseInt(e.currentItem.css("paddingLeft")||0,10)-parseInt(e.currentItem.css("paddingRight")||0,10)))}}),e.placeholder=t(s.placeholder.element.call(e.element,e.currentItem)),e.currentItem.after(e.placeholder),s.placeholder.update(e,e.placeholder)},_createTrPlaceholder:function(e,i){var s=this;e.children().each(function(){t("<td> </td>",s.document[0]).attr("colspan",t(this).attr("colspan")||1).appendTo(i)})},_contactContainers:function(e){var i,s,n,o,a,r,l,h,c,u,d=null,p=null;for(i=this.containers.length-1;i>=0;i--)if(!t.contains(this.currentItem[0],this.containers[i].element[0]))if(this._intersectsWith(this.containers[i].containerCache)){if(d&&t.contains(this.containers[i].element[0],d.element[0]))continue;d=this.containers[i],p=i}else this.containers[i].containerCache.over&&(this.containers[i]._trigger("out",e,this._uiHash(this)),this.containers[i].containerCache.over=0);if(d)if(1===this.containers.length)this.containers[p].containerCache.over||(this.containers[p]._trigger("over",e,this._uiHash(this)),this.containers[p].containerCache.over=1);else{for(n=1e4,o=null,c=d.floating||this._isFloating(this.currentItem),a=c?"left":"top",r=c?"width":"height",u=c?"pageX":"pageY",s=this.items.length-1;s>=0;s--)t.contains(this.containers[p].element[0],this.items[s].item[0])&&this.items[s].item[0]!==this.currentItem[0]&&(l=this.items[s].item.offset()[a],h=!1,e[u]-l>this.items[s][r]/2&&(h=!0),n>Math.abs(e[u]-l)&&(n=Math.abs(e[u]-l),o=this.items[s],this.direction=h?"up":"down"));if(!o&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[p])return this.currentContainer.containerCache.over||(this.containers[p]._trigger("over",e,this._uiHash()),this.currentContainer.containerCache.over=1),void 0;o?this._rearrange(e,o,null,!0):this._rearrange(e,null,this.containers[p].element,!0),this._trigger("change",e,this._uiHash()),this.containers[p]._trigger("change",e,this._uiHash(this)),this.currentContainer=this.containers[p],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[p]._trigger("over",e,this._uiHash(this)),this.containers[p].containerCache.over=1}},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper)?t(i.helper.apply(this.element[0],[e,this.currentItem])):"clone"===i.helper?this.currentItem.clone():this.currentItem;return s.parents("body").length||t("parent"!==i.appendTo?i.appendTo:this.currentItem[0].parentNode)[0].appendChild(s[0]),s[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!s[0].style.width||i.forceHelperSize)&&s.width(this.currentItem.width()),(!s[0].style.height||i.forceHelperSize)&&s.height(this.currentItem.height()),s},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top) +},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var e=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===this.document[0].body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&t.ui.ie)&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var t=this.currentItem.position();return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:t.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options;"parent"===n.containment&&(n.containment=this.helper[0].parentNode),("document"===n.containment||"window"===n.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,"document"===n.containment?this.document.width():this.window.width()-this.helperProportions.width-this.margins.left,("document"===n.containment?this.document.height()||document.body.parentNode.scrollHeight:this.window.height()||this.document[0].body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(n.containment)||(e=t(n.containment)[0],i=t(n.containment).offset(),s="hidden"!==t(e).css("overflow"),this.containment=[i.left+(parseInt(t(e).css("borderLeftWidth"),10)||0)+(parseInt(t(e).css("paddingLeft"),10)||0)-this.margins.left,i.top+(parseInt(t(e).css("borderTopWidth"),10)||0)+(parseInt(t(e).css("paddingTop"),10)||0)-this.margins.top,i.left+(s?Math.max(e.scrollWidth,e.offsetWidth):e.offsetWidth)-(parseInt(t(e).css("borderLeftWidth"),10)||0)-(parseInt(t(e).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,i.top+(s?Math.max(e.scrollHeight,e.offsetHeight):e.offsetHeight)-(parseInt(t(e).css("borderTopWidth"),10)||0)-(parseInt(t(e).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(e,i){i||(i=this.position);var s="absolute"===e?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,o=/(html|body)/i.test(n[0].tagName);return{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():o?0:n.scrollTop())*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():o?0:n.scrollLeft())*s}},_generatePosition:function(e){var i,s,n=this.options,o=e.pageX,a=e.pageY,r="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,l=/(html|body)/i.test(r[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(e.pageX-this.offset.click.left<this.containment[0]&&(o=this.containment[0]+this.offset.click.left),e.pageY-this.offset.click.top<this.containment[1]&&(a=this.containment[1]+this.offset.click.top),e.pageX-this.offset.click.left>this.containment[2]&&(o=this.containment[2]+this.offset.click.left),e.pageY-this.offset.click.top>this.containment[3]&&(a=this.containment[3]+this.offset.click.top)),n.grid&&(i=this.originalPageY+Math.round((a-this.originalPageY)/n.grid[1])*n.grid[1],a=this.containment?i-this.offset.click.top>=this.containment[1]&&i-this.offset.click.top<=this.containment[3]?i:i-this.offset.click.top>=this.containment[1]?i-n.grid[1]:i+n.grid[1]:i,s=this.originalPageX+Math.round((o-this.originalPageX)/n.grid[0])*n.grid[0],o=this.containment?s-this.offset.click.left>=this.containment[0]&&s-this.offset.click.left<=this.containment[2]?s:s-this.offset.click.left>=this.containment[0]?s-n.grid[0]:s+n.grid[0]:s)),{top:a-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():l?0:r.scrollTop()),left:o-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():l?0:r.scrollLeft())}},_rearrange:function(t,e,i,s){i?i[0].appendChild(this.placeholder[0]):e.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?e.item[0]:e.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter;this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(t,e){function i(t,e,i){return function(s){i._trigger(t,s,e._uiHash(e))}}this.reverting=!1;var s,n=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(s in this._storedCSS)("auto"===this._storedCSS[s]||"static"===this._storedCSS[s])&&(this._storedCSS[s]="");this.currentItem.css(this._storedCSS),this._removeClass(this.currentItem,"ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!e&&n.push(function(t){this._trigger("receive",t,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||e||n.push(function(t){this._trigger("update",t,this._uiHash())}),this!==this.currentContainer&&(e||(n.push(function(t){this._trigger("remove",t,this._uiHash())}),n.push(function(t){return function(e){t._trigger("receive",e,this._uiHash(this))}}.call(this,this.currentContainer)),n.push(function(t){return function(e){t._trigger("update",e,this._uiHash(this))}}.call(this,this.currentContainer)))),s=this.containers.length-1;s>=0;s--)e||n.push(i("deactivate",this,this.containers[s])),this.containers[s].containerCache.over&&(n.push(i("out",this,this.containers[s])),this.containers[s].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,e||this._trigger("beforeStop",t,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.cancelHelperRemoval||(this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null),!e){for(s=0;n.length>s;s++)n[s].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!this.cancelHelperRemoval},_trigger:function(){t.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(e){var i=e||this;return{helper:i.helper,placeholder:i.placeholder||t([]),position:i.position,originalPosition:i.originalPosition,offset:i.positionAbs,item:i.currentItem,sender:e?e.element:null}}}),t.extend(t.ui,{datepicker:{version:"1.12.1"}});var h;t.extend(i.prototype,{markerClassName:"hasDatepicker",maxRows:4,_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(t){return o(this._defaults,t||{}),this},_attachDatepicker:function(e,i){var s,n,o;s=e.nodeName.toLowerCase(),n="div"===s||"span"===s,e.id||(this.uuid+=1,e.id="dp"+this.uuid),o=this._newInst(t(e),n),o.settings=t.extend({},i||{}),"input"===s?this._connectDatepicker(e,o):n&&this._inlineDatepicker(e,o)},_newInst:function(e,i){var n=e[0].id.replace(/([^A-Za-z0-9_\-])/g,"\\\\$1");return{id:n,input:e,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:i,dpDiv:i?s(t("<div class='"+this._inlineClass+" ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")):this.dpDiv}},_connectDatepicker:function(e,i){var s=t(e);i.append=t([]),i.trigger=t([]),s.hasClass(this.markerClassName)||(this._attachments(s,i),s.addClass(this.markerClassName).on("keydown",this._doKeyDown).on("keypress",this._doKeyPress).on("keyup",this._doKeyUp),this._autoSize(i),t.data(e,"datepicker",i),i.settings.disabled&&this._disableDatepicker(e))},_attachments:function(e,i){var s,n,o,a=this._get(i,"appendText"),r=this._get(i,"isRTL");i.append&&i.append.remove(),a&&(i.append=t("<span class='"+this._appendClass+"'>"+a+"</span>"),e[r?"before":"after"](i.append)),e.off("focus",this._showDatepicker),i.trigger&&i.trigger.remove(),s=this._get(i,"showOn"),("focus"===s||"both"===s)&&e.on("focus",this._showDatepicker),("button"===s||"both"===s)&&(n=this._get(i,"buttonText"),o=this._get(i,"buttonImage"),i.trigger=t(this._get(i,"buttonImageOnly")?t("<img/>").addClass(this._triggerClass).attr({src:o,alt:n,title:n}):t("<button type='button'></button>").addClass(this._triggerClass).html(o?t("<img/>").attr({src:o,alt:n,title:n}):n)),e[r?"before":"after"](i.trigger),i.trigger.on("click",function(){return t.datepicker._datepickerShowing&&t.datepicker._lastInput===e[0]?t.datepicker._hideDatepicker():t.datepicker._datepickerShowing&&t.datepicker._lastInput!==e[0]?(t.datepicker._hideDatepicker(),t.datepicker._showDatepicker(e[0])):t.datepicker._showDatepicker(e[0]),!1}))},_autoSize:function(t){if(this._get(t,"autoSize")&&!t.inline){var e,i,s,n,o=new Date(2009,11,20),a=this._get(t,"dateFormat");a.match(/[DM]/)&&(e=function(t){for(i=0,s=0,n=0;t.length>n;n++)t[n].length>i&&(i=t[n].length,s=n);return s},o.setMonth(e(this._get(t,a.match(/MM/)?"monthNames":"monthNamesShort"))),o.setDate(e(this._get(t,a.match(/DD/)?"dayNames":"dayNamesShort"))+20-o.getDay())),t.input.attr("size",this._formatDate(t,o).length)}},_inlineDatepicker:function(e,i){var s=t(e);s.hasClass(this.markerClassName)||(s.addClass(this.markerClassName).append(i.dpDiv),t.data(e,"datepicker",i),this._setDate(i,this._getDefaultDate(i),!0),this._updateDatepicker(i),this._updateAlternate(i),i.settings.disabled&&this._disableDatepicker(e),i.dpDiv.css("display","block"))},_dialogDatepicker:function(e,i,s,n,a){var r,l,h,c,u,d=this._dialogInst;return d||(this.uuid+=1,r="dp"+this.uuid,this._dialogInput=t("<input type='text' id='"+r+"' style='position: absolute; top: -100px; width: 0px;'/>"),this._dialogInput.on("keydown",this._doKeyDown),t("body").append(this._dialogInput),d=this._dialogInst=this._newInst(this._dialogInput,!1),d.settings={},t.data(this._dialogInput[0],"datepicker",d)),o(d.settings,n||{}),i=i&&i.constructor===Date?this._formatDate(d,i):i,this._dialogInput.val(i),this._pos=a?a.length?a:[a.pageX,a.pageY]:null,this._pos||(l=document.documentElement.clientWidth,h=document.documentElement.clientHeight,c=document.documentElement.scrollLeft||document.body.scrollLeft,u=document.documentElement.scrollTop||document.body.scrollTop,this._pos=[l/2-100+c,h/2-150+u]),this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px"),d.settings.onSelect=s,this._inDialog=!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialogInput[0]),t.blockUI&&t.blockUI(this.dpDiv),t.data(this._dialogInput[0],"datepicker",d),this},_destroyDatepicker:function(e){var i,s=t(e),n=t.data(e,"datepicker");s.hasClass(this.markerClassName)&&(i=e.nodeName.toLowerCase(),t.removeData(e,"datepicker"),"input"===i?(n.append.remove(),n.trigger.remove(),s.removeClass(this.markerClassName).off("focus",this._showDatepicker).off("keydown",this._doKeyDown).off("keypress",this._doKeyPress).off("keyup",this._doKeyUp)):("div"===i||"span"===i)&&s.removeClass(this.markerClassName).empty(),h===n&&(h=null))},_enableDatepicker:function(e){var i,s,n=t(e),o=t.data(e,"datepicker");n.hasClass(this.markerClassName)&&(i=e.nodeName.toLowerCase(),"input"===i?(e.disabled=!1,o.trigger.filter("button").each(function(){this.disabled=!1}).end().filter("img").css({opacity:"1.0",cursor:""})):("div"===i||"span"===i)&&(s=n.children("."+this._inlineClass),s.children().removeClass("ui-state-disabled"),s.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!1)),this._disabledInputs=t.map(this._disabledInputs,function(t){return t===e?null:t}))},_disableDatepicker:function(e){var i,s,n=t(e),o=t.data(e,"datepicker");n.hasClass(this.markerClassName)&&(i=e.nodeName.toLowerCase(),"input"===i?(e.disabled=!0,o.trigger.filter("button").each(function(){this.disabled=!0}).end().filter("img").css({opacity:"0.5",cursor:"default"})):("div"===i||"span"===i)&&(s=n.children("."+this._inlineClass),s.children().addClass("ui-state-disabled"),s.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!0)),this._disabledInputs=t.map(this._disabledInputs,function(t){return t===e?null:t}),this._disabledInputs[this._disabledInputs.length]=e)},_isDisabledDatepicker:function(t){if(!t)return!1;for(var e=0;this._disabledInputs.length>e;e++)if(this._disabledInputs[e]===t)return!0;return!1},_getInst:function(e){try{return t.data(e,"datepicker")}catch(i){throw"Missing instance data for this datepicker"}},_optionDatepicker:function(e,i,s){var n,a,r,l,h=this._getInst(e);return 2===arguments.length&&"string"==typeof i?"defaults"===i?t.extend({},t.datepicker._defaults):h?"all"===i?t.extend({},h.settings):this._get(h,i):null:(n=i||{},"string"==typeof i&&(n={},n[i]=s),h&&(this._curInst===h&&this._hideDatepicker(),a=this._getDateDatepicker(e,!0),r=this._getMinMaxDate(h,"min"),l=this._getMinMaxDate(h,"max"),o(h.settings,n),null!==r&&void 0!==n.dateFormat&&void 0===n.minDate&&(h.settings.minDate=this._formatDate(h,r)),null!==l&&void 0!==n.dateFormat&&void 0===n.maxDate&&(h.settings.maxDate=this._formatDate(h,l)),"disabled"in n&&(n.disabled?this._disableDatepicker(e):this._enableDatepicker(e)),this._attachments(t(e),h),this._autoSize(h),this._setDate(h,a),this._updateAlternate(h),this._updateDatepicker(h)),void 0)},_changeDatepicker:function(t,e,i){this._optionDatepicker(t,e,i)},_refreshDatepicker:function(t){var e=this._getInst(t);e&&this._updateDatepicker(e)},_setDateDatepicker:function(t,e){var i=this._getInst(t);i&&(this._setDate(i,e),this._updateDatepicker(i),this._updateAlternate(i))},_getDateDatepicker:function(t,e){var i=this._getInst(t);return i&&!i.inline&&this._setDateFromField(i,e),i?this._getDate(i):null},_doKeyDown:function(e){var i,s,n,o=t.datepicker._getInst(e.target),a=!0,r=o.dpDiv.is(".ui-datepicker-rtl");if(o._keyEvent=!0,t.datepicker._datepickerShowing)switch(e.keyCode){case 9:t.datepicker._hideDatepicker(),a=!1;break;case 13:return n=t("td."+t.datepicker._dayOverClass+":not(."+t.datepicker._currentClass+")",o.dpDiv),n[0]&&t.datepicker._selectDay(e.target,o.selectedMonth,o.selectedYear,n[0]),i=t.datepicker._get(o,"onSelect"),i?(s=t.datepicker._formatDate(o),i.apply(o.input?o.input[0]:null,[s,o])):t.datepicker._hideDatepicker(),!1;case 27:t.datepicker._hideDatepicker();break;case 33:t.datepicker._adjustDate(e.target,e.ctrlKey?-t.datepicker._get(o,"stepBigMonths"):-t.datepicker._get(o,"stepMonths"),"M");break;case 34:t.datepicker._adjustDate(e.target,e.ctrlKey?+t.datepicker._get(o,"stepBigMonths"):+t.datepicker._get(o,"stepMonths"),"M");break;case 35:(e.ctrlKey||e.metaKey)&&t.datepicker._clearDate(e.target),a=e.ctrlKey||e.metaKey;break;case 36:(e.ctrlKey||e.metaKey)&&t.datepicker._gotoToday(e.target),a=e.ctrlKey||e.metaKey;break;case 37:(e.ctrlKey||e.metaKey)&&t.datepicker._adjustDate(e.target,r?1:-1,"D"),a=e.ctrlKey||e.metaKey,e.originalEvent.altKey&&t.datepicker._adjustDate(e.target,e.ctrlKey?-t.datepicker._get(o,"stepBigMonths"):-t.datepicker._get(o,"stepMonths"),"M");break;case 38:(e.ctrlKey||e.metaKey)&&t.datepicker._adjustDate(e.target,-7,"D"),a=e.ctrlKey||e.metaKey;break;case 39:(e.ctrlKey||e.metaKey)&&t.datepicker._adjustDate(e.target,r?-1:1,"D"),a=e.ctrlKey||e.metaKey,e.originalEvent.altKey&&t.datepicker._adjustDate(e.target,e.ctrlKey?+t.datepicker._get(o,"stepBigMonths"):+t.datepicker._get(o,"stepMonths"),"M");break;case 40:(e.ctrlKey||e.metaKey)&&t.datepicker._adjustDate(e.target,7,"D"),a=e.ctrlKey||e.metaKey;break;default:a=!1}else 36===e.keyCode&&e.ctrlKey?t.datepicker._showDatepicker(this):a=!1;a&&(e.preventDefault(),e.stopPropagation())},_doKeyPress:function(e){var i,s,n=t.datepicker._getInst(e.target);return t.datepicker._get(n,"constrainInput")?(i=t.datepicker._possibleChars(t.datepicker._get(n,"dateFormat")),s=String.fromCharCode(null==e.charCode?e.keyCode:e.charCode),e.ctrlKey||e.metaKey||" ">s||!i||i.indexOf(s)>-1):void 0},_doKeyUp:function(e){var i,s=t.datepicker._getInst(e.target);if(s.input.val()!==s.lastVal)try{i=t.datepicker.parseDate(t.datepicker._get(s,"dateFormat"),s.input?s.input.val():null,t.datepicker._getFormatConfig(s)),i&&(t.datepicker._setDateFromField(s),t.datepicker._updateAlternate(s),t.datepicker._updateDatepicker(s))}catch(n){}return!0},_showDatepicker:function(i){if(i=i.target||i,"input"!==i.nodeName.toLowerCase()&&(i=t("input",i.parentNode)[0]),!t.datepicker._isDisabledDatepicker(i)&&t.datepicker._lastInput!==i){var s,n,a,r,l,h,c;s=t.datepicker._getInst(i),t.datepicker._curInst&&t.datepicker._curInst!==s&&(t.datepicker._curInst.dpDiv.stop(!0,!0),s&&t.datepicker._datepickerShowing&&t.datepicker._hideDatepicker(t.datepicker._curInst.input[0])),n=t.datepicker._get(s,"beforeShow"),a=n?n.apply(i,[i,s]):{},a!==!1&&(o(s.settings,a),s.lastVal=null,t.datepicker._lastInput=i,t.datepicker._setDateFromField(s),t.datepicker._inDialog&&(i.value=""),t.datepicker._pos||(t.datepicker._pos=t.datepicker._findPos(i),t.datepicker._pos[1]+=i.offsetHeight),r=!1,t(i).parents().each(function(){return r|="fixed"===t(this).css("position"),!r}),l={left:t.datepicker._pos[0],top:t.datepicker._pos[1]},t.datepicker._pos=null,s.dpDiv.empty(),s.dpDiv.css({position:"absolute",display:"block",top:"-1000px"}),t.datepicker._updateDatepicker(s),l=t.datepicker._checkOffset(s,l,r),s.dpDiv.css({position:t.datepicker._inDialog&&t.blockUI?"static":r?"fixed":"absolute",display:"none",left:l.left+"px",top:l.top+"px"}),s.inline||(h=t.datepicker._get(s,"showAnim"),c=t.datepicker._get(s,"duration"),s.dpDiv.css("z-index",e(t(i))+1),t.datepicker._datepickerShowing=!0,t.effects&&t.effects.effect[h]?s.dpDiv.show(h,t.datepicker._get(s,"showOptions"),c):s.dpDiv[h||"show"](h?c:null),t.datepicker._shouldFocusInput(s)&&s.input.trigger("focus"),t.datepicker._curInst=s))}},_updateDatepicker:function(e){this.maxRows=4,h=e,e.dpDiv.empty().append(this._generateHTML(e)),this._attachHandlers(e);var i,s=this._getNumberOfMonths(e),o=s[1],a=17,r=e.dpDiv.find("."+this._dayOverClass+" a");r.length>0&&n.apply(r.get(0)),e.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""),o>1&&e.dpDiv.addClass("ui-datepicker-multi-"+o).css("width",a*o+"em"),e.dpDiv[(1!==s[0]||1!==s[1]?"add":"remove")+"Class"]("ui-datepicker-multi"),e.dpDiv[(this._get(e,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"),e===t.datepicker._curInst&&t.datepicker._datepickerShowing&&t.datepicker._shouldFocusInput(e)&&e.input.trigger("focus"),e.yearshtml&&(i=e.yearshtml,setTimeout(function(){i===e.yearshtml&&e.yearshtml&&e.dpDiv.find("select.ui-datepicker-year:first").replaceWith(e.yearshtml),i=e.yearshtml=null},0))},_shouldFocusInput:function(t){return t.input&&t.input.is(":visible")&&!t.input.is(":disabled")&&!t.input.is(":focus")},_checkOffset:function(e,i,s){var n=e.dpDiv.outerWidth(),o=e.dpDiv.outerHeight(),a=e.input?e.input.outerWidth():0,r=e.input?e.input.outerHeight():0,l=document.documentElement.clientWidth+(s?0:t(document).scrollLeft()),h=document.documentElement.clientHeight+(s?0:t(document).scrollTop());return i.left-=this._get(e,"isRTL")?n-a:0,i.left-=s&&i.left===e.input.offset().left?t(document).scrollLeft():0,i.top-=s&&i.top===e.input.offset().top+r?t(document).scrollTop():0,i.left-=Math.min(i.left,i.left+n>l&&l>n?Math.abs(i.left+n-l):0),i.top-=Math.min(i.top,i.top+o>h&&h>o?Math.abs(o+r):0),i},_findPos:function(e){for(var i,s=this._getInst(e),n=this._get(s,"isRTL");e&&("hidden"===e.type||1!==e.nodeType||t.expr.filters.hidden(e));)e=e[n?"previousSibling":"nextSibling"];return i=t(e).offset(),[i.left,i.top]},_hideDatepicker:function(e){var i,s,n,o,a=this._curInst;!a||e&&a!==t.data(e,"datepicker")||this._datepickerShowing&&(i=this._get(a,"showAnim"),s=this._get(a,"duration"),n=function(){t.datepicker._tidyDialog(a)},t.effects&&(t.effects.effect[i]||t.effects[i])?a.dpDiv.hide(i,t.datepicker._get(a,"showOptions"),s,n):a.dpDiv["slideDown"===i?"slideUp":"fadeIn"===i?"fadeOut":"hide"](i?s:null,n),i||n(),this._datepickerShowing=!1,o=this._get(a,"onClose"),o&&o.apply(a.input?a.input[0]:null,[a.input?a.input.val():"",a]),this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),t.blockUI&&(t.unblockUI(),t("body").append(this.dpDiv))),this._inDialog=!1)},_tidyDialog:function(t){t.dpDiv.removeClass(this._dialogClass).off(".ui-datepicker-calendar")},_checkExternalClick:function(e){if(t.datepicker._curInst){var i=t(e.target),s=t.datepicker._getInst(i[0]);(i[0].id!==t.datepicker._mainDivId&&0===i.parents("#"+t.datepicker._mainDivId).length&&!i.hasClass(t.datepicker.markerClassName)&&!i.closest("."+t.datepicker._triggerClass).length&&t.datepicker._datepickerShowing&&(!t.datepicker._inDialog||!t.blockUI)||i.hasClass(t.datepicker.markerClassName)&&t.datepicker._curInst!==s)&&t.datepicker._hideDatepicker()}},_adjustDate:function(e,i,s){var n=t(e),o=this._getInst(n[0]);this._isDisabledDatepicker(n[0])||(this._adjustInstDate(o,i+("M"===s?this._get(o,"showCurrentAtPos"):0),s),this._updateDatepicker(o))},_gotoToday:function(e){var i,s=t(e),n=this._getInst(s[0]);this._get(n,"gotoCurrent")&&n.currentDay?(n.selectedDay=n.currentDay,n.drawMonth=n.selectedMonth=n.currentMonth,n.drawYear=n.selectedYear=n.currentYear):(i=new Date,n.selectedDay=i.getDate(),n.drawMonth=n.selectedMonth=i.getMonth(),n.drawYear=n.selectedYear=i.getFullYear()),this._notifyChange(n),this._adjustDate(s)},_selectMonthYear:function(e,i,s){var n=t(e),o=this._getInst(n[0]);o["selected"+("M"===s?"Month":"Year")]=o["draw"+("M"===s?"Month":"Year")]=parseInt(i.options[i.selectedIndex].value,10),this._notifyChange(o),this._adjustDate(n)},_selectDay:function(e,i,s,n){var o,a=t(e);t(n).hasClass(this._unselectableClass)||this._isDisabledDatepicker(a[0])||(o=this._getInst(a[0]),o.selectedDay=o.currentDay=t("a",n).html(),o.selectedMonth=o.currentMonth=i,o.selectedYear=o.currentYear=s,this._selectDate(e,this._formatDate(o,o.currentDay,o.currentMonth,o.currentYear)))},_clearDate:function(e){var i=t(e);this._selectDate(i,"")},_selectDate:function(e,i){var s,n=t(e),o=this._getInst(n[0]);i=null!=i?i:this._formatDate(o),o.input&&o.input.val(i),this._updateAlternate(o),s=this._get(o,"onSelect"),s?s.apply(o.input?o.input[0]:null,[i,o]):o.input&&o.input.trigger("change"),o.inline?this._updateDatepicker(o):(this._hideDatepicker(),this._lastInput=o.input[0],"object"!=typeof o.input[0]&&o.input.trigger("focus"),this._lastInput=null)},_updateAlternate:function(e){var i,s,n,o=this._get(e,"altField");o&&(i=this._get(e,"altFormat")||this._get(e,"dateFormat"),s=this._getDate(e),n=this.formatDate(i,s,this._getFormatConfig(e)),t(o).val(n))},noWeekends:function(t){var e=t.getDay();return[e>0&&6>e,""]},iso8601Week:function(t){var e,i=new Date(t.getTime());return i.setDate(i.getDate()+4-(i.getDay()||7)),e=i.getTime(),i.setMonth(0),i.setDate(1),Math.floor(Math.round((e-i)/864e5)/7)+1},parseDate:function(e,i,s){if(null==e||null==i)throw"Invalid arguments";if(i="object"==typeof i?""+i:i+"",""===i)return null;var n,o,a,r,l=0,h=(s?s.shortYearCutoff:null)||this._defaults.shortYearCutoff,c="string"!=typeof h?h:(new Date).getFullYear()%100+parseInt(h,10),u=(s?s.dayNamesShort:null)||this._defaults.dayNamesShort,d=(s?s.dayNames:null)||this._defaults.dayNames,p=(s?s.monthNamesShort:null)||this._defaults.monthNamesShort,f=(s?s.monthNames:null)||this._defaults.monthNames,g=-1,m=-1,_=-1,v=-1,b=!1,y=function(t){var i=e.length>n+1&&e.charAt(n+1)===t;return i&&n++,i},w=function(t){var e=y(t),s="@"===t?14:"!"===t?20:"y"===t&&e?4:"o"===t?3:2,n="y"===t?s:1,o=RegExp("^\\d{"+n+","+s+"}"),a=i.substring(l).match(o);if(!a)throw"Missing number at position "+l;return l+=a[0].length,parseInt(a[0],10)},k=function(e,s,n){var o=-1,a=t.map(y(e)?n:s,function(t,e){return[[e,t]]}).sort(function(t,e){return-(t[1].length-e[1].length)});if(t.each(a,function(t,e){var s=e[1];return i.substr(l,s.length).toLowerCase()===s.toLowerCase()?(o=e[0],l+=s.length,!1):void 0}),-1!==o)return o+1;throw"Unknown name at position "+l},x=function(){if(i.charAt(l)!==e.charAt(n))throw"Unexpected literal at position "+l;l++};for(n=0;e.length>n;n++)if(b)"'"!==e.charAt(n)||y("'")?x():b=!1;else switch(e.charAt(n)){case"d":_=w("d");break;case"D":k("D",u,d);break;case"o":v=w("o");break;case"m":m=w("m");break;case"M":m=k("M",p,f);break;case"y":g=w("y");break;case"@":r=new Date(w("@")),g=r.getFullYear(),m=r.getMonth()+1,_=r.getDate();break;case"!":r=new Date((w("!")-this._ticksTo1970)/1e4),g=r.getFullYear(),m=r.getMonth()+1,_=r.getDate();break;case"'":y("'")?x():b=!0;break;default:x()}if(i.length>l&&(a=i.substr(l),!/^\s+/.test(a)))throw"Extra/unparsed characters found in date: "+a;if(-1===g?g=(new Date).getFullYear():100>g&&(g+=(new Date).getFullYear()-(new Date).getFullYear()%100+(c>=g?0:-100)),v>-1)for(m=1,_=v;;){if(o=this._getDaysInMonth(g,m-1),o>=_)break;m++,_-=o}if(r=this._daylightSavingAdjust(new Date(g,m-1,_)),r.getFullYear()!==g||r.getMonth()+1!==m||r.getDate()!==_)throw"Invalid date";return r},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:1e7*60*60*24*(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925)),formatDate:function(t,e,i){if(!e)return"";var s,n=(i?i.dayNamesShort:null)||this._defaults.dayNamesShort,o=(i?i.dayNames:null)||this._defaults.dayNames,a=(i?i.monthNamesShort:null)||this._defaults.monthNamesShort,r=(i?i.monthNames:null)||this._defaults.monthNames,l=function(e){var i=t.length>s+1&&t.charAt(s+1)===e;return i&&s++,i},h=function(t,e,i){var s=""+e;if(l(t))for(;i>s.length;)s="0"+s;return s},c=function(t,e,i,s){return l(t)?s[e]:i[e]},u="",d=!1;if(e)for(s=0;t.length>s;s++)if(d)"'"!==t.charAt(s)||l("'")?u+=t.charAt(s):d=!1;else switch(t.charAt(s)){case"d":u+=h("d",e.getDate(),2);break;case"D":u+=c("D",e.getDay(),n,o);break;case"o":u+=h("o",Math.round((new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime()-new Date(e.getFullYear(),0,0).getTime())/864e5),3);break;case"m":u+=h("m",e.getMonth()+1,2);break;case"M":u+=c("M",e.getMonth(),a,r);break;case"y":u+=l("y")?e.getFullYear():(10>e.getFullYear()%100?"0":"")+e.getFullYear()%100;break;case"@":u+=e.getTime();break;case"!":u+=1e4*e.getTime()+this._ticksTo1970;break;case"'":l("'")?u+="'":d=!0;break;default:u+=t.charAt(s)}return u},_possibleChars:function(t){var e,i="",s=!1,n=function(i){var s=t.length>e+1&&t.charAt(e+1)===i;return s&&e++,s};for(e=0;t.length>e;e++)if(s)"'"!==t.charAt(e)||n("'")?i+=t.charAt(e):s=!1;else switch(t.charAt(e)){case"d":case"m":case"y":case"@":i+="0123456789";break;case"D":case"M":return null;case"'":n("'")?i+="'":s=!0;break;default:i+=t.charAt(e)}return i},_get:function(t,e){return void 0!==t.settings[e]?t.settings[e]:this._defaults[e]},_setDateFromField:function(t,e){if(t.input.val()!==t.lastVal){var i=this._get(t,"dateFormat"),s=t.lastVal=t.input?t.input.val():null,n=this._getDefaultDate(t),o=n,a=this._getFormatConfig(t);try{o=this.parseDate(i,s,a)||n}catch(r){s=e?"":s}t.selectedDay=o.getDate(),t.drawMonth=t.selectedMonth=o.getMonth(),t.drawYear=t.selectedYear=o.getFullYear(),t.currentDay=s?o.getDate():0,t.currentMonth=s?o.getMonth():0,t.currentYear=s?o.getFullYear():0,this._adjustInstDate(t)}},_getDefaultDate:function(t){return this._restrictMinMax(t,this._determineDate(t,this._get(t,"defaultDate"),new Date))},_determineDate:function(e,i,s){var n=function(t){var e=new Date;return e.setDate(e.getDate()+t),e},o=function(i){try{return t.datepicker.parseDate(t.datepicker._get(e,"dateFormat"),i,t.datepicker._getFormatConfig(e))}catch(s){}for(var n=(i.toLowerCase().match(/^c/)?t.datepicker._getDate(e):null)||new Date,o=n.getFullYear(),a=n.getMonth(),r=n.getDate(),l=/([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,h=l.exec(i);h;){switch(h[2]||"d"){case"d":case"D":r+=parseInt(h[1],10);break;case"w":case"W":r+=7*parseInt(h[1],10);break;case"m":case"M":a+=parseInt(h[1],10),r=Math.min(r,t.datepicker._getDaysInMonth(o,a));break;case"y":case"Y":o+=parseInt(h[1],10),r=Math.min(r,t.datepicker._getDaysInMonth(o,a))}h=l.exec(i)}return new Date(o,a,r)},a=null==i||""===i?s:"string"==typeof i?o(i):"number"==typeof i?isNaN(i)?s:n(i):new Date(i.getTime());return a=a&&"Invalid Date"==""+a?s:a,a&&(a.setHours(0),a.setMinutes(0),a.setSeconds(0),a.setMilliseconds(0)),this._daylightSavingAdjust(a)},_daylightSavingAdjust:function(t){return t?(t.setHours(t.getHours()>12?t.getHours()+2:0),t):null},_setDate:function(t,e,i){var s=!e,n=t.selectedMonth,o=t.selectedYear,a=this._restrictMinMax(t,this._determineDate(t,e,new Date));t.selectedDay=t.currentDay=a.getDate(),t.drawMonth=t.selectedMonth=t.currentMonth=a.getMonth(),t.drawYear=t.selectedYear=t.currentYear=a.getFullYear(),n===t.selectedMonth&&o===t.selectedYear||i||this._notifyChange(t),this._adjustInstDate(t),t.input&&t.input.val(s?"":this._formatDate(t))},_getDate:function(t){var e=!t.currentYear||t.input&&""===t.input.val()?null:this._daylightSavingAdjust(new Date(t.currentYear,t.currentMonth,t.currentDay));return e},_attachHandlers:function(e){var i=this._get(e,"stepMonths"),s="#"+e.id.replace(/\\\\/g,"\\");e.dpDiv.find("[data-handler]").map(function(){var e={prev:function(){t.datepicker._adjustDate(s,-i,"M")},next:function(){t.datepicker._adjustDate(s,+i,"M")},hide:function(){t.datepicker._hideDatepicker()},today:function(){t.datepicker._gotoToday(s)},selectDay:function(){return t.datepicker._selectDay(s,+this.getAttribute("data-month"),+this.getAttribute("data-year"),this),!1},selectMonth:function(){return t.datepicker._selectMonthYear(s,this,"M"),!1},selectYear:function(){return t.datepicker._selectMonthYear(s,this,"Y"),!1}};t(this).on(this.getAttribute("data-event"),e[this.getAttribute("data-handler")])})},_generateHTML:function(t){var e,i,s,n,o,a,r,l,h,c,u,d,p,f,g,m,_,v,b,y,w,k,x,C,D,T,I,M,P,S,N,H,A,z,O,E,W,F,L,R=new Date,Y=this._daylightSavingAdjust(new Date(R.getFullYear(),R.getMonth(),R.getDate())),B=this._get(t,"isRTL"),j=this._get(t,"showButtonPanel"),q=this._get(t,"hideIfNoPrevNext"),K=this._get(t,"navigationAsDateFormat"),U=this._getNumberOfMonths(t),V=this._get(t,"showCurrentAtPos"),X=this._get(t,"stepMonths"),$=1!==U[0]||1!==U[1],G=this._daylightSavingAdjust(t.currentDay?new Date(t.currentYear,t.currentMonth,t.currentDay):new Date(9999,9,9)),J=this._getMinMaxDate(t,"min"),Q=this._getMinMaxDate(t,"max"),Z=t.drawMonth-V,te=t.drawYear;if(0>Z&&(Z+=12,te--),Q)for(e=this._daylightSavingAdjust(new Date(Q.getFullYear(),Q.getMonth()-U[0]*U[1]+1,Q.getDate())),e=J&&J>e?J:e;this._daylightSavingAdjust(new Date(te,Z,1))>e;)Z--,0>Z&&(Z=11,te--);for(t.drawMonth=Z,t.drawYear=te,i=this._get(t,"prevText"),i=K?this.formatDate(i,this._daylightSavingAdjust(new Date(te,Z-X,1)),this._getFormatConfig(t)):i,s=this._canAdjustMonth(t,-1,te,Z)?"<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click' title='"+i+"'><span class='ui-icon ui-icon-circle-triangle-"+(B?"e":"w")+"'>"+i+"</span></a>":q?"":"<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='"+i+"'><span class='ui-icon ui-icon-circle-triangle-"+(B?"e":"w")+"'>"+i+"</span></a>",n=this._get(t,"nextText"),n=K?this.formatDate(n,this._daylightSavingAdjust(new Date(te,Z+X,1)),this._getFormatConfig(t)):n,o=this._canAdjustMonth(t,1,te,Z)?"<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click' title='"+n+"'><span class='ui-icon ui-icon-circle-triangle-"+(B?"w":"e")+"'>"+n+"</span></a>":q?"":"<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='"+n+"'><span class='ui-icon ui-icon-circle-triangle-"+(B?"w":"e")+"'>"+n+"</span></a>",a=this._get(t,"currentText"),r=this._get(t,"gotoCurrent")&&t.currentDay?G:Y,a=K?this.formatDate(a,r,this._getFormatConfig(t)):a,l=t.inline?"":"<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>"+this._get(t,"closeText")+"</button>",h=j?"<div class='ui-datepicker-buttonpane ui-widget-content'>"+(B?l:"")+(this._isInRange(t,r)?"<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'>"+a+"</button>":"")+(B?"":l)+"</div>":"",c=parseInt(this._get(t,"firstDay"),10),c=isNaN(c)?0:c,u=this._get(t,"showWeek"),d=this._get(t,"dayNames"),p=this._get(t,"dayNamesMin"),f=this._get(t,"monthNames"),g=this._get(t,"monthNamesShort"),m=this._get(t,"beforeShowDay"),_=this._get(t,"showOtherMonths"),v=this._get(t,"selectOtherMonths"),b=this._getDefaultDate(t),y="",k=0;U[0]>k;k++){for(x="",this.maxRows=4,C=0;U[1]>C;C++){if(D=this._daylightSavingAdjust(new Date(te,Z,t.selectedDay)),T=" ui-corner-all",I="",$){if(I+="<div class='ui-datepicker-group",U[1]>1)switch(C){case 0:I+=" ui-datepicker-group-first",T=" ui-corner-"+(B?"right":"left"); +break;case U[1]-1:I+=" ui-datepicker-group-last",T=" ui-corner-"+(B?"left":"right");break;default:I+=" ui-datepicker-group-middle",T=""}I+="'>"}for(I+="<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix"+T+"'>"+(/all|left/.test(T)&&0===k?B?o:s:"")+(/all|right/.test(T)&&0===k?B?s:o:"")+this._generateMonthYearHeader(t,Z,te,J,Q,k>0||C>0,f,g)+"</div><table class='ui-datepicker-calendar'><thead>"+"<tr>",M=u?"<th class='ui-datepicker-week-col'>"+this._get(t,"weekHeader")+"</th>":"",w=0;7>w;w++)P=(w+c)%7,M+="<th scope='col'"+((w+c+6)%7>=5?" class='ui-datepicker-week-end'":"")+">"+"<span title='"+d[P]+"'>"+p[P]+"</span></th>";for(I+=M+"</tr></thead><tbody>",S=this._getDaysInMonth(te,Z),te===t.selectedYear&&Z===t.selectedMonth&&(t.selectedDay=Math.min(t.selectedDay,S)),N=(this._getFirstDayOfMonth(te,Z)-c+7)%7,H=Math.ceil((N+S)/7),A=$?this.maxRows>H?this.maxRows:H:H,this.maxRows=A,z=this._daylightSavingAdjust(new Date(te,Z,1-N)),O=0;A>O;O++){for(I+="<tr>",E=u?"<td class='ui-datepicker-week-col'>"+this._get(t,"calculateWeek")(z)+"</td>":"",w=0;7>w;w++)W=m?m.apply(t.input?t.input[0]:null,[z]):[!0,""],F=z.getMonth()!==Z,L=F&&!v||!W[0]||J&&J>z||Q&&z>Q,E+="<td class='"+((w+c+6)%7>=5?" ui-datepicker-week-end":"")+(F?" ui-datepicker-other-month":"")+(z.getTime()===D.getTime()&&Z===t.selectedMonth&&t._keyEvent||b.getTime()===z.getTime()&&b.getTime()===D.getTime()?" "+this._dayOverClass:"")+(L?" "+this._unselectableClass+" ui-state-disabled":"")+(F&&!_?"":" "+W[1]+(z.getTime()===G.getTime()?" "+this._currentClass:"")+(z.getTime()===Y.getTime()?" ui-datepicker-today":""))+"'"+(F&&!_||!W[2]?"":" title='"+W[2].replace(/'/g,"'")+"'")+(L?"":" data-handler='selectDay' data-event='click' data-month='"+z.getMonth()+"' data-year='"+z.getFullYear()+"'")+">"+(F&&!_?" ":L?"<span class='ui-state-default'>"+z.getDate()+"</span>":"<a class='ui-state-default"+(z.getTime()===Y.getTime()?" ui-state-highlight":"")+(z.getTime()===G.getTime()?" ui-state-active":"")+(F?" ui-priority-secondary":"")+"' href='#'>"+z.getDate()+"</a>")+"</td>",z.setDate(z.getDate()+1),z=this._daylightSavingAdjust(z);I+=E+"</tr>"}Z++,Z>11&&(Z=0,te++),I+="</tbody></table>"+($?"</div>"+(U[0]>0&&C===U[1]-1?"<div class='ui-datepicker-row-break'></div>":""):""),x+=I}y+=x}return y+=h,t._keyEvent=!1,y},_generateMonthYearHeader:function(t,e,i,s,n,o,a,r){var l,h,c,u,d,p,f,g,m=this._get(t,"changeMonth"),_=this._get(t,"changeYear"),v=this._get(t,"showMonthAfterYear"),b="<div class='ui-datepicker-title'>",y="";if(o||!m)y+="<span class='ui-datepicker-month'>"+a[e]+"</span>";else{for(l=s&&s.getFullYear()===i,h=n&&n.getFullYear()===i,y+="<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>",c=0;12>c;c++)(!l||c>=s.getMonth())&&(!h||n.getMonth()>=c)&&(y+="<option value='"+c+"'"+(c===e?" selected='selected'":"")+">"+r[c]+"</option>");y+="</select>"}if(v||(b+=y+(!o&&m&&_?"":" ")),!t.yearshtml)if(t.yearshtml="",o||!_)b+="<span class='ui-datepicker-year'>"+i+"</span>";else{for(u=this._get(t,"yearRange").split(":"),d=(new Date).getFullYear(),p=function(t){var e=t.match(/c[+\-].*/)?i+parseInt(t.substring(1),10):t.match(/[+\-].*/)?d+parseInt(t,10):parseInt(t,10);return isNaN(e)?d:e},f=p(u[0]),g=Math.max(f,p(u[1]||"")),f=s?Math.max(f,s.getFullYear()):f,g=n?Math.min(g,n.getFullYear()):g,t.yearshtml+="<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";g>=f;f++)t.yearshtml+="<option value='"+f+"'"+(f===i?" selected='selected'":"")+">"+f+"</option>";t.yearshtml+="</select>",b+=t.yearshtml,t.yearshtml=null}return b+=this._get(t,"yearSuffix"),v&&(b+=(!o&&m&&_?"":" ")+y),b+="</div>"},_adjustInstDate:function(t,e,i){var s=t.selectedYear+("Y"===i?e:0),n=t.selectedMonth+("M"===i?e:0),o=Math.min(t.selectedDay,this._getDaysInMonth(s,n))+("D"===i?e:0),a=this._restrictMinMax(t,this._daylightSavingAdjust(new Date(s,n,o)));t.selectedDay=a.getDate(),t.drawMonth=t.selectedMonth=a.getMonth(),t.drawYear=t.selectedYear=a.getFullYear(),("M"===i||"Y"===i)&&this._notifyChange(t)},_restrictMinMax:function(t,e){var i=this._getMinMaxDate(t,"min"),s=this._getMinMaxDate(t,"max"),n=i&&i>e?i:e;return s&&n>s?s:n},_notifyChange:function(t){var e=this._get(t,"onChangeMonthYear");e&&e.apply(t.input?t.input[0]:null,[t.selectedYear,t.selectedMonth+1,t])},_getNumberOfMonths:function(t){var e=this._get(t,"numberOfMonths");return null==e?[1,1]:"number"==typeof e?[1,e]:e},_getMinMaxDate:function(t,e){return this._determineDate(t,this._get(t,e+"Date"),null)},_getDaysInMonth:function(t,e){return 32-this._daylightSavingAdjust(new Date(t,e,32)).getDate()},_getFirstDayOfMonth:function(t,e){return new Date(t,e,1).getDay()},_canAdjustMonth:function(t,e,i,s){var n=this._getNumberOfMonths(t),o=this._daylightSavingAdjust(new Date(i,s+(0>e?e:n[0]*n[1]),1));return 0>e&&o.setDate(this._getDaysInMonth(o.getFullYear(),o.getMonth())),this._isInRange(t,o)},_isInRange:function(t,e){var i,s,n=this._getMinMaxDate(t,"min"),o=this._getMinMaxDate(t,"max"),a=null,r=null,l=this._get(t,"yearRange");return l&&(i=l.split(":"),s=(new Date).getFullYear(),a=parseInt(i[0],10),r=parseInt(i[1],10),i[0].match(/[+\-].*/)&&(a+=s),i[1].match(/[+\-].*/)&&(r+=s)),(!n||e.getTime()>=n.getTime())&&(!o||e.getTime()<=o.getTime())&&(!a||e.getFullYear()>=a)&&(!r||r>=e.getFullYear())},_getFormatConfig:function(t){var e=this._get(t,"shortYearCutoff");return e="string"!=typeof e?e:(new Date).getFullYear()%100+parseInt(e,10),{shortYearCutoff:e,dayNamesShort:this._get(t,"dayNamesShort"),dayNames:this._get(t,"dayNames"),monthNamesShort:this._get(t,"monthNamesShort"),monthNames:this._get(t,"monthNames")}},_formatDate:function(t,e,i,s){e||(t.currentDay=t.selectedDay,t.currentMonth=t.selectedMonth,t.currentYear=t.selectedYear);var n=e?"object"==typeof e?e:this._daylightSavingAdjust(new Date(s,i,e)):this._daylightSavingAdjust(new Date(t.currentYear,t.currentMonth,t.currentDay));return this.formatDate(this._get(t,"dateFormat"),n,this._getFormatConfig(t))}}),t.fn.datepicker=function(e){if(!this.length)return this;t.datepicker.initialized||(t(document).on("mousedown",t.datepicker._checkExternalClick),t.datepicker.initialized=!0),0===t("#"+t.datepicker._mainDivId).length&&t("body").append(t.datepicker.dpDiv);var i=Array.prototype.slice.call(arguments,1);return"string"!=typeof e||"isDisabled"!==e&&"getDate"!==e&&"widget"!==e?"option"===e&&2===arguments.length&&"string"==typeof arguments[1]?t.datepicker["_"+e+"Datepicker"].apply(t.datepicker,[this[0]].concat(i)):this.each(function(){"string"==typeof e?t.datepicker["_"+e+"Datepicker"].apply(t.datepicker,[this].concat(i)):t.datepicker._attachDatepicker(this,e)}):t.datepicker["_"+e+"Datepicker"].apply(t.datepicker,[this[0]].concat(i))},t.datepicker=new i,t.datepicker.initialized=!1,t.datepicker.uuid=(new Date).getTime(),t.datepicker.version="1.12.1",t.datepicker}); \ No newline at end of file diff --git a/public/js/lib/modernizr-custom.js b/public/js/lib/modernizr-custom.js new file mode 100644 index 0000000000..93132c060c --- /dev/null +++ b/public/js/lib/modernizr-custom.js @@ -0,0 +1,3 @@ +/*! modernizr 3.3.1 (Custom Build) | MIT * + * https://modernizr.com/download/?-inputtypes-setclasses !*/ +!function(e,t,n){function a(e,t){return typeof e===t}function s(){var e,t,n,s,i,o,c;for(var u in r)if(r.hasOwnProperty(u)){if(e=[],t=r[u],t.name&&(e.push(t.name.toLowerCase()),t.options&&t.options.aliases&&t.options.aliases.length))for(n=0;n<t.options.aliases.length;n++)e.push(t.options.aliases[n].toLowerCase());for(s=a(t.fn,"function")?t.fn():t.fn,i=0;i<e.length;i++)o=e[i],c=o.split("."),1===c.length?Modernizr[c[0]]=s:(!Modernizr[c[0]]||Modernizr[c[0]]instanceof Boolean||(Modernizr[c[0]]=new Boolean(Modernizr[c[0]])),Modernizr[c[0]][c[1]]=s),l.push((s?"":"no-")+c.join("-"))}}function i(e){var t=u.className,n=Modernizr._config.classPrefix||"";if(f&&(t=t.baseVal),Modernizr._config.enableJSClass){var a=new RegExp("(^|\\s)"+n+"no-js(\\s|$)");t=t.replace(a,"$1"+n+"js$2")}Modernizr._config.enableClasses&&(t+=" "+n+e.join(" "+n),f?u.className.baseVal=t:u.className=t)}function o(){return"function"!=typeof t.createElement?t.createElement(arguments[0]):f?t.createElementNS.call(t,"http://www.w3.org/2000/svg",arguments[0]):t.createElement.apply(t,arguments)}var l=[],r=[],c={_version:"3.3.1",_config:{classPrefix:"",enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(e,t){var n=this;setTimeout(function(){t(n[e])},0)},addTest:function(e,t,n){r.push({name:e,fn:t,options:n})},addAsyncTest:function(e){r.push({name:null,fn:e})}},Modernizr=function(){};Modernizr.prototype=c,Modernizr=new Modernizr;var u=t.documentElement,f="svg"===u.nodeName.toLowerCase(),p=o("input"),d="search tel url email datetime date month week time datetime-local number range color".split(" "),m={};Modernizr.inputtypes=function(e){for(var a,s,i,o=e.length,l="1)",r=0;o>r;r++)p.setAttribute("type",a=e[r]),i="text"!==p.type&&"style"in p,i&&(p.value=l,p.style.cssText="position:absolute;visibility:hidden;",/^range$/.test(a)&&p.style.WebkitAppearance!==n?(u.appendChild(p),s=t.defaultView,i=s.getComputedStyle&&"textfield"!==s.getComputedStyle(p,null).WebkitAppearance&&0!==p.offsetHeight,u.removeChild(p)):/^(search|tel)$/.test(a)||(i=/^(url|email)$/.test(a)?p.checkValidity&&p.checkValidity()===!1:p.value!=l)),m[e[r]]=!!i;return m}(d),s(),i(l),delete c.addTest,delete c.addAsyncTest;for(var h=0;h<Modernizr._q.length;h++)Modernizr._q[h]();e.Modernizr=Modernizr}(window,document); \ No newline at end of file diff --git a/resources/views/transactions/single/create.twig b/resources/views/transactions/single/create.twig index 2b4f3aaaff..b6f2edbfef 100644 --- a/resources/views/transactions/single/create.twig +++ b/resources/views/transactions/single/create.twig @@ -223,9 +223,13 @@ </script> <script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js"></script> <script type="text/javascript" src="js/lib/bootstrap-tagsinput.min.js"></script> + <script type="text/javascript" src="js/lib/modernizr-custom.js"></script> + <script type="text/javascript" src="js/lib/jquery-ui.min.js"></script> <script type="text/javascript" src="js/ff/transactions/single/create.js"></script> {% endblock %} {% block styles %} <link href="css/bootstrap-tagsinput.css" type="text/css" rel="stylesheet" media="all"> + <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> + <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> {% endblock %} diff --git a/resources/views/transactions/single/edit.twig b/resources/views/transactions/single/edit.twig index ce096966b5..82149d1d50 100644 --- a/resources/views/transactions/single/edit.twig +++ b/resources/views/transactions/single/edit.twig @@ -233,8 +233,13 @@ </script> <script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js"></script> <script type="text/javascript" src="js/lib/bootstrap-tagsinput.min.js"></script> + <script type="text/javascript" src="js/lib/jquery-ui.min.js"></script> + <script type="text/javascript" src="js/lib/modernizr-custom.js"></script> <script type="text/javascript" src="js/ff/transactions/single/edit.js"></script> + {% endblock %} {% block styles %} <link href="css/bootstrap-tagsinput.css" type="text/css" rel="stylesheet" media="all"> + <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> + <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> {% endblock %} From 6b7a47ca28b81d8ba554d75b6a81dfd3686ca92b Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 15 Jan 2017 20:10:34 +0100 Subject: [PATCH 566/709] Clean up JS. --- public/js/ff/transactions/single/edit.js | 86 ++++++------------------ 1 file changed, 20 insertions(+), 66 deletions(-) diff --git a/public/js/ff/transactions/single/edit.js b/public/js/ff/transactions/single/edit.js index 8e6552758c..9d0a8a9cbc 100644 --- a/public/js/ff/transactions/single/edit.js +++ b/public/js/ff/transactions/single/edit.js @@ -19,7 +19,6 @@ $(document).ready(function () { ); } - // the destination account name is always an expense account name. if ($('input[name="destination_account_name"]').length > 0) { $.getJSON('json/expense-accounts').done(function (data) { @@ -27,29 +26,20 @@ $(document).ready(function () { }); } - // also for multi input - if ($('input[name="destination_account_name[]"]').length > 0) { - $.getJSON('json/expense-accounts').done(function (data) { - $('input[name="destination_account_name[]"]').typeahead({source: data}); - }); - } + $.getJSON('json/tags').done(function (data) { - if ($('input[name="tags"]').length > 0) { - $.getJSON('json/tags').done(function (data) { - - var opt = { - typeahead: { - source: data, - afterSelect: function (val) { - this.$element.val(""); - } + var opt = { + typeahead: { + source: data, + afterSelect: function (val) { + this.$element.val(""); } - }; - $('input[name="tags"]').tagsinput( - opt - ); - }); - } + } + }; + $('input[name="tags"]').tagsinput( + opt + ); + }); // the source account name is always a revenue account name. if ($('input[name="source_account_name"]').length > 0) { @@ -57,50 +47,14 @@ $(document).ready(function () { $('input[name="source_account_name"]').typeahead({source: data}); }); } - // also for multi-input: - if ($('input[name="source_account_name[]"]').length > 0) { - $.getJSON('json/revenue-accounts').done(function (data) { - $('input[name="source_account_name[]"]').typeahead({source: data}); - }); - } - // and for split: - if ($('input[name="journal_source_account_name"]').length > 0) { - $.getJSON('json/revenue-accounts').done(function (data) { - $('input[name="journal_source_account_name"]').typeahead({source: data}); - }); - } - - - if ($('input[name="description"]').length > 0 && !(typeof what === "undefined")) { - $.getJSON('json/transaction-journals/' + what).done(function (data) { - $('input[name="description"]').typeahead({source: data}); - }); - } - // also for multi input: - if ($('input[name="description[]"]').length > 0 && !(typeof what === "undefined")) { - $.getJSON('json/transaction-journals/' + what).done(function (data) { - $('input[name="description[]"]').typeahead({source: data}); - }); - } - // and for the (rare) journal_description: - if ($('input[name="journal_description"]').length > 0 && !(typeof what === "undefined")) { - $.getJSON('json/transaction-journals/' + what).done(function (data) { - $('input[name="journal_description"]').typeahead({source: data}); - }); - } - - if ($('input[name="category"]').length > 0) { - $.getJSON('json/categories').done(function (data) { - $('input[name="category"]').typeahead({source: data}); - }); - } - - // also for multi input: - if ($('input[name^="category["]').length > 0) { - $.getJSON('json/categories').done(function (data) { - $('input[name^="category["]').typeahead({source: data}); - }); - } + + $.getJSON('json/transaction-journals/' + what).done(function (data) { + $('input[name="description"]').typeahead({source: data}); + }); + $.getJSON('json/categories').done(function (data) { + $('input[name="category"]').typeahead({source: data}); + }); + }); From 1e69a54972f9f3cac3f3f1901a54cf977311f6d3 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 15 Jan 2017 20:18:32 +0100 Subject: [PATCH 567/709] Small JS issue. [skip ci] --- public/js/ff/transactions/single/edit.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/public/js/ff/transactions/single/edit.js b/public/js/ff/transactions/single/edit.js index 9d0a8a9cbc..b141df5335 100644 --- a/public/js/ff/transactions/single/edit.js +++ b/public/js/ff/transactions/single/edit.js @@ -8,6 +8,8 @@ * See the LICENSE file for details. */ +/** global: what */ + $(document).ready(function () { "use strict"; // give date a datepicker if not natively supported. @@ -31,7 +33,7 @@ $(document).ready(function () { var opt = { typeahead: { source: data, - afterSelect: function (val) { + afterSelect: function () { this.$element.val(""); } } @@ -56,5 +58,5 @@ $(document).ready(function () { $.getJSON('json/categories').done(function (data) { $('input[name="category"]').typeahead({source: data}); }); - + }); From 77f889aba65ea5168d99b1330f66331011ca5ae3 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 16 Jan 2017 20:10:47 +0100 Subject: [PATCH 568/709] Include a batch of naughty strings to see what happens to Firefly. --- resources/tests/blns.base64.json | 468 ++++++++++++++++++ tests/TestCase.php | 37 +- .../Transaction/SingleControllerTest.php | 27 +- 3 files changed, 520 insertions(+), 12 deletions(-) create mode 100644 resources/tests/blns.base64.json diff --git a/resources/tests/blns.base64.json b/resources/tests/blns.base64.json new file mode 100644 index 0000000000..19c54f1325 --- /dev/null +++ b/resources/tests/blns.base64.json @@ -0,0 +1,468 @@ +[ + "", + "dW5kZWZpbmVkCg==", + "dW5kZWYK", + "bnVsbAo=", + "TlVMTAo=", + "KG51bGwpCg==", + "bmlsCg==", + "TklMCg==", + "dHJ1ZQo=", + "ZmFsc2UK", + "VHJ1ZQo=", + "RmFsc2UK", + "Tm9uZQo=", + "aGFzT3duUHJvcGVydHkK", + "XFw=", + "MAo=", + "XFxcXAo=", + "MQo=", + "MS4wMAo=", + "JDEuMDAK", + "MS8yCg==", + "MUUyCg==", + "MUUwMgo=", + "MUUrMDIK", + "LTEK", + "LTEuMDAK", + "LSQxLjAwCg==", + "LTEvMgo=", + "LTFFMgo=", + "LTFFMDIK", + "LTFFKzAyCg==", + "MS8wCg==", + "MC8wCg==", + "LTIxNDc0ODM2NDgvLTEK", + "LTkyMjMzNzIwMzY4NTQ3NzU4MDgvLTEK", + "MC4wMAo=", + "MC4uMAo=", + "Lgo=", + "MC4wLjAK", + "MCwwMAo=", + "MCwsMAo=", + "LAo=", + "MCwwLDAK", + "MC4wLzAK", + "MS4wLzAuMAo=", + "MC4wLzAuMAo=", + "MSwwLzAsMAo=", + "MCwwLzAsMAo=", + "LS0xCg==", + "LQo=", + "LS4K", + "LSwK", + "OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5Cg==", + "TmFOCg==", + "SW5maW5pdHkK", + "LUluZmluaXR5Cg==", + "MHgwCg==", + "MHhmZmZmZmZmZgo=", + "MHhmZmZmZmZmZmZmZmZmZmZmCg==", + "MHhhYmFkMWRlYQo=", + "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5Cg==", + "MSwwMDAuMDAK", + "MSAwMDAuMDAK", + "MScwMDAuMDAK", + "MSwwMDAsMDAwLjAwCg==", + "MSAwMDAgMDAwLjAwCg==", + "MScwMDAnMDAwLjAwCg==", + "MS4wMDAsMDAK", + "MSAwMDAsMDAK", + "MScwMDAsMDAK", + "MS4wMDAuMDAwLDAwCg==", + "MSAwMDAgMDAwLDAwCg==", + "MScwMDAnMDAwLDAwCg==", + "MDEwMDAK", + "MDgK", + "MDkK", + "Mi4yMjUwNzM4NTg1MDcyMDExZS0zMDgK", + "LC4vOydbXS09Cg==", + "PD4/OiJ7fXxfKwo=", + "IUAjJCVeJiooKWB+Cg==", + "zqniiYjDp+KImuKIq8ucwrXiiaTiiaXDtwo=", + "w6XDn+KIgsaSwqnLmeKIhsuawqzigKbDpgo=", + "xZPiiJHCtMKu4oCgwqXCqMuGw7jPgOKAnOKAmAo=", + "wqHihKLCo8Ki4oiewqfCtuKAosKqwrrigJPiiaAK", + "wrjLm8OH4peKxLHLnMOCwq/LmMK/Cg==", + "w4XDjcOOw4/LncOTw5Tvo7/DksOaw4bimIMK", + "xZLigJ7CtOKAsMuHw4HCqMuGw5jiiI/igJ3igJkK", + "YOKBhOKCrOKAueKAuu+sge+sguKAocKwwrfigJrigJTCsQo=", + "4oWb4oWc4oWd4oWeCg==", + "0IHQgtCD0ITQhdCG0IfQiNCJ0IrQi9CM0I3QjtCP0JDQkdCS0JPQlNCV0JbQl9CY0JnQmtCb0JzQndCe0J/QoNCh0KLQo9Ck0KXQptCn0KjQqdCq0KvQrNCt0K7Qr9Cw0LHQstCz0LTQtdC20LfQuNC50LrQu9C80L3QvtC/0YDRgdGC0YPRhNGF0YbRh9GI0YnRitGL0YzRjdGO0Y8K", + "2aDZodmi2aPZpNml2abZp9mo2akK", + "4oGw4oG04oG1Cg==", + "4oKA4oKB4oKCCg==", + "4oGw4oG04oG14oKA4oKB4oKCCg==", + "Jwo=", + "Igo=", + "JycK", + "IiIK", + "JyInCg==", + "IicnJyciJyIK", + "IiciJyInJycnIgo=", + "55Sw5Lit44GV44KT44Gr44GC44GS44Gm5LiL44GV44GECg==", + "44OR44O844OG44Kj44O844G46KGM44GL44Gq44GE44GLCg==", + "5ZKM6KO95ryi6KqeCg==", + "6YOo6JC95qC8Cg==", + "7IKs7ZqM6rO87ZWZ7JuQIOyWtO2VmeyXsOq1rOyGjAo=", + "7LCm7LCo66W8IO2DgOqzoCDsmKgg7Y6y7Iuc66eo6rO8IOyRm+uLpOumrCDrmKDrsKnqsIHtlZgK", + "56S+5pyD56eR5a246Zmi6Kqe5a2456CU56m25omACg==", + "7Jq4656A67CU7Yag66W0Cg==", + "8KCcjvCgnLHwoJ258KCxk/CgsbjwoLKW8KCzjwo=", + "44O94Ly84LqI2YTNnOC6iOC8ve++iSDjg73gvLzguojZhM2c4LqI4Ly9776JCg==", + "KO+9oeKXlSDiiIAg4peV772hKQo=", + "772A772oKMK04oiA772A4oipCg==", + "X1/vvpsoLF8sKikK", + "44O7KO+/o+KIgO+/oynjg7s6KjoK", + "776f772l4py/44O+4pWyKO+9oeKXleKAv+KXle+9oSnilbHinL/vvaXvvp8K", + "LOOAguODuzoqOuODu+OCnOKAmSgg4pi7IM+JIOKYuyAp44CC44O7Oio644O744Kc4oCZCg==", + "KOKVr8Kw4pahwrDvvInila/vuLUg4pS74pSB4pS7KQo=", + "KO++ieCypeebiuCype+8ie++ie+7vyDilLvilIHilLsK", + "KCDNocKwIM2cypYgzaHCsCkK", + "8J+YjQo=", + "8J+RqfCfj70K", + "8J+RviDwn5mHIPCfkoEg8J+ZhSDwn5mGIPCfmYsg8J+ZjiDwn5mNCg==", + "8J+QtSDwn5mIIPCfmYkg8J+Zigo=", + "4p2k77iPIPCfkpQg8J+SjCDwn5KVIPCfkp4g8J+SkyDwn5KXIPCfkpYg8J+SmCDwn5KdIPCfkp8g8J+SnCDwn5KbIPCfkpog8J+SmQo=", + "4pyL8J+PvyDwn5Kq8J+PvyDwn5GQ8J+PvyDwn5mM8J+PvyDwn5GP8J+PvyDwn5mP8J+Pvwo=", + "8J+aviDwn4aSIPCfhpMg8J+GlSDwn4aWIPCfhpcg8J+GmSDwn4+nCg==", + "MO+4j+KDoyAx77iP4oOjIDLvuI/ig6MgM++4j+KDoyA077iP4oOjIDXvuI/ig6MgNu+4j+KDoyA377iP4oOjIDjvuI/ig6MgOe+4j+KDoyDwn5SfCg==", + "77yR77yS77yTCg==", + "2aHZotmjCg==", + "2KvZhSDZhtmB2LMg2LPZgti32Kog2YjYqNin2YTYqtit2K/Zitiv2IwsINis2LLZitix2KrZiiDYqNin2LPYqtiu2K/Yp9mFINij2YYg2K/ZhtmILiDYpdiwINmH2YbYp9ifINin2YTYs9iq2KfYsSDZiNiq2YbYtdmK2Kgg2YPYp9mGLiDYo9mH2ZHZhCDYp9mK2LfYp9mE2YrYp9iMINio2LHZiti32KfZhtmK2Kct2YHYsdmG2LPYpyDZgtivINij2K7YsC4g2LPZhNmK2YXYp9mG2Iwg2KXYqtmB2KfZgtmK2Kkg2KjZitmGINmF2KcsINmK2LDZg9ixINin2YTYrdiv2YjYryDYo9mKINio2LnYrywg2YXYudin2YXZhNipINio2YjZhNmG2K/Yp9iMINin2YTYpdi32YTYp9mCINi52YQg2KXZitmILgo=", + "15HWsNa816jWtdeQ16nWtNeB15nXqiwg15HWuNa816jWuNeQINeQ1rHXnNa515TWtNeZ150sINeQ1rXXqiDXlNa316nWuNa814HXnta315nWtNedLCDXldaw15DWtdeqINeU1rjXkNa416jWttelCg==", + "15TWuNeZ1rDXqta415R0ZXN02KfZhNi12YHYrdin2Kog2KfZhNiq2ZHYrdmI2YQK", + "77e9Cg==", + "77e6Cg==", + "4oCLCg==", + "4ZqACg==", + "4aCOCg==", + "44CACg==", + "77u/Cg==", + "4pCjCg==", + "4pCiCg==", + "4pChCg==", + "4oCq4oCqdGVzdOKAqgo=", + "4oCrdGVzdOKAqwo=", + "4oCpdGVzdOKAqQo=", + "dGVzdOKBoHRlc3TigKsK", + "4oGmdGVzdOKBpwo=", + "4bmwzLrMusyVb82eIMy3acyyzKzNh8yqzZluzJ3Ml82VdsyfzJzMmMymzZ9vzLbMmcywzKBrw6jNmsyuzLrMqsy5zLHMpCDMlnTMnc2VzLPMo8y7zKrNnmjMvM2TzLLMpsyzzJjMsmXNh8yjzLDMpsyszY4gzKLMvMy7zLHMmGjNms2OzZnMnMyjzLLNhWnMpsyyzKPMsMykdsy7zY1lzLrMrcyzzKrMsC1tzKJpzYVuzJbMusyezLLMr8ywZMy1zLzMn82ZzKnMvMyYzLMgzJ7MpcyxzLPMrXLMm8yXzJhlzZlwzaByzLzMnsy7zK3Ml2XMusygzKPNn3PMmM2HzLPNjcydzYllzYnMpcyvzJ7Mss2azKzNnMe5zKzNjs2OzJ/Mls2HzKR0zY3MrMykzZPMvMytzZjNhWnMqsyxbs2gZ8y0zYkgzY/Nic2FY8yszJ9ozaFhzKvMu8yvzZhvzKvMn8yWzY3MmcydzYlzzJfMpsyyLsyozLnNiMyjCg==", + "zKHNk8yezYVJzJfMmMymzZ1uzYfNh82ZdsyuzKtva8yyzKvMmc2IacyWzZnMrcy5zKDMnm7Mocy7zK7Mo8y6Z8yyzYjNmcytzZnMrM2OIMywdM2UzKZozJ7MsmXMosykIM2NzKzMss2WZsy0zJjNlcyjw6jNluG6ucylzKlszZbNlM2aac2TzZrMps2gbs2WzY3Ml82TzLPMrmfNjSDMqG/NmsyqzaFmzJjMo8ysIMyWzJjNlsyfzZnMrmPSic2UzKvNls2TzYfNls2FaMy1zKTMo82azZTDocyXzLzNlc2Fb8y8zKPMpXPMsc2IzLrMlsymzLvNoi7Mm8yWzJ7MoMyrzLAK", + "zJfMus2WzLnMr82T4bmuzKTNjcylzYfNiGjMssyBZc2PzZPMvMyXzJnMvMyjzZQgzYfMnMyxzKDNk82NzYVOzZXNoGXMl8yxesyYzJ3MnMy6zZlwzKTMusy5zY3Mr82aZcygzLvMoM2ccsyozKTNjcy6zJbNlMyWzJZkzKDMn8ytzKzMnc2facymzZbMqc2TzZTMpGHMoMyXzKzNicyZbs2azZwgzLvMnsywzZrNhWjMtc2JacyzzJ52zKLNh+G4mc2OzZ8t0onMrcypzLzNlG3MpMytzKtpzZXNh8ydzKZuzJfNmeG4jcyfIMyvzLLNlc2ex6vMn8yvzLDMss2ZzLvMnWYgzKrMsMywzJfMlsytzJjNmGPMps2NzLLMns2NzKnMmeG4pc2aYcyuzY7Mn8yZzZzGocypzLnNjnPMpC7MncydINKJWsyhzJbMnM2WzLDMo82JzJxhzZbMsM2ZzKzNoWzMssyrzLPNjcypZ8yhzJ/MvMyxzZrMnsyszYVvzJfNnC7Mnwo=", + "zKZIzKzMpMyXzKTNnWXNnCDMnMylzJ3Mu82NzJ/MgXfMlWjMlsyvzZNvzJ3NmcyWzY7MscyuINKJzLrMmcyezJ/NiFfMt8y8zK1hzLrMqs2NxK/NiM2VzK3NmcyvzJx0zLbMvMyuc8yYzZnNlsyVIMygzKvMoELMu82NzZnNicyzzYVlzLVozLXMrM2HzKvNmWnMuc2TzLPMs8yuzY7Mq8yVbs2fZMy0zKrMnMyWIMywzYnMqc2HzZnMss2ezYVUzZbMvM2TzKrNomjNj82TzK7Mu2XMrMydzJ/NhSDMpMy5zJ1XzZnMnsydzZTNh82dzYVhzY/Nk82UzLnMvMyjbMy0zZTMsMykzJ/NlOG4vcyrLs2VCg==", + "WsyuzJ7MoM2ZzZTNheG4gMyXzJ7NiMy7zJfhuLbNmc2OzK/MucyezZNHzLtPzK3Ml8yuCg==", + "y5nJkG5i4bSJbMmQIMmQdcaDyZDJryDHncm5b2xvcCDKh8edIMedyblvccmQbCDKh24gyod1bnDhtIlw4bSJyZR14bSJIMm5b2TJr8edyocgcG/Jr3Nu4bSJx50gb3AgcMedcyAnyofhtIlsx50gxoN14bSJyZRz4bSJZOG0iXDJkCDJuW7Kh8edyofJlMedc3VvyZQgJ8qHx53Jr8mQIMqH4bSJcyDJuW9sb3Agya9uc2ThtIkgya/Hncm5b8ulCg==", + "MDDLmcaWJC0K", + "77y0772I772FIO+9ke+9le+9ie+9g++9iyDvvYLvvZLvvY/vvZfvvY4g772G772P772YIO+9iu+9le+9je+9kO+9kyDvvY/vvZbvvYXvvZIg772U772I772FIO+9jO+9ge+9mu+9mSDvvYTvvY/vvYcK", + "8J2Qk/CdkKHwnZCeIPCdkKrwnZCu8J2QovCdkJzwnZCkIPCdkJvwnZCr8J2QqPCdkLDwnZCnIPCdkJ/wnZCo8J2QsSDwnZCj8J2QrvCdkKbwnZCp8J2QrCDwnZCo8J2Qr/CdkJ7wnZCrIPCdkK3wnZCh8J2QniDwnZCl8J2QmvCdkLPwnZCyIPCdkJ3wnZCo8J2QoAo=", + "8J2Vv/Cdlo3wnZaKIPCdlpbwnZaa8J2WjvCdlojwnZaQIPCdlofwnZaX8J2WlPCdlpzwnZaTIPCdlovwnZaU8J2WnSDwnZaP8J2WmvCdlpLwnZaV8J2WmCDwnZaU8J2Wm/CdlorwnZaXIPCdlpnwnZaN8J2WiiDwnZaR8J2WhvCdlp/wnZaeIPCdlonwnZaU8J2WjAo=", + "8J2Ru/CdkonwnZKGIPCdkpLwnZKW8J2SivCdkoTwnZKMIPCdkoPwnZKT8J2SkPCdkpjwnZKPIPCdkofwnZKQ8J2SmSDwnZKL8J2SlvCdko7wnZKR8J2SlCDwnZKQ8J2Sl/CdkobwnZKTIPCdkpXwnZKJ8J2ShiDwnZKN8J2SgvCdkpvwnZKaIPCdkoXwnZKQ8J2SiAo=", + "8J2To/Cdk7HwnZOuIPCdk7rwnZO+8J2TsvCdk6zwnZO0IPCdk6vwnZO78J2TuPCdlIDwnZO3IPCdk6/wnZO48J2UgSDwnZOz8J2TvvCdk7bwnZO58J2TvCDwnZO48J2Tv/Cdk67wnZO7IPCdk73wnZOx8J2TriDwnZO18J2TqvCdlIPwnZSCIPCdk63wnZO48J2TsAo=", + "8J2Vi/CdlZnwnZWWIPCdlaLwnZWm8J2VmvCdlZTwnZWcIPCdlZPwnZWj8J2VoPCdlajwnZWfIPCdlZfwnZWg8J2VqSDwnZWb8J2VpvCdlZ7wnZWh8J2VpCDwnZWg8J2Vp/CdlZbwnZWjIPCdlaXwnZWZ8J2VliDwnZWd8J2VkvCdlavwnZWqIPCdlZXwnZWg8J2VmAo=", + "8J2ag/CdmpHwnZqOIPCdmprwnZqe8J2akvCdmozwnZqUIPCdmovwnZqb8J2amPCdmqDwnZqXIPCdmo/wnZqY8J2aoSDwnZqT8J2anvCdmpbwnZqZ8J2anCDwnZqY8J2an/Cdmo7wnZqbIPCdmp3wnZqR8J2ajiDwnZqV8J2aivCdmqPwnZqiIPCdmo3wnZqY8J2akAo=", + "4pKv4pKj4pKgIOKSrOKSsOKSpOKSnuKSpiDikp3ikq3ikqrikrLikqkg4pKh4pKq4pKzIOKSpeKSsOKSqOKSq+KSriDikqrikrHikqDikq0g4pKv4pKj4pKgIOKSp+KSnOKSteKStCDikp/ikqrikqIK", + "PHNjcmlwdD5hbGVydCgxMjMpPC9zY3JpcHQ+Cg==", + "Jmx0O3NjcmlwdCZndDthbGVydCgmIzM5OzEyMyYjMzk7KTsmbHQ7L3NjcmlwdCZndDsK", + "PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KDEyMykgLz4K", + "PHN2Zz48c2NyaXB0PjEyMzwxPmFsZXJ0KDEyMyk8L3NjcmlwdD4K", + "Ij48c2NyaXB0PmFsZXJ0KDEyMyk8L3NjcmlwdD4K", + "Jz48c2NyaXB0PmFsZXJ0KDEyMyk8L3NjcmlwdD4K", + "PjxzY3JpcHQ+YWxlcnQoMTIzKTwvc2NyaXB0Pgo=", + "PC9zY3JpcHQ+PHNjcmlwdD5hbGVydCgxMjMpPC9zY3JpcHQ+Cg==", + "PCAvIHNjcmlwdCA+PCBzY3JpcHQgPmFsZXJ0KDEyMyk8IC8gc2NyaXB0ID4K", + "b25mb2N1cz1KYVZhU0NyaXB0OmFsZXJ0KDEyMykgYXV0b2ZvY3VzCg==", + "IiBvbmZvY3VzPUphVmFTQ3JpcHQ6YWxlcnQoMTIzKSBhdXRvZm9jdXMK", + "JyBvbmZvY3VzPUphVmFTQ3JpcHQ6YWxlcnQoMTIzKSBhdXRvZm9jdXMK", + "77ycc2NyaXB077yeYWxlcnQoMTIzKe+8nC9zY3JpcHTvvJ4K", + "PHNjPHNjcmlwdD5yaXB0PmFsZXJ0KDEyMyk8L3NjPC9zY3JpcHQ+cmlwdD4K", + "LS0+PHNjcmlwdD5hbGVydCgxMjMpPC9zY3JpcHQ+Cg==", + "IjthbGVydCgxMjMpO3Q9Igo=", + "JzthbGVydCgxMjMpO3Q9Jwo=", + "SmF2YVNDcmlwdDphbGVydCgxMjMpCg==", + "O2FsZXJ0KDEyMyk7Cg==", + "c3JjPUphVmFTQ3JpcHQ6cHJvbXB0KDEzMikK", + "Ij48c2NyaXB0PmFsZXJ0KDEyMyk7PC9zY3JpcHQgeD0iCg==", + "Jz48c2NyaXB0PmFsZXJ0KDEyMyk7PC9zY3JpcHQgeD0nCg==", + "PjxzY3JpcHQ+YWxlcnQoMTIzKTs8L3NjcmlwdCB4PQo=", + "IiBhdXRvZm9jdXMgb25rZXl1cD0iamF2YXNjcmlwdDphbGVydCgxMjMpCg==", + "JyBhdXRvZm9jdXMgb25rZXl1cD0namF2YXNjcmlwdDphbGVydCgxMjMpCg==", + "PHNjcmlwdHgyMHR5cGU9InRleHQvamF2YXNjcmlwdCI+amF2YXNjcmlwdDphbGVydCgxKTs8L3NjcmlwdD4K", + "PHNjcmlwdHgzRXR5cGU9InRleHQvamF2YXNjcmlwdCI+amF2YXNjcmlwdDphbGVydCgxKTs8L3NjcmlwdD4K", + "PHNjcmlwdHgwRHR5cGU9InRleHQvamF2YXNjcmlwdCI+amF2YXNjcmlwdDphbGVydCgxKTs8L3NjcmlwdD4K", + "PHNjcmlwdHgwOXR5cGU9InRleHQvamF2YXNjcmlwdCI+amF2YXNjcmlwdDphbGVydCgxKTs8L3NjcmlwdD4K", + "PHNjcmlwdHgwQ3R5cGU9InRleHQvamF2YXNjcmlwdCI+amF2YXNjcmlwdDphbGVydCgxKTs8L3NjcmlwdD4K", + "PHNjcmlwdHgyRnR5cGU9InRleHQvamF2YXNjcmlwdCI+amF2YXNjcmlwdDphbGVydCgxKTs8L3NjcmlwdD4K", + "PHNjcmlwdHgwQXR5cGU9InRleHQvamF2YXNjcmlwdCI+amF2YXNjcmlwdDphbGVydCgxKTs8L3NjcmlwdD4K", + "J2AiPjx4M0NzY3JpcHQ+amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "J2AiPjx4MDBzY3JpcHQ+amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "QUJDPGRpdiBzdHlsZT0ieHgzQWV4cHJlc3Npb24oamF2YXNjcmlwdDphbGVydCgxKSI+REVGCg==", + "QUJDPGRpdiBzdHlsZT0ieDpleHByZXNzaW9ueDVDKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDpleHByZXNzaW9ueDAwKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDpleHB4MDByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDpleHB4NUNyZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4MEFleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4MDlleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4RTN4ODB4ODBleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODRleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4QzJ4QTBleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODBleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4OEFleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4MERleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4MENleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODdleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4RUZ4QkJ4QkZleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4MjBleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODhleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4MDBleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4OEJleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODZleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODVleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODJleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4MEJleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODFleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODNleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODlleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRgo=", + "PGEgaHJlZj0ieDBCamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDBGamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieEMyeEEwamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDA1amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieEUxeEEweDhFamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDE4amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDExamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieEUyeDgweDg4amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieEUyeDgweDg5amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieEUyeDgweDgwamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDE3amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDAzamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDBFamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDFBamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDAwamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDEwamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieEUyeDgweDgyamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDIwamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDEzamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDA5amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieEUyeDgweDhBamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDE0amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDE5amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieEUyeDgweEFGamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDFGamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieEUyeDgweDgxamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDFEamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieEUyeDgweDg3amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDA3amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieEUxeDlBeDgwamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieEUyeDgweDgzamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDA0amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDAxamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDA4amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieEUyeDgweDg0amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieEUyeDgweDg2amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieEUzeDgweDgwamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDEyamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDBEamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDBBamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDBDamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDE1amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieEUyeDgweEE4amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDE2amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDAyamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDFCamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDA2amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieEUyeDgweEE5amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieEUyeDgweDg1amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDFFamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieEUyeDgxeDlGamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0ieDFDamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0iamF2YXNjcmlwdHgwMDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0iamF2YXNjcmlwdHgzQTpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0iamF2YXNjcmlwdHgwOTpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0iamF2YXNjcmlwdHgwRDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "PGEgaHJlZj0iamF2YXNjcmlwdHgwQTpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1lbnQxIj50ZXN0PC9hPgo=", + "YCInPjxpbWcgc3JjPXh4eDp4IHgwQW9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4K", + "YCInPjxpbWcgc3JjPXh4eDp4IHgyMm9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4K", + "YCInPjxpbWcgc3JjPXh4eDp4IHgwQm9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4K", + "YCInPjxpbWcgc3JjPXh4eDp4IHgwRG9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4K", + "YCInPjxpbWcgc3JjPXh4eDp4IHgyRm9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4K", + "YCInPjxpbWcgc3JjPXh4eDp4IHgwOW9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4K", + "YCInPjxpbWcgc3JjPXh4eDp4IHgwQ29uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4K", + "YCInPjxpbWcgc3JjPXh4eDp4IHgwMG9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4K", + "YCInPjxpbWcgc3JjPXh4eDp4IHgyN29uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4K", + "YCInPjxpbWcgc3JjPXh4eDp4IHgyMG9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4K", + "ImAnPjxzY3JpcHQ+eDNCamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eDBEamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEVGeEJCeEJGamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEUyeDgweDgxamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEUyeDgweDg0amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEUzeDgweDgwamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eDA5amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEUyeDgweDg5amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEUyeDgweDg1amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEUyeDgweDg4amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eDAwamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEUyeDgweEE4amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEUyeDgweDhBamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEUxeDlBeDgwamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eDBDamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eDJCamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEYweDkweDk2eDlBamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+LWphdmFzY3JpcHQ6YWxlcnQoMSk8L3NjcmlwdD4K", + "ImAnPjxzY3JpcHQ+eDBBamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEUyeDgweEFGamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eDdFamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEUyeDgweDg3amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEUyeDgxeDlGamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEUyeDgweEE5amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEMyeDg1amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEVGeEJGeEFFamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEUyeDgweDgzamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEUyeDgweDhCamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEVGeEJGeEJFamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEUyeDgweDgwamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eDIxamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEUyeDgweDgyamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEUyeDgweDg2amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEUxeEEweDhFamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eDBCamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eDIwamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "ImAnPjxzY3JpcHQ+eEMyeEEwamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pgo=", + "PGltZyB4MDBzcmM9eCBvbmVycm9yPSJhbGVydCgxKSI+Cg==", + "PGltZyB4NDdzcmM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4K", + "PGltZyB4MTFzcmM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4K", + "PGltZyB4MTJzcmM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4K", + "PGltZ3g0N3NyYz14IG9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPgo=", + "PGltZ3gxMHNyYz14IG9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPgo=", + "PGltZ3gxM3NyYz14IG9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPgo=", + "PGltZ3gzMnNyYz14IG9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPgo=", + "PGltZ3g0N3NyYz14IG9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPgo=", + "PGltZ3gxMXNyYz14IG9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPgo=", + "PGltZyB4NDdzcmM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4K", + "PGltZyB4MzRzcmM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4K", + "PGltZyB4MzlzcmM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4K", + "PGltZyB4MDBzcmM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4K", + "PGltZyBzcmN4MDk9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4K", + "PGltZyBzcmN4MTA9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4K", + "PGltZyBzcmN4MTM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4K", + "PGltZyBzcmN4MzI9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4K", + "PGltZyBzcmN4MTI9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4K", + "PGltZyBzcmN4MTE9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4K", + "PGltZyBzcmN4MDA9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4K", + "PGltZyBzcmN4NDc9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4K", + "PGltZyBzcmM9eHgwOW9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPgo=", + "PGltZyBzcmM9eHgxMG9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPgo=", + "PGltZyBzcmM9eHgxMW9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPgo=", + "PGltZyBzcmM9eHgxMm9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPgo=", + "PGltZyBzcmM9eHgxM29uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPgo=", + "PGltZ1thXVtiXVtjXXNyY1tkXT14W2Vdb25lcnJvcj1bZl0iYWxlcnQoMSkiPgo=", + "PGltZyBzcmM9eCBvbmVycm9yPXgwOSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4K", + "PGltZyBzcmM9eCBvbmVycm9yPXgxMCJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4K", + "PGltZyBzcmM9eCBvbmVycm9yPXgxMSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4K", + "PGltZyBzcmM9eCBvbmVycm9yPXgxMiJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4K", + "PGltZyBzcmM9eCBvbmVycm9yPXgzMiJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4K", + "PGltZyBzcmM9eCBvbmVycm9yPXgwMCJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4K", + "PGEgaHJlZj1qYXZhJiMxJiMyJiMzJiM0JiM1JiM2JiM3JiM4JiMxMSYjMTJzY3JpcHQ6amF2YXNjcmlwdDphbGVydCgxKT5YWFg8L2E+Cg==", + "PGltZyBzcmM9InhgIGA8c2NyaXB0PmphdmFzY3JpcHQ6YWxlcnQoMSk8L3NjcmlwdD4iYCBgPgo=", + "PGltZyBzcmMgb25lcnJvciAvIiAnIj0gYWx0PWphdmFzY3JpcHQ6YWxlcnQoMSkvLyI+Cg==", + "PHRpdGxlIG9ucHJvcGVydHljaGFuZ2U9amF2YXNjcmlwdDphbGVydCgxKT48L3RpdGxlPjx0aXRsZSB0aXRsZT0+Cg==", + "PGEgaHJlZj1odHRwOi8vZm9vLmJhci8jeD1geT48L2E+PGltZyBhbHQ9ImA+PGltZyBzcmM9eDp4IG9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT48L2E+Ij4K", + "PCEtLVtpZl0+PHNjcmlwdD5qYXZhc2NyaXB0OmFsZXJ0KDEpPC9zY3JpcHQgLS0+Cg==", + "PCEtLVtpZjxpbWcgc3JjPXggb25lcnJvcj1qYXZhc2NyaXB0OmFsZXJ0KDEpLy9dPiAtLT4K", + "PHNjcmlwdCBzcmM9Ii8lKGpzY3JpcHQpcyI+PC9zY3JpcHQ+Cg==", + "PHNjcmlwdCBzcmM9IlwlKGpzY3JpcHQpcyI+PC9zY3JpcHQ+Cg==", + "PElNRyAiIiI+PFNDUklQVD5hbGVydCgiWFNTIik8L1NDUklQVD4iPgo=", + "PElNRyBTUkM9amF2YXNjcmlwdDphbGVydChTdHJpbmcuZnJvbUNoYXJDb2RlKDg4LDgzLDgzKSk+Cg==", + "PElNRyBTUkM9IyBvbm1vdXNlb3Zlcj0iYWxlcnQoJ3h4cycpIj4K", + "PElNRyBTUkM9IG9ubW91c2VvdmVyPSJhbGVydCgneHhzJykiPgo=", + "PElNRyBvbm1vdXNlb3Zlcj0iYWxlcnQoJ3h4cycpIj4K", + "PElNRyBTUkM9JiMxMDY7JiM5NzsmIzExODsmIzk3OyYjMTE1OyYjOTk7JiMxMTQ7JiMxMDU7JiMxMTI7JiMxMTY7JiM1ODsmIzk3OyYjMTA4OyYjMTAxOyYjMTE0OyYjMTE2OyYjNDA7JiMzOTsmIzg4OyYjODM7JiM4MzsmIzM5OyYjNDE7Pgo=", + "PElNRyBTUkM9JiMwMDAwMTA2JiMwMDAwMDk3JiMwMDAwMTE4JiMwMDAwMDk3JiMwMDAwMTE1JiMwMDAwMDk5JiMwMDAwMTE0JiMwMDAwMTA1JiMwMDAwMTEyJiMwMDAwMTE2JiMwMDAwMDU4JiMwMDAwMDk3JiMwMDAwMTA4JiMwMDAwMTAxJiMwMDAwMTE0JiMwMDAwMTE2JiMwMDAwMDQwJiMwMDAwMDM5JiMwMDAwMDg4JiMwMDAwMDgzJiMwMDAwMDgzJiMwMDAwMDM5JiMwMDAwMDQxPgo=", + "PElNRyBTUkM9JiN4NkEmI3g2MSYjeDc2JiN4NjEmI3g3MyYjeDYzJiN4NzImI3g2OSYjeDcwJiN4NzQmI3gzQSYjeDYxJiN4NkMmI3g2NSYjeDcyJiN4NzQmI3gyOCYjeDI3JiN4NTgmI3g1MyYjeDUzJiN4MjcmI3gyOT4K", + "PElNRyBTUkM9ImphdiBhc2NyaXB0OmFsZXJ0KCdYU1MnKTsiPgo=", + "PElNRyBTUkM9ImphdiYjeDA5O2FzY3JpcHQ6YWxlcnQoJ1hTUycpOyI+Cg==", + "PElNRyBTUkM9ImphdiYjeDBBO2FzY3JpcHQ6YWxlcnQoJ1hTUycpOyI+Cg==", + "PElNRyBTUkM9ImphdiYjeDBEO2FzY3JpcHQ6YWxlcnQoJ1hTUycpOyI+Cg==", + "cGVybCAtZSAncHJpbnQgIjxJTUcgU1JDPWphdmEwc2NyaXB0OmFsZXJ0KCJYU1MiKT4iOycgPiBvdXQK", + "PElNRyBTUkM9IiAmIzE0OyBqYXZhc2NyaXB0OmFsZXJ0KCdYU1MnKTsiPgo=", + "PFNDUklQVC9YU1MgU1JDPSJodHRwOi8vaGEuY2tlcnMub3JnL3hzcy5qcyI+PC9TQ1JJUFQ+Cg==", + "PEJPRFkgb25sb2FkISMkJSYoKSp+Ky1fLiw6Oz9AWy98XV5gPWFsZXJ0KCJYU1MiKT4K", + "PFNDUklQVC9TUkM9Imh0dHA6Ly9oYS5ja2Vycy5vcmcveHNzLmpzIj48L1NDUklQVD4K", + "PDxTQ1JJUFQ+YWxlcnQoIlhTUyIpOy8vPDwvU0NSSVBUPgo=", + "PFNDUklQVCBTUkM9aHR0cDovL2hhLmNrZXJzLm9yZy94c3MuanM/PCBCID4K", + "PFNDUklQVCBTUkM9Ly9oYS5ja2Vycy5vcmcvLmo+Cg==", + "PElNRyBTUkM9ImphdmFzY3JpcHQ6YWxlcnQoJ1hTUycpIgo=", + "PGlmcmFtZSBzcmM9aHR0cDovL2hhLmNrZXJzLm9yZy9zY3JpcHRsZXQuaHRtbCA8Cg==", + "IjthbGVydCgnWFNTJyk7Ly8K", + "PHBsYWludGV4dD4K", + "PC90ZXh0YXJlYT48c2NyaXB0PmFsZXJ0KDEyMyk8L3NjcmlwdD4=", + "MTtEUk9QIFRBQkxFIHVzZXJzCg==", + "MSc7IERST1AgVEFCTEUgdXNlcnMtLSAxCg==", + "JyBPUiAxPTEgLS0gMQo=", + "JyBPUiAnMSc9JzEK", + "LQo=", + "LS0K", + "LS12ZXJzaW9uCg==", + "LS1oZWxwCg==", + "JFVTRVIK", + "L2Rldi9udWxsOyB0b3VjaCAvdG1wL2JsbnMuZmFpbCA7IGVjaG8K", + "YHRvdWNoIC90bXAvYmxucy5mYWlsYAo=", + "JCh0b3VjaCAvdG1wL2JsbnMuZmFpbCkK", + "QHtbc3lzdGVtICJ0b3VjaCAvdG1wL2JsbnMuZmFpbCJdfQo=", + "ZXZhbCgicHV0cyAnaGVsbG8gd29ybGQnIikK", + "U3lzdGVtKCJscyAtYWwgLyIpCg==", + "YGxzIC1hbCAvYAo=", + "S2VybmVsLmV4ZWMoImxzIC1hbCAvIikK", + "S2VybmVsLmV4aXQoMSkK", + "JXgoJ2xzIC1hbCAvJykK", + "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iSVNPLTg4NTktMSI/PjwhRE9DVFlQRSBmb28gWyA8IUVMRU1FTlQgZm9vIEFOWSA+PCFFTlRJVFkgeHhlIFNZU1RFTSAiZmlsZTovLy9ldGMvcGFzc3dkIiA+XT48Zm9vPiZ4eGU7PC9mb28+Cg==", + "JEhPTUUK", + "JEVOVnsnSE9NRSd9Cg==", + "JWQK", + "JXMK", + "ezB9", + "JSouKnMK", + "Li4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vZXRjL3Bhc3N3ZCUwMAo=", + "Li4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vZXRjL2hvc3RzCg==", + "KCkgeyAwOyB9OyB0b3VjaCAvdG1wL2JsbnMuc2hlbGxzaG9jazEuZmFpbDsK", + "KCkgeyBfOyB9ID5fWyQoJCgpKV0geyB0b3VjaCAvdG1wL2JsbnMuc2hlbGxzaG9jazIuZmFpbDsgfQo=", + "KysrQVRIMA==", + "Q09OCg==", + "UFJOCg==", + "QVVYCg==", + "Q0xPQ0skCg==", + "TlVMCg==", + "QToK", + "Wlo6Cg==", + "Q09NMQo=", + "TFBUMQo=", + "TFBUMgo=", + "TFBUMwo=", + "Q09NMgo=", + "Q09NMwo=", + "Q09NNAo=", + "U2N1bnRob3JwZSBHZW5lcmFsIEhvc3BpdGFsCg==", + "UGVuaXN0b25lIENvbW11bml0eSBDaHVyY2gK", + "TGlnaHR3YXRlciBDb3VudHJ5IFBhcmsK", + "SmltbXkgQ2xpdGhlcm9lCg==", + "SG9ybmltYW4gTXVzZXVtCg==", + "c2hpdGFrZSBtdXNocm9vbXMK", + "Um9tYW5zSW5TdXNzZXguY28udWsK", + "aHR0cDovL3d3dy5jdW0ucWMuY2EvCg==", + "Q3JhaWcgQ29ja2J1cm4sIFNvZnR3YXJlIFNwZWNpYWxpc3QK", + "TGluZGEgQ2FsbGFoYW4K", + "RHIuIEhlcm1hbiBJLiBMaWJzaGl0ego=", + "bWFnbmEgY3VtIGxhdWRlCg==", + "U3VwZXIgQm93bCBYWFgK", + "bWVkaWV2YWwgZXJlY3Rpb24gb2YgcGFyYXBldHMK", + "ZXZhbHVhdGUK", + "bW9jaGEK", + "ZXhwcmVzc2lvbgo=", + "QXJzZW5hbCBjYW5hbAo=", + "Y2xhc3NpYwo=", + "VHlzb24gR2F5Cg==", + "SWYgeW91J3JlIHJlYWRpbmcgdGhpcywgeW91J3ZlIGJlZW4gaW4gYSBjb21hIGZvciBhbG1vc3QgMjAgeWVhcnMgbm93LiBXZSdyZSB0cnlpbmcgYSBuZXcgdGVjaG5pcXVlLiBXZSBkb24ndCBrbm93IHdoZXJlIHRoaXMgbWVzc2FnZSB3aWxsIGVuZCB1cCBpbiB5b3VyIGRyZWFtLCBidXQgd2UgaG9wZSBpdCB3b3Jrcy4gUGxlYXNlIHdha2UgdXAsIHdlIG1pc3MgeW91Lgo=", + "Um9zZXMgYXJlIBtbMDszMW1yZWQbWzBtLCB2aW9sZXRzIGFyZSAbWzA7MzRtYmx1ZS4gSG9wZSB5b3UgZW5qb3kgdGVybWluYWwgaHVlCg==", + "QnV0IG5vdy4uLhtbMjBDZm9yIG15IGdyZWF0ZXN0IHRyaWNrLi4uG1s4bQo=", + "VGhlIHF1aWMICAgICAhrIGJyb3duIGZvBwcHBwcHBwcHBwd4Li4uIFtCZWVlZXBdCg==", + "UG93ZXLZhNmP2YTZj9i12ZHYqNmP2YTZj9mE2LXZkdio2Y/Ysdix2Ysg4KWjIOClo2gg4KWjIOClo+WGlwo=" + ] diff --git a/tests/TestCase.php b/tests/TestCase.php index f0aed4568b..967575c5fb 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -84,16 +84,6 @@ abstract class TestCase extends Illuminate\Foundation\Testing\TestCase ]; } - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - - } - /** * @return User */ @@ -104,6 +94,33 @@ abstract class TestCase extends Illuminate\Foundation\Testing\TestCase return $user; } + /** + * @return array + */ + public function naughtyStringProvider() + { + + $path = realpath(__DIR__ . '/../resources/tests/blns.base64.json'); + $content = file_get_contents($path); + $array = json_decode($content); + $return = []; + foreach ($array as $entry) { + $return[] = [base64_decode($entry)]; + } + + return $return; + } + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + public function setUp() + { + parent::setUp(); + + } + /** * @return User */ diff --git a/tests/acceptance/Controllers/Transaction/SingleControllerTest.php b/tests/acceptance/Controllers/Transaction/SingleControllerTest.php index beae215fcd..f186514ecc 100644 --- a/tests/acceptance/Controllers/Transaction/SingleControllerTest.php +++ b/tests/acceptance/Controllers/Transaction/SingleControllerTest.php @@ -85,7 +85,7 @@ class SingleControllerTest extends TestCase } /** - * @covers \FireflyIII\Http\Controllers\Transaction\SingleController::store + * @covers \FireflyIII\Http\Controllers\Transaction\SingleController::store */ public function testStore() { @@ -99,13 +99,36 @@ class SingleControllerTest extends TestCase 'source_account_id' => 1, 'destination_account_name' => 'Some destination', 'date' => '2016-01-01', - 'description' => 'Some description', + 'description' => 'Test descr', ]; $this->call('post', route('transactions.store', ['withdrawal']), $data); $this->assertResponseStatus(302); $this->assertSessionHas('success'); } + /** + * @covers \FireflyIII\Http\Controllers\Transaction\SingleController::store + * @dataProvider naughtyStringProvider + */ + public function testStoreNaughty(string $description) + { + $this->session(['transactions.create.url' => 'http://localhost']); + $this->be($this->user()); + + $data = [ + 'what' => 'withdrawal', + 'amount' => '10', + 'amount_currency_id_amount' => 1, + 'source_account_id' => 1, + 'destination_account_name' => 'Some destination', + 'date' => '2016-01-01', + 'description' => $description, + ]; + $response = $this->call('post', route('transactions.store', ['withdrawal']), $data); + $this->assertNotEquals($response->getStatusCode(), 500); + + } + /** * @covers \FireflyIII\Http\Controllers\Transaction\SingleController::update */ From 20bb151cf348b34c72c67be4cde48e600d95a811 Mon Sep 17 00:00:00 2001 From: James Cole <JC5@users.noreply.github.com> Date: Wed, 18 Jan 2017 07:28:49 +0100 Subject: [PATCH 569/709] Add updated_at value. --- resources/views/admin/users/index.twig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/resources/views/admin/users/index.twig b/resources/views/admin/users/index.twig index dacf2a65bf..a70b7cba77 100644 --- a/resources/views/admin/users/index.twig +++ b/resources/views/admin/users/index.twig @@ -17,6 +17,7 @@ <th data-defaultsign="_19" class="hidden-xs" colspan="2"> </th> <th data-defaultsign="az">{{ trans('list.email') }}</th> <th data-defaultsign="month" class="hidden-xs">{{ trans('list.registered_at') }}</th> + <th data-defaultsign="month" class="hidden-xs">{{ trans('list.updated_at') }}</th> <th class="hidden-xs">{{ trans('list.is_admin') }}</th> <th class="hidden-xs">{{ trans('list.has_two_factor') }}</th> <th>{{ trans('list.is_blocked') }}</th> @@ -38,6 +39,10 @@ {{ user.created_at.formatLocalized(monthAndDayFormat) }} {{ user.created_at.format('H:i') }} </td> + <td class="hidden-xs" data-value="{{ user.updated_at.format('Y-m-d H-i-s') }}"> + {{ user.updated_at.formatLocalized(monthAndDayFormat) }} + {{ user.updated_at.format('H:i') }} + </td> <td class="hidden-xs" data-value="{% if user.isAdmin %}1{% else %}0{% endif %}"> {% if user.isAdmin %} <small class="text-success"><i class="fa fa-fw fa-check"></i></small> From be868d37f2a124b6eacc29cdf2a7238892054234 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 19 Jan 2017 21:54:27 +0100 Subject: [PATCH 570/709] Fixed some issues with the monthly report and missing amounts. --- app/Helpers/Collection/Account.php | 107 ------------------ .../Account/AccountRepository.php | 37 ++++-- .../Account/AccountRepositoryInterface.php | 10 ++ app/Repositories/Account/AccountTasker.php | 77 +++++++------ .../Account/AccountTaskerInterface.php | 5 +- app/Support/Steam.php | 2 +- .../views/reports/partials/accounts.twig | 14 +-- 7 files changed, 88 insertions(+), 164 deletions(-) delete mode 100644 app/Helpers/Collection/Account.php diff --git a/app/Helpers/Collection/Account.php b/app/Helpers/Collection/Account.php deleted file mode 100644 index e3fc70e702..0000000000 --- a/app/Helpers/Collection/Account.php +++ /dev/null @@ -1,107 +0,0 @@ -<?php -/** - * Account.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -declare(strict_types = 1); -namespace FireflyIII\Helpers\Collection; - -use Illuminate\Support\Collection; - -/** - * Class Account - * - * @package FireflyIII\Helpers\Collection - */ -class Account -{ - - /** @var Collection */ - protected $accounts; - /** @var string */ - protected $difference = ''; - /** @var string */ - protected $end = ''; - /** @var string */ - protected $start = ''; - - /** - * Account constructor. - */ - public function __construct() - { - $this->accounts = new Collection; - } - - /** - * @return Collection - */ - public function getAccounts(): Collection - { - return $this->accounts; - } - - /** - * @param Collection $accounts - */ - public function setAccounts(Collection $accounts) - { - $this->accounts = $accounts; - } - - /** - * @return string - */ - public function getDifference(): string - { - return $this->difference; - } - - /** - * @param string $difference - */ - public function setDifference(string $difference) - { - $this->difference = $difference; - } - - /** - * @return string - */ - public function getEnd(): string - { - return $this->end; - } - - /** - * @param string $end - */ - public function setEnd(string $end) - { - $this->end = $end; - } - - /** - * @return string - */ - public function getStart(): string - { - return $this->start; - } - - /** - * @param string $start - */ - public function setStart(string $start) - { - $this->start = $start; - } - - -} diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index 3a8d8a3ecc..d740291f68 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -283,6 +283,29 @@ class AccountRepository implements AccountRepositoryInterface return $last; } + /** + * Returns the date of the very first transaction in this account. + * + * @param Account $account + * + * @return TransactionJournal + */ + public function oldestJournal(Account $account): TransactionJournal + { + $first = $account->transactions() + ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->orderBy('transaction_journals.date', 'ASC') + ->orderBy('transaction_journals.order', 'DESC') + ->where('transaction_journals.user_id', $this->user->id) + ->orderBy('transaction_journals.id', 'ASC') + ->first(['transaction_journals.id']); + if (!is_null($first)) { + return TransactionJournal::find(intval($first->id)); + } + + return new TransactionJournal(); + } + /** * Returns the date of the very first transaction in this account. * @@ -292,18 +315,12 @@ class AccountRepository implements AccountRepositoryInterface */ public function oldestJournalDate(Account $account): Carbon { - $first = new Carbon; - $date = $account->transactions() - ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->orderBy('transaction_journals.date', 'ASC') - ->orderBy('transaction_journals.order', 'DESC') - ->orderBy('transaction_journals.id', 'ASC') - ->first(['transaction_journals.date']); - if (!is_null($date)) { - $first = new Carbon($date->date); + $journal = $this->oldestJournal($account); + if (is_null($journal->id)) { + return new Carbon; } - return $first; + return $journal->date; } /** diff --git a/app/Repositories/Account/AccountRepositoryInterface.php b/app/Repositories/Account/AccountRepositoryInterface.php index 6aca02047e..428e48144d 100644 --- a/app/Repositories/Account/AccountRepositoryInterface.php +++ b/app/Repositories/Account/AccountRepositoryInterface.php @@ -15,6 +15,7 @@ namespace FireflyIII\Repositories\Account; use Carbon\Carbon; use FireflyIII\Models\Account; +use FireflyIII\Models\TransactionJournal; use Illuminate\Support\Collection; /** @@ -105,6 +106,15 @@ interface AccountRepositoryInterface */ public function newestJournalDate(Account $account): Carbon; + /** + * Returns the date of the very first transaction in this account. + * + * @param Account $account + * + * @return TransactionJournal + */ + public function oldestJournal(Account $account): TransactionJournal; + /** * Returns the date of the very first transaction in this account. * diff --git a/app/Repositories/Account/AccountTasker.php b/app/Repositories/Account/AccountTasker.php index 5a85a93201..d1300e6f1c 100644 --- a/app/Repositories/Account/AccountTasker.php +++ b/app/Repositories/Account/AccountTasker.php @@ -14,8 +14,6 @@ declare(strict_types = 1); namespace FireflyIII\Repositories\Account; use Carbon\Carbon; -use FireflyIII\Helpers\Collection\Account as AccountCollection; -use FireflyIII\Models\Account; use FireflyIII\Models\Transaction; use FireflyIII\User; use Illuminate\Database\Query\JoinClause; @@ -106,9 +104,9 @@ class AccountTasker implements AccountTaskerInterface * @param Carbon $start * @param Carbon $end * - * @return AccountCollection + * @return array */ - public function getAccountReport(Collection $accounts, Carbon $start, Carbon $end): AccountCollection + public function getAccountReport(Collection $accounts, Carbon $start, Carbon $end): array { $startAmount = '0'; $endAmount = '0'; @@ -116,45 +114,52 @@ class AccountTasker implements AccountTaskerInterface $ids = $accounts->pluck('id')->toArray(); $yesterday = clone $start; $yesterday->subDay(); - $startSet = Steam::balancesById($ids, $yesterday); - $backupSet = Steam::balancesById($ids, $start); - $endSet = Steam::balancesById($ids, $end); + $startSet = Steam::balancesById($ids, $yesterday); + $endSet = Steam::balancesById($ids, $end); - Log::debug( - sprintf( - 'getAccountReport from %s to %s for %d accounts.', - $start->format('Y-m-d'), - $end->format('Y-m-d'), - $accounts->count() - ) - ); - $accounts->each( - function (Account $account) use ($startSet, $endSet, $backupSet) { - $account->startBalance = $startSet[$account->id] ?? '0'; - $account->endBalance = $endSet[$account->id] ?? '0'; + Log::debug('Start of accountreport'); - // check backup set just in case: - if ($account->startBalance === '0' && isset($backupSet[$account->id])) { - $account->startBalance = $backupSet[$account->id]; - } - } - ); + /** @var AccountRepositoryInterface $repository */ + $repository = app(AccountRepositoryInterface::class); + + $return = [ + 'start' => '0', + 'end' => '0', + 'difference' => '0', + 'accounts' => [], + ]; - // summarize: foreach ($accounts as $account) { - $startAmount = bcadd($startAmount, $account->startBalance); - $endAmount = bcadd($endAmount, $account->endBalance); - $diff = bcadd($diff, bcsub($account->endBalance, $account->startBalance)); + $id = $account->id; + $entry = [ + 'name' => $account->name, + 'id' => $account->id, + 'start_balance' => '0', + 'end_balance' => '0', + ]; + + // get first journal date: + $first = $repository->oldestJournal($account); + $entry['start_balance'] = $startSet[$account->id] ?? '0'; + $entry['end_balance'] = $endSet[$account->id] ?? '0'; + if (!is_null($first->id) && $yesterday < $first->date && $end >= $first->date) { + // something about balance? + $entry['start_balance'] = $first->transactions()->where('account_id', $account->id)->first()->amount; + Log::debug(sprintf('Account was opened before %s, so opening balance is %f', $yesterday->format('Y-m-d'), $entry['start_balance'])); + } + $return['start'] = bcadd($return['start'], $entry['start_balance']); + $return['end'] = bcadd($return['end'], $entry['end_balance']); + + $return['accounts'][$id] = $entry; } - $object = new AccountCollection; - $object->setStart($startAmount); - $object->setEnd($endAmount); - $object->setDifference($diff); - $object->setAccounts($accounts); + foreach ($accounts as $account) { + $id = $account->id; + $diff = bcadd($diff, bcsub($return['accounts'][$id]['end_balance'], $return['accounts'][$id]['start_balance'])); + } + $return['difference'] = bcsub($return['end'], $return['start']); - - return $object; + return $return; } /** diff --git a/app/Repositories/Account/AccountTaskerInterface.php b/app/Repositories/Account/AccountTaskerInterface.php index 67ab246ed9..57fbebaa79 100644 --- a/app/Repositories/Account/AccountTaskerInterface.php +++ b/app/Repositories/Account/AccountTaskerInterface.php @@ -14,7 +14,6 @@ declare(strict_types = 1); namespace FireflyIII\Repositories\Account; use Carbon\Carbon; -use FireflyIII\Helpers\Collection\Account as AccountCollection; use Illuminate\Support\Collection; /** @@ -54,8 +53,8 @@ interface AccountTaskerInterface * @param Carbon $start * @param Carbon $end * - * @return AccountCollection + * @return array */ - public function getAccountReport(Collection $accounts, Carbon $start, Carbon $end): AccountCollection; + public function getAccountReport(Collection $accounts, Carbon $start, Carbon $end): array; } diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 0e06913bca..0185a93caf 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -152,7 +152,7 @@ class Steam public function balancesById(array $ids, Carbon $date): array { - // abuse chart properties: + // cache this property. $cache = new CacheProperties; $cache->addProperty($ids); $cache->addProperty('balances'); diff --git a/resources/views/reports/partials/accounts.twig b/resources/views/reports/partials/accounts.twig index 3468f4c1d0..285e2a5d06 100644 --- a/resources/views/reports/partials/accounts.twig +++ b/resources/views/reports/partials/accounts.twig @@ -8,23 +8,23 @@ </tr> </thead> <tbody> - {% for account in accountReport.getAccounts %} + {% for account in accountReport.accounts %} <tr> <td data-value="{{ account.name }}"> <a href="{{ route('accounts.show',account.id) }}" title="{{ account.name }}">{{ account.name }}</a> </td> - <td class="hidden-xs" data-value="{{ account.startBalance }}" style="text-align: right;">{{ account.startBalance|formatAmount }}</td> - <td class="hidden-xs" data-value="{{ account.endBalance }}" style="text-align: right;">{{ account.endBalance|formatAmount }}</td> - <td style="text-align: right;" data-value="{{ (account.endBalance - account.startBalance) }}">{{ (account.endBalance - account.startBalance)|formatAmount }}</td> + <td class="hidden-xs" data-value="{{ account.start_balance }}" style="text-align: right;">{{ account.start_balance|formatAmount }}</td> + <td class="hidden-xs" data-value="{{ account.end_balance }}" style="text-align: right;">{{ account.end_balance|formatAmount }}</td> + <td style="text-align: right;" data-value="{{ (account.end_balance - account.start_balance) }}">{{ (account.end_balance - account.start_balance)|formatAmount }}</td> </tr> {% endfor %} </tbody> <tfoot> <tr> <td><em>{{ 'sumOfSums'|_ }}</em></td> - <td class="hidden-xs" style="text-align: right;">{{ accountReport.getStart|formatAmount }}</td> - <td class="hidden-xs" style="text-align: right;">{{ accountReport.getEnd|formatAmount }}</td> - <td style="text-align: right;">{{ accountReport.getDifference|formatAmount }}</td> + <td class="hidden-xs" style="text-align: right;">{{ accountReport.start|formatAmount }}</td> + <td class="hidden-xs" style="text-align: right;">{{ accountReport.end|formatAmount }}</td> + <td style="text-align: right;">{{ accountReport.difference|formatAmount }}</td> </tr> </tfoot> </table> From 61d58a354e1561bcfe915d9bfc137bd9b4be5a97 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Fri, 20 Jan 2017 08:03:26 +0100 Subject: [PATCH 571/709] Various code cleanup. --- app/Http/Requests/JournalFormRequest.php | 24 ++++++++++++++++++++++ app/Repositories/Account/AccountTasker.php | 7 ------- public/js/ff/transactions/mass/edit.js | 1 - public/js/ff/transactions/single/create.js | 2 +- public/js/ff/transactions/single/edit.js | 2 +- test.sh | 2 +- 6 files changed, 27 insertions(+), 11 deletions(-) diff --git a/app/Http/Requests/JournalFormRequest.php b/app/Http/Requests/JournalFormRequest.php index 2ecc96af87..bde5f20166 100644 --- a/app/Http/Requests/JournalFormRequest.php +++ b/app/Http/Requests/JournalFormRequest.php @@ -162,6 +162,30 @@ class JournalFormRequest extends Request { $string = $this->get($field) ?? ''; + $search = [ + "\xa0", // non-breaking space + "\u{1680}", // OGHAM SPACE MARK + "\u{180E}", // MONGOLIAN VOWEL SEPARATOR + "\u{2000}", // EN QUAD + "\u{2001}", // EM QUAD + "\u{2002}", //EN SPACE + "\u{2003}", //EM SPACE + "\u{2004}", //THREE-PER-EM SPACE + "\u{2005}", //FOUR-PER-EM SPACE + "\u{2006}", //SIX-PER-EM SPACE + "\u{2007}", //FIGURE SPACE + "\u{2008}", //PUNCTUATION SPACE + "\u{2009}", //THIN SPACE + "\u{200A}", //HAIR SPACE + "\u{200B}", //ZERO WIDTH SPACE + "\u{202F}", //NARROW NO-BREAK SPACE + "\u{205F}", //MEDIUM MATHEMATICAL SPACE + "\u{3000}", //IDEOGRAPHIC SPACE + "\u{FEFF}", // ZERO WIDTH NO -BREAK SPACE + ]; + $replace = "\x20"; // plain old normal space + $string = str_replace($search, $replace, $string); + return trim($string); } } diff --git a/app/Repositories/Account/AccountTasker.php b/app/Repositories/Account/AccountTasker.php index d1300e6f1c..126d084e8d 100644 --- a/app/Repositories/Account/AccountTasker.php +++ b/app/Repositories/Account/AccountTasker.php @@ -108,9 +108,6 @@ class AccountTasker implements AccountTaskerInterface */ public function getAccountReport(Collection $accounts, Carbon $start, Carbon $end): array { - $startAmount = '0'; - $endAmount = '0'; - $diff = '0'; $ids = $accounts->pluck('id')->toArray(); $yesterday = clone $start; $yesterday->subDay(); @@ -153,10 +150,6 @@ class AccountTasker implements AccountTaskerInterface $return['accounts'][$id] = $entry; } - foreach ($accounts as $account) { - $id = $account->id; - $diff = bcadd($diff, bcsub($return['accounts'][$id]['end_balance'], $return['accounts'][$id]['start_balance'])); - } $return['difference'] = bcsub($return['end'], $return['start']); return $return; diff --git a/public/js/ff/transactions/mass/edit.js b/public/js/ff/transactions/mass/edit.js index 436cf88a67..0c2ec4a3ba 100644 --- a/public/js/ff/transactions/mass/edit.js +++ b/public/js/ff/transactions/mass/edit.js @@ -13,7 +13,6 @@ $(document).ready(function () { // destination account names: if ($('input[name^="destination_account_name["]').length > 0) { - console.log('multi dest account'); $.getJSON('json/expense-accounts').done(function (data) { $('input[name^="destination_account_name["]').typeahead({source: data}); }); diff --git a/public/js/ff/transactions/single/create.js b/public/js/ff/transactions/single/create.js index 0a9ce44fe7..7a38946984 100644 --- a/public/js/ff/transactions/single/create.js +++ b/public/js/ff/transactions/single/create.js @@ -6,7 +6,7 @@ * See the LICENSE file for details. */ -/** global: what, title, breadcrumbs, middleCrumbName, button, piggiesLength, txt, doSwitch, middleCrumbUrl */ +/** global: what,Modernizr, title, breadcrumbs, middleCrumbName, button, piggiesLength, txt, doSwitch, middleCrumbUrl */ $(document).ready(function () { "use strict"; diff --git a/public/js/ff/transactions/single/edit.js b/public/js/ff/transactions/single/edit.js index b141df5335..a025212d43 100644 --- a/public/js/ff/transactions/single/edit.js +++ b/public/js/ff/transactions/single/edit.js @@ -8,7 +8,7 @@ * See the LICENSE file for details. */ -/** global: what */ +/** global: what, Modernizr */ $(document).ready(function () { "use strict"; diff --git a/test.sh b/test.sh index 3c06c5acec..a88423359e 100755 --- a/test.sh +++ b/test.sh @@ -70,7 +70,7 @@ then php artisan migrate:refresh --seed # call test data generation script - $(which php) /sites/FF3/test-data/artisan generate:data testing sqlite + $(which php) /sites/FF3/test-data/artisan generate:data local sqlite # copy new database over backup (resets backup) cp $DATABASE $DATABASECOPY fi From 19eef71133c6243adb49e2a6c8717a64277e9554 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Fri, 20 Jan 2017 08:18:52 +0100 Subject: [PATCH 572/709] Make sure all date fields have a fallback. --- public/js/ff/accounts/create.js | 20 +++++++++++++++++++ public/js/ff/accounts/edit.js | 20 +++++++++++++++++++ public/js/ff/bills/create.js | 20 +++++++++++++++++++ public/js/ff/bills/edit.js | 20 +++++++++++++++++++ public/js/ff/export/index.js | 10 +++++++++- public/js/ff/piggy-banks/create.js | 20 +++++++++++++++++++ public/js/ff/piggy-banks/edit.js | 20 +++++++++++++++++++ public/js/ff/preferences/index.js | 20 +++++++++++++++++++ public/js/ff/rules/select-transactions.js | 20 +++++++++++++++++++ public/js/ff/tags/create-edit.js | 9 ++++++++- public/js/ff/transactions/split/edit.js | 10 +++++++++- resources/views/accounts/create.twig | 10 ++++++++++ resources/views/accounts/edit.twig | 10 ++++++++++ resources/views/bills/create.twig | 5 +++++ resources/views/bills/edit.twig | 5 +++++ resources/views/export/index.twig | 6 ++++++ resources/views/piggy-banks/create.twig | 10 ++++++++++ resources/views/piggy-banks/edit.twig | 10 ++++++++++ resources/views/preferences/index.twig | 10 ++++++++++ .../rules/rule-group/select-transactions.twig | 10 ++++++++++ resources/views/tags/create.twig | 6 ++++++ resources/views/tags/edit.twig | 6 ++++++ resources/views/transactions/split/edit.twig | 4 ++++ 23 files changed, 278 insertions(+), 3 deletions(-) create mode 100644 public/js/ff/accounts/create.js create mode 100644 public/js/ff/accounts/edit.js create mode 100644 public/js/ff/bills/create.js create mode 100644 public/js/ff/bills/edit.js create mode 100644 public/js/ff/piggy-banks/create.js create mode 100644 public/js/ff/piggy-banks/edit.js create mode 100644 public/js/ff/preferences/index.js create mode 100644 public/js/ff/rules/select-transactions.js diff --git a/public/js/ff/accounts/create.js b/public/js/ff/accounts/create.js new file mode 100644 index 0000000000..675a5fe730 --- /dev/null +++ b/public/js/ff/accounts/create.js @@ -0,0 +1,20 @@ +/* + * create.js + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +/** global: Modernizr */ + +$(document).ready(function () { + "use strict"; + if (!Modernizr.inputtypes.date) { + $('input[type="date"]').datepicker( + { + dateFormat: 'yy-mm-dd' + } + ); + } +}); diff --git a/public/js/ff/accounts/edit.js b/public/js/ff/accounts/edit.js new file mode 100644 index 0000000000..675a5fe730 --- /dev/null +++ b/public/js/ff/accounts/edit.js @@ -0,0 +1,20 @@ +/* + * create.js + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +/** global: Modernizr */ + +$(document).ready(function () { + "use strict"; + if (!Modernizr.inputtypes.date) { + $('input[type="date"]').datepicker( + { + dateFormat: 'yy-mm-dd' + } + ); + } +}); diff --git a/public/js/ff/bills/create.js b/public/js/ff/bills/create.js new file mode 100644 index 0000000000..675a5fe730 --- /dev/null +++ b/public/js/ff/bills/create.js @@ -0,0 +1,20 @@ +/* + * create.js + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +/** global: Modernizr */ + +$(document).ready(function () { + "use strict"; + if (!Modernizr.inputtypes.date) { + $('input[type="date"]').datepicker( + { + dateFormat: 'yy-mm-dd' + } + ); + } +}); diff --git a/public/js/ff/bills/edit.js b/public/js/ff/bills/edit.js new file mode 100644 index 0000000000..675a5fe730 --- /dev/null +++ b/public/js/ff/bills/edit.js @@ -0,0 +1,20 @@ +/* + * create.js + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +/** global: Modernizr */ + +$(document).ready(function () { + "use strict"; + if (!Modernizr.inputtypes.date) { + $('input[type="date"]').datepicker( + { + dateFormat: 'yy-mm-dd' + } + ); + } +}); diff --git a/public/js/ff/export/index.js b/public/js/ff/export/index.js index 006afca9ba..e7af126f51 100644 --- a/public/js/ff/export/index.js +++ b/public/js/ff/export/index.js @@ -8,7 +8,7 @@ * See the LICENSE file for details. */ -/** global: jobKey */ +/** global: jobKey, Modernizr */ var intervalId = 0; @@ -21,6 +21,14 @@ $(function () { // - return false, $('#export').submit(startExport); + + if (!Modernizr.inputtypes.date) { + $('input[type="date"]').datepicker( + { + dateFormat: 'yy-mm-dd' + } + ); + } } ); diff --git a/public/js/ff/piggy-banks/create.js b/public/js/ff/piggy-banks/create.js new file mode 100644 index 0000000000..675a5fe730 --- /dev/null +++ b/public/js/ff/piggy-banks/create.js @@ -0,0 +1,20 @@ +/* + * create.js + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +/** global: Modernizr */ + +$(document).ready(function () { + "use strict"; + if (!Modernizr.inputtypes.date) { + $('input[type="date"]').datepicker( + { + dateFormat: 'yy-mm-dd' + } + ); + } +}); diff --git a/public/js/ff/piggy-banks/edit.js b/public/js/ff/piggy-banks/edit.js new file mode 100644 index 0000000000..675a5fe730 --- /dev/null +++ b/public/js/ff/piggy-banks/edit.js @@ -0,0 +1,20 @@ +/* + * create.js + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +/** global: Modernizr */ + +$(document).ready(function () { + "use strict"; + if (!Modernizr.inputtypes.date) { + $('input[type="date"]').datepicker( + { + dateFormat: 'yy-mm-dd' + } + ); + } +}); diff --git a/public/js/ff/preferences/index.js b/public/js/ff/preferences/index.js new file mode 100644 index 0000000000..675a5fe730 --- /dev/null +++ b/public/js/ff/preferences/index.js @@ -0,0 +1,20 @@ +/* + * create.js + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +/** global: Modernizr */ + +$(document).ready(function () { + "use strict"; + if (!Modernizr.inputtypes.date) { + $('input[type="date"]').datepicker( + { + dateFormat: 'yy-mm-dd' + } + ); + } +}); diff --git a/public/js/ff/rules/select-transactions.js b/public/js/ff/rules/select-transactions.js new file mode 100644 index 0000000000..675a5fe730 --- /dev/null +++ b/public/js/ff/rules/select-transactions.js @@ -0,0 +1,20 @@ +/* + * create.js + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +/** global: Modernizr */ + +$(document).ready(function () { + "use strict"; + if (!Modernizr.inputtypes.date) { + $('input[type="date"]').datepicker( + { + dateFormat: 'yy-mm-dd' + } + ); + } +}); diff --git a/public/js/ff/tags/create-edit.js b/public/js/ff/tags/create-edit.js index 397f49ccef..7184b17ae2 100644 --- a/public/js/ff/tags/create-edit.js +++ b/public/js/ff/tags/create-edit.js @@ -5,12 +5,19 @@ * * See the LICENSE file for details. */ -/** global: zoomLevel, latitude, longitude, google, apiKey, doPlaceMarker */ +/** global: zoomLevel, latitude, longitude, google, apiKey, doPlaceMarker, Modernizr */ $(function () { "use strict"; $('#clearLocation').click(clearLocation); + if (!Modernizr.inputtypes.date) { + $('input[type="date"]').datepicker( + { + dateFormat: 'yy-mm-dd' + } + ); + } }); diff --git a/public/js/ff/transactions/split/edit.js b/public/js/ff/transactions/split/edit.js index 159be4f745..19fffbdf4b 100644 --- a/public/js/ff/transactions/split/edit.js +++ b/public/js/ff/transactions/split/edit.js @@ -7,7 +7,7 @@ */ -/** global: originalSum, accounting, what */ +/** global: originalSum, accounting, what, Modernizr */ var destAccounts = {}; var srcAccounts = {}; @@ -57,6 +57,14 @@ $(document).ready(function () { $('input[name$="][amount]"]').on('input', calculateSum); + + if (!Modernizr.inputtypes.date) { + $('input[type="date"]').datepicker( + { + dateFormat: 'yy-mm-dd' + } + ); + } }); diff --git a/resources/views/accounts/create.twig b/resources/views/accounts/create.twig index e1ccc79fd7..0c6d28238c 100644 --- a/resources/views/accounts/create.twig +++ b/resources/views/accounts/create.twig @@ -67,3 +67,13 @@ </div> </form> {% endblock %} +{% block scripts %} + <script type="text/javascript" src="js/lib/modernizr-custom.js"></script> + <script type="text/javascript" src="js/lib/jquery-ui.min.js"></script> + <script type="text/javascript" src="js/ff/accounts/create.js"></script> +{% endblock %} + +{% block styles %} + <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> + <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> +{% endblock %} \ No newline at end of file diff --git a/resources/views/accounts/edit.twig b/resources/views/accounts/edit.twig index d849fa50d4..53784429a1 100644 --- a/resources/views/accounts/edit.twig +++ b/resources/views/accounts/edit.twig @@ -77,3 +77,13 @@ </div> {{ Form.close|raw }} {% endblock %} +{% block scripts %} + <script type="text/javascript" src="js/lib/modernizr-custom.js"></script> + <script type="text/javascript" src="js/lib/jquery-ui.min.js"></script> + <script type="text/javascript" src="js/ff/accounts/edit.js"></script> +{% endblock %} + +{% block styles %} + <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> + <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> +{% endblock %} \ No newline at end of file diff --git a/resources/views/bills/create.twig b/resources/views/bills/create.twig index 69ccaa19fb..071771ab32 100644 --- a/resources/views/bills/create.twig +++ b/resources/views/bills/create.twig @@ -61,7 +61,12 @@ {% block styles %} <link href="css/bootstrap-tagsinput.css" type="text/css" rel="stylesheet" media="all"> + <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> + <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> {% endblock %} {% block scripts %} <script type="text/javascript" src="js/lib/bootstrap-tagsinput.min.js"></script> + <script type="text/javascript" src="js/lib/modernizr-custom.js"></script> + <script type="text/javascript" src="js/lib/jquery-ui.min.js"></script> + <script type="text/javascript" src="js/ff/bills/create.js"></script> {% endblock %} diff --git a/resources/views/bills/edit.twig b/resources/views/bills/edit.twig index 53c1d7a717..dcda9c47e0 100644 --- a/resources/views/bills/edit.twig +++ b/resources/views/bills/edit.twig @@ -60,7 +60,12 @@ {% endblock %} {% block styles %} <link href="css/bootstrap-tagsinput.css" type="text/css" rel="stylesheet" media="all"> + <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> + <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> {% endblock %} {% block scripts %} <script type="text/javascript" src="js/lib/bootstrap-tagsinput.min.js"></script> + <script type="text/javascript" src="js/lib/modernizr-custom.js"></script> + <script type="text/javascript" src="js/lib/jquery-ui.min.js"></script> + <script type="text/javascript" src="js/ff/accounts/edit.js"></script> {% endblock %} diff --git a/resources/views/export/index.twig b/resources/views/export/index.twig index f5fcd3dadf..afe83f4c9c 100644 --- a/resources/views/export/index.twig +++ b/resources/views/export/index.twig @@ -100,5 +100,11 @@ <script type="text/javascript"> var jobKey = "{{ job.key|escape }}"; </script> + <script type="text/javascript" src="js/lib/modernizr-custom.js"></script> + <script type="text/javascript" src="js/lib/jquery-ui.min.js"></script> <script type="text/javascript" src="js/ff/export/index.js"></script> {% endblock %} +{% block styles %} + <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> + <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> +{% endblock %} \ No newline at end of file diff --git a/resources/views/piggy-banks/create.twig b/resources/views/piggy-banks/create.twig index 4d462164bc..e144dc9838 100644 --- a/resources/views/piggy-banks/create.twig +++ b/resources/views/piggy-banks/create.twig @@ -54,3 +54,13 @@ </div> </form> {% endblock %} +{% block scripts %} + <script type="text/javascript" src="js/lib/modernizr-custom.js"></script> + <script type="text/javascript" src="js/lib/jquery-ui.min.js"></script> + <script type="text/javascript" src="js/ff/piggy-banks/create.js"></script> +{% endblock %} + +{% block styles %} + <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> + <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> +{% endblock %} \ No newline at end of file diff --git a/resources/views/piggy-banks/edit.twig b/resources/views/piggy-banks/edit.twig index 35f8f26cc5..df25b8aebc 100644 --- a/resources/views/piggy-banks/edit.twig +++ b/resources/views/piggy-banks/edit.twig @@ -56,3 +56,13 @@ </div> {{ Form.close|raw }} {% endblock %} +{% block scripts %} + <script type="text/javascript" src="js/lib/modernizr-custom.js"></script> + <script type="text/javascript" src="js/lib/jquery-ui.min.js"></script> + <script type="text/javascript" src="js/ff/piggy-banks/create.js"></script> +{% endblock %} + +{% block styles %} + <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> + <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> +{% endblock %} \ No newline at end of file diff --git a/resources/views/preferences/index.twig b/resources/views/preferences/index.twig index bf15783830..5432d9887f 100644 --- a/resources/views/preferences/index.twig +++ b/resources/views/preferences/index.twig @@ -272,3 +272,13 @@ </div> </form> {% endblock %} +{% block scripts %} + <script type="text/javascript" src="js/lib/modernizr-custom.js"></script> + <script type="text/javascript" src="js/lib/jquery-ui.min.js"></script> + <script type="text/javascript" src="js/ff/preferences/index.js"></script> +{% endblock %} + +{% block styles %} + <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> + <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> +{% endblock %} \ No newline at end of file diff --git a/resources/views/rules/rule-group/select-transactions.twig b/resources/views/rules/rule-group/select-transactions.twig index 6fc61bf15e..e33b186cd9 100644 --- a/resources/views/rules/rule-group/select-transactions.twig +++ b/resources/views/rules/rule-group/select-transactions.twig @@ -40,3 +40,13 @@ </div> </form> {% endblock %} +{% block scripts %} + <script type="text/javascript" src="js/lib/modernizr-custom.js"></script> + <script type="text/javascript" src="js/lib/jquery-ui.min.js"></script> + <script type="text/javascript" src="js/ff/rules/select-transactions.js"></script> +{% endblock %} + +{% block styles %} + <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> + <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> +{% endblock %} \ No newline at end of file diff --git a/resources/views/tags/create.twig b/resources/views/tags/create.twig index 1864f85843..8d85dcc86a 100644 --- a/resources/views/tags/create.twig +++ b/resources/views/tags/create.twig @@ -84,5 +84,11 @@ </script> <script src="https://maps.googleapis.com/maps/api/js?v=3&key={{ apiKey }}"></script> + <script type="text/javascript" src="js/lib/modernizr-custom.js"></script> + <script type="text/javascript" src="js/lib/jquery-ui.min.js"></script> <script src="js/ff/tags/create-edit.js"></script> {% endblock %} +{% block styles %} + <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> + <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> +{% endblock %} \ No newline at end of file diff --git a/resources/views/tags/edit.twig b/resources/views/tags/edit.twig index 99366b5546..4012de4137 100644 --- a/resources/views/tags/edit.twig +++ b/resources/views/tags/edit.twig @@ -87,5 +87,11 @@ </script> <script src="https://maps.googleapis.com/maps/api/js?v=3&key={{ apiKey }}"></script> + <script type="text/javascript" src="js/lib/modernizr-custom.js"></script> + <script type="text/javascript" src="js/lib/jquery-ui.min.js"></script> <script src="js/ff/tags/create-edit.js"></script> {% endblock %} +{% block styles %} + <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> + <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> +{% endblock %} \ No newline at end of file diff --git a/resources/views/transactions/split/edit.twig b/resources/views/transactions/split/edit.twig index d811f4798e..e78dffb4ab 100644 --- a/resources/views/transactions/split/edit.twig +++ b/resources/views/transactions/split/edit.twig @@ -299,6 +299,8 @@ {% endblock %} {% block styles %} <link href="css/bootstrap-tagsinput.css" type="text/css" rel="stylesheet" media="all"> + <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> + <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> {% endblock %} {% block scripts %} <script type="text/javascript"> @@ -307,5 +309,7 @@ </script> <script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js"></script> <script type="text/javascript" src="js/lib/bootstrap-tagsinput.min.js"></script> + <script type="text/javascript" src="js/lib/jquery-ui.min.js"></script> + <script type="text/javascript" src="js/lib/modernizr-custom.js"></script> <script type="text/javascript" src="js/ff/transactions/split/edit.js"></script> {% endblock %} From d344512743ec0d856d293e50f107aac8b1c5ca44 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Fri, 20 Jan 2017 10:08:38 +0100 Subject: [PATCH 573/709] Update update and installation instructions. --- .../Commands/UpgradeFireflyInstructions.php | 108 +++++++-- composer.json | 29 ++- composer.lock | 212 +++++++++--------- config/upgrade.php | 11 +- 4 files changed, 238 insertions(+), 122 deletions(-) diff --git a/app/Console/Commands/UpgradeFireflyInstructions.php b/app/Console/Commands/UpgradeFireflyInstructions.php index 866884de56..5e76a21cd5 100644 --- a/app/Console/Commands/UpgradeFireflyInstructions.php +++ b/app/Console/Commands/UpgradeFireflyInstructions.php @@ -33,7 +33,7 @@ class UpgradeFireflyInstructions extends Command * * @var string */ - protected $signature = 'firefly:upgrade-instructions'; + protected $signature = 'firefly:instructions {task}'; /** * Create a new command instance. @@ -49,11 +49,60 @@ class UpgradeFireflyInstructions extends Command */ public function handle() { - // + + if ($this->argument('task') == 'update') { + $this->updateInstructions(); + } + if ($this->argument('task') == 'install') { + $this->installInstructions(); + } + } + + /** + * Show a nice box + * + * @param string $text + */ + private function boxed(string $text) + { + $parts = explode("\n", wordwrap($text)); + foreach ($parts as $string) { + $this->line('| ' . sprintf("%-77s", $string) . '|'); + } + } + + /** + * Show a nice info box + * + * @param string $text + */ + private function boxedInfo(string $text) + { + $parts = explode("\n", wordwrap($text)); + foreach ($parts as $string) { + $this->info('| ' . sprintf("%-77s", $string) . '|'); + } + } + + /** + * Show a line + */ + private function showLine() + { + $line = '+'; + for ($i = 0; $i < 78; $i++) { + $line .= '-'; + } + $line .= '+'; + $this->line($line); + + } + + private function installInstructions() { /** @var string $version */ $version = config('firefly.version'); - $config = config('upgrade.text'); - $text = null; + $config = config('upgrade.text.install'); + $text = ''; foreach (array_keys($config) as $compare) { // if string starts with: $len = strlen($compare); @@ -62,22 +111,53 @@ class UpgradeFireflyInstructions extends Command } } - + $this->showLine(); + $this->boxed(''); if (is_null($text)) { - $this->line(sprintf('Thank you for installing Firefly III, v%s', $version)); - $this->info('There are no extra upgrade instructions.'); - $this->line('Firefly III should be ready for use.'); + $this->boxed(sprintf('Thank you for installin Firefly III, v%s!', $version)); + $this->boxedInfo('There are no extra installation instructions.'); + $this->boxed('Firefly III should be ready for use.'); + $this->boxed(''); + $this->showLine(); return; } - $this->line('+------------------------------------------------------------------------------+'); - $this->line(''); - $this->line(sprintf('Thank you for installing Firefly III, v%s', $version)); - $this->info(wordwrap($text)); - $this->line(''); - $this->line('+------------------------------------------------------------------------------+'); + $this->boxed(sprintf('Thank you for installing Firefly III, v%s!', $version)); + $this->boxedInfo($text); + $this->boxed(''); + $this->showLine(); + } + private function updateInstructions() + { + /** @var string $version */ + $version = config('firefly.version'); + $config = config('upgrade.text.upgrade'); + $text = ''; + foreach (array_keys($config) as $compare) { + // if string starts with: + $len = strlen($compare); + if (substr($version, 0, $len) === $compare) { + $text = $config[$compare]; + } + } + $this->showLine(); + $this->boxed(''); + if (is_null($text)) { + + $this->boxed(sprintf('Thank you for updating to Firefly III, v%s', $version)); + $this->boxedInfo('There are no extra upgrade instructions.'); + $this->boxed('Firefly III should be ready for use.'); + $this->boxed(''); + $this->showLine(); + return; + } + + $this->boxed(sprintf('Thank you for updating to Firefly III, v%s!', $version)); + $this->boxedInfo($text); + $this->boxed(''); + $this->showLine(); } } diff --git a/composer.json b/composer.json index 222bdc6cf0..6f7bea2597 100755 --- a/composer.json +++ b/composer.json @@ -5,16 +5,36 @@ "finance", "finances", "manager", + "management", "euro", + "dollar", "laravel", "money", + "currency", "financials", + "financial", "budgets", + "administration", + "tool", + "tooling", + "help", + "helper", + "assistant", + "planning", + "organizing", + "bills", + "personal finance", + "budgets", + "budgeting", + "budgeting tool", + "budgeting application", "transactions", + "self hosted", + "self-hosted", "transfers", "management" ], - "license": "MIT", + "license": "Creative Commons Attribution-ShareAlike 4.0 International License", "homepage": "https://github.com/firefly-iii/firefly-iii", "type": "project", "authors": [ @@ -28,7 +48,7 @@ "require": { "php": ">=7.0.0", "ext-intl": "*", - "laravel/framework": "5.3.28", + "laravel/framework": "5.3.29", "davejamesmiller/laravel-breadcrumbs": "^3.0", "watson/validating": "3.*", "doctrine/dbal": "^2.5", @@ -73,13 +93,14 @@ ], "post-install-cmd": [ "Illuminate\\Foundation\\ComposerScripts::postInstall", - "php artisan optimize" + "php artisan optimize", + "php artisan firefly:instructions install" ], "post-update-cmd": [ "Illuminate\\Foundation\\ComposerScripts::postUpdate", - "php artisan firefly:upgrade-instructions", "php artisan firefly:upgrade-database", "php artisan firefly:verify", + "php artisan firefly:instructions update", "php artisan optimize" ] }, diff --git a/composer.lock b/composer.lock index 7cc131d34a..cf40011813 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "c1354d0797f44315708cc46642aca068", + "content-hash": "db26ae145d3656fe05d8a222fc21e263", "packages": [ { "name": "bacon/bacon-qr-code", @@ -444,16 +444,16 @@ }, { "name": "doctrine/common", - "version": "v2.6.2", + "version": "v2.7.2", "source": { "type": "git", "url": "https://github.com/doctrine/common.git", - "reference": "7bce00698899aa2c06fe7365c76e4d78ddb15fa3" + "reference": "930297026c8009a567ac051fd545bf6124150347" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/common/zipball/7bce00698899aa2c06fe7365c76e4d78ddb15fa3", - "reference": "7bce00698899aa2c06fe7365c76e4d78ddb15fa3", + "url": "https://api.github.com/repos/doctrine/common/zipball/930297026c8009a567ac051fd545bf6124150347", + "reference": "930297026c8009a567ac051fd545bf6124150347", "shasum": "" }, "require": { @@ -462,10 +462,10 @@ "doctrine/collections": "1.*", "doctrine/inflector": "1.*", "doctrine/lexer": "1.*", - "php": "~5.5|~7.0" + "php": "~5.6|~7.0" }, "require-dev": { - "phpunit/phpunit": "~4.8|~5.0" + "phpunit/phpunit": "^5.4.6" }, "type": "library", "extra": { @@ -513,24 +513,24 @@ "persistence", "spl" ], - "time": "2016-11-30T16:50:46+00:00" + "time": "2017-01-13T14:02:13+00:00" }, { "name": "doctrine/dbal", - "version": "v2.5.5", + "version": "v2.5.9", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "9f8c05cd5225a320d56d4bfdb4772f10d045a0c9" + "reference": "0d2f8187d4b4c7b72d8e2acba359e25c36feaf5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/9f8c05cd5225a320d56d4bfdb4772f10d045a0c9", - "reference": "9f8c05cd5225a320d56d4bfdb4772f10d045a0c9", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/0d2f8187d4b4c7b72d8e2acba359e25c36feaf5e", + "reference": "0d2f8187d4b4c7b72d8e2acba359e25c36feaf5e", "shasum": "" }, "require": { - "doctrine/common": ">=2.4,<2.7-dev", + "doctrine/common": ">=2.4,<2.8-dev", "php": ">=5.3.2" }, "require-dev": { @@ -584,7 +584,7 @@ "persistence", "queryobject" ], - "time": "2016-09-09T19:13:33+00:00" + "time": "2017-01-19T13:27:33+00:00" }, { "name": "doctrine/inflector", @@ -854,16 +854,16 @@ }, { "name": "laravel/framework", - "version": "v5.3.28", + "version": "v5.3.29", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "a64fc4f8958091ca39623b2e8c8f173cb34fa47a" + "reference": "6fd76dec90466dc3f703d8df72e38130f2ee6a32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/a64fc4f8958091ca39623b2e8c8f173cb34fa47a", - "reference": "a64fc4f8958091ca39623b2e8c8f173cb34fa47a", + "url": "https://api.github.com/repos/laravel/framework/zipball/6fd76dec90466dc3f703d8df72e38130f2ee6a32", + "reference": "6fd76dec90466dc3f703d8df72e38130f2ee6a32", "shasum": "" }, "require": { @@ -880,7 +880,7 @@ "php": ">=5.6.4", "psy/psysh": "0.7.*|0.8.*", "ramsey/uuid": "~3.0", - "swiftmailer/swiftmailer": "~5.1", + "swiftmailer/swiftmailer": "~5.4", "symfony/console": "3.1.*", "symfony/debug": "3.1.*", "symfony/finder": "3.1.*", @@ -978,7 +978,7 @@ "framework", "laravel" ], - "time": "2016-12-15T18:03:17+00:00" + "time": "2017-01-06T14:33:56+00:00" }, { "name": "laravelcollective/html", @@ -1367,26 +1367,32 @@ }, { "name": "nesbot/carbon", - "version": "1.21.0", + "version": "1.22.1", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "7b08ec6f75791e130012f206e3f7b0e76e18e3d7" + "reference": "7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/7b08ec6f75791e130012f206e3f7b0e76e18e3d7", - "reference": "7b08ec6f75791e130012f206e3f7b0e76e18e3d7", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc", + "reference": "7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc", "shasum": "" }, "require": { "php": ">=5.3.0", - "symfony/translation": "~2.6|~3.0" + "symfony/translation": "~2.6 || ~3.0" }, "require-dev": { - "phpunit/phpunit": "~4.0|~5.0" + "friendsofphp/php-cs-fixer": "~2", + "phpunit/phpunit": "~4.0 || ~5.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.23-dev" + } + }, "autoload": { "psr-4": { "Carbon\\": "src/Carbon/" @@ -1410,7 +1416,7 @@ "datetime", "time" ], - "time": "2015-11-04T20:07:17+00:00" + "time": "2017-01-16T07:55:07+00:00" }, { "name": "nikic/php-parser", @@ -1621,16 +1627,16 @@ }, { "name": "psy/psysh", - "version": "v0.8.0", + "version": "v0.8.1", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "4a8860e13aa68a4bbf2476c014f8a1f14f1bf991" + "reference": "701e8a1cc426ee170f1296f5d9f6b8a26ad25c4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/4a8860e13aa68a4bbf2476c014f8a1f14f1bf991", - "reference": "4a8860e13aa68a4bbf2476c014f8a1f14f1bf991", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/701e8a1cc426ee170f1296f5d9f6b8a26ad25c4a", + "reference": "701e8a1cc426ee170f1296f5d9f6b8a26ad25c4a", "shasum": "" }, "require": { @@ -1660,7 +1666,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-develop": "0.8.x-dev" + "dev-develop": "0.9.x-dev" } }, "autoload": { @@ -1690,7 +1696,7 @@ "interactive", "shell" ], - "time": "2016-12-07T17:15:07+00:00" + "time": "2017-01-15T17:54:13+00:00" }, { "name": "ramsey/uuid", @@ -1943,16 +1949,16 @@ }, { "name": "symfony/console", - "version": "v3.1.8", + "version": "v3.1.9", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "221a60fb2f369a065eea1ed96b61183219fdfa6e" + "reference": "047f16485d68c083bd5d9b73ff16f9cb9c1a9f52" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/221a60fb2f369a065eea1ed96b61183219fdfa6e", - "reference": "221a60fb2f369a065eea1ed96b61183219fdfa6e", + "url": "https://api.github.com/repos/symfony/console/zipball/047f16485d68c083bd5d9b73ff16f9cb9c1a9f52", + "reference": "047f16485d68c083bd5d9b73ff16f9cb9c1a9f52", "shasum": "" }, "require": { @@ -2000,20 +2006,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2016-12-08T14:58:14+00:00" + "time": "2017-01-08T20:43:43+00:00" }, { "name": "symfony/debug", - "version": "v3.1.8", + "version": "v3.1.9", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "c058661c32f5b462722e36d120905940089cbd9a" + "reference": "73f1c337907ba963af8028844fea1af98498dfff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/c058661c32f5b462722e36d120905940089cbd9a", - "reference": "c058661c32f5b462722e36d120905940089cbd9a", + "url": "https://api.github.com/repos/symfony/debug/zipball/73f1c337907ba963af8028844fea1af98498dfff", + "reference": "73f1c337907ba963af8028844fea1af98498dfff", "shasum": "" }, "require": { @@ -2057,20 +2063,20 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2016-11-15T12:55:20+00:00" + "time": "2017-01-02T20:31:54+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v3.2.1", + "version": "v3.2.2", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "e8f47a327c2f0fd5aa04fa60af2b693006ed7283" + "reference": "9137eb3a3328e413212826d63eeeb0217836e2b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/e8f47a327c2f0fd5aa04fa60af2b693006ed7283", - "reference": "e8f47a327c2f0fd5aa04fa60af2b693006ed7283", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9137eb3a3328e413212826d63eeeb0217836e2b6", + "reference": "9137eb3a3328e413212826d63eeeb0217836e2b6", "shasum": "" }, "require": { @@ -2117,20 +2123,20 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2016-10-13T06:29:04+00:00" + "time": "2017-01-02T20:32:22+00:00" }, { "name": "symfony/finder", - "version": "v3.1.8", + "version": "v3.1.9", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "74dcd370c8d057882575e535616fde935e411b19" + "reference": "59687a255d1562f2c17b012418273862083d85f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/74dcd370c8d057882575e535616fde935e411b19", - "reference": "74dcd370c8d057882575e535616fde935e411b19", + "url": "https://api.github.com/repos/symfony/finder/zipball/59687a255d1562f2c17b012418273862083d85f7", + "reference": "59687a255d1562f2c17b012418273862083d85f7", "shasum": "" }, "require": { @@ -2166,20 +2172,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2016-12-13T09:38:21+00:00" + "time": "2017-01-02T20:31:54+00:00" }, { "name": "symfony/http-foundation", - "version": "v3.1.8", + "version": "v3.1.9", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "88a1d3cee2dbd06f7103ff63938743b903b65a92" + "reference": "cef0ad49a2e90455cfc649522025b5a2929648c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/88a1d3cee2dbd06f7103ff63938743b903b65a92", - "reference": "88a1d3cee2dbd06f7103ff63938743b903b65a92", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/cef0ad49a2e90455cfc649522025b5a2929648c0", + "reference": "cef0ad49a2e90455cfc649522025b5a2929648c0", "shasum": "" }, "require": { @@ -2219,20 +2225,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2016-11-27T04:21:07+00:00" + "time": "2017-01-08T20:43:43+00:00" }, { "name": "symfony/http-kernel", - "version": "v3.1.8", + "version": "v3.1.9", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "d7a4671a6f8e4174127770263dcd95bee5713f76" + "reference": "d7578a0ed01e689f5b058e1ed37b9ad0718a1ef3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/d7a4671a6f8e4174127770263dcd95bee5713f76", - "reference": "d7a4671a6f8e4174127770263dcd95bee5713f76", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/d7578a0ed01e689f5b058e1ed37b9ad0718a1ef3", + "reference": "d7578a0ed01e689f5b058e1ed37b9ad0718a1ef3", "shasum": "" }, "require": { @@ -2301,7 +2307,7 @@ ], "description": "Symfony HttpKernel Component", "homepage": "https://symfony.com", - "time": "2016-12-13T12:52:10+00:00" + "time": "2017-01-12T20:43:39+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -2472,16 +2478,16 @@ }, { "name": "symfony/process", - "version": "v3.1.8", + "version": "v3.1.9", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "d23427a7f97a373129f61bc3b876fe4c66e2b3c7" + "reference": "b525066a9efe372f0910296e486aa61741b09025" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/d23427a7f97a373129f61bc3b876fe4c66e2b3c7", - "reference": "d23427a7f97a373129f61bc3b876fe4c66e2b3c7", + "url": "https://api.github.com/repos/symfony/process/zipball/b525066a9efe372f0910296e486aa61741b09025", + "reference": "b525066a9efe372f0910296e486aa61741b09025", "shasum": "" }, "require": { @@ -2517,20 +2523,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2016-11-24T01:08:05+00:00" + "time": "2017-01-02T20:31:54+00:00" }, { "name": "symfony/routing", - "version": "v3.1.8", + "version": "v3.1.9", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "4beb3dceb14cf2dd63dd222d1825ca981a2952cb" + "reference": "5cd8d7b88e9f30a7d830fa15876828da272685d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/4beb3dceb14cf2dd63dd222d1825ca981a2952cb", - "reference": "4beb3dceb14cf2dd63dd222d1825ca981a2952cb", + "url": "https://api.github.com/repos/symfony/routing/zipball/5cd8d7b88e9f30a7d830fa15876828da272685d3", + "reference": "5cd8d7b88e9f30a7d830fa15876828da272685d3", "shasum": "" }, "require": { @@ -2592,20 +2598,20 @@ "uri", "url" ], - "time": "2016-11-25T12:27:14+00:00" + "time": "2017-01-02T20:31:54+00:00" }, { "name": "symfony/translation", - "version": "v3.1.8", + "version": "v3.1.9", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "2f4b6114b75c506dd1ee7eb485b35facbcb2d873" + "reference": "7882149d1e1fd46d960f3e42344c9caf2e535573" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/2f4b6114b75c506dd1ee7eb485b35facbcb2d873", - "reference": "2f4b6114b75c506dd1ee7eb485b35facbcb2d873", + "url": "https://api.github.com/repos/symfony/translation/zipball/7882149d1e1fd46d960f3e42344c9caf2e535573", + "reference": "7882149d1e1fd46d960f3e42344c9caf2e535573", "shasum": "" }, "require": { @@ -2656,20 +2662,20 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2016-11-18T21:15:08+00:00" + "time": "2017-01-02T20:31:54+00:00" }, { "name": "symfony/var-dumper", - "version": "v3.1.8", + "version": "v3.1.9", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "5ccbd23a97035886e595ce497dbe94bc88ac0b57" + "reference": "d34a930421233f119fe61149ce046bb5b0b412d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/5ccbd23a97035886e595ce497dbe94bc88ac0b57", - "reference": "5ccbd23a97035886e595ce497dbe94bc88ac0b57", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/d34a930421233f119fe61149ce046bb5b0b412d9", + "reference": "d34a930421233f119fe61149ce046bb5b0b412d9", "shasum": "" }, "require": { @@ -2719,7 +2725,7 @@ "debug", "dump" ], - "time": "2016-12-08T14:58:14+00:00" + "time": "2017-01-02T20:31:54+00:00" }, { "name": "twig/twig", @@ -4527,16 +4533,16 @@ }, { "name": "symfony/class-loader", - "version": "v3.2.1", + "version": "v3.2.2", "source": { "type": "git", "url": "https://github.com/symfony/class-loader.git", - "reference": "87cd4e69435d98de01d0162c5f9c0ac017075c63" + "reference": "0152f7a47acd564ca62c652975c2b32ac6d613a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/class-loader/zipball/87cd4e69435d98de01d0162c5f9c0ac017075c63", - "reference": "87cd4e69435d98de01d0162c5f9c0ac017075c63", + "url": "https://api.github.com/repos/symfony/class-loader/zipball/0152f7a47acd564ca62c652975c2b32ac6d613a6", + "reference": "0152f7a47acd564ca62c652975c2b32ac6d613a6", "shasum": "" }, "require": { @@ -4579,20 +4585,20 @@ ], "description": "Symfony ClassLoader Component", "homepage": "https://symfony.com", - "time": "2016-11-29T08:26:13+00:00" + "time": "2017-01-10T14:14:38+00:00" }, { "name": "symfony/css-selector", - "version": "v3.1.8", + "version": "v3.1.9", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "a37b3359566415a91cba55a2d95820b3fa1a9658" + "reference": "722a87478a72d95dc2a3bcf41dc9c2d13fd4cb2d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/a37b3359566415a91cba55a2d95820b3fa1a9658", - "reference": "a37b3359566415a91cba55a2d95820b3fa1a9658", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/722a87478a72d95dc2a3bcf41dc9c2d13fd4cb2d", + "reference": "722a87478a72d95dc2a3bcf41dc9c2d13fd4cb2d", "shasum": "" }, "require": { @@ -4632,20 +4638,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2016-11-03T08:04:31+00:00" + "time": "2017-01-02T20:31:54+00:00" }, { "name": "symfony/dom-crawler", - "version": "v3.1.8", + "version": "v3.1.9", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "51e979357eba65b1e6aac7cba75cf5aa6379b8f3" + "reference": "a950260ebc947578fba82a3222e2085d90682376" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/51e979357eba65b1e6aac7cba75cf5aa6379b8f3", - "reference": "51e979357eba65b1e6aac7cba75cf5aa6379b8f3", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/a950260ebc947578fba82a3222e2085d90682376", + "reference": "a950260ebc947578fba82a3222e2085d90682376", "shasum": "" }, "require": { @@ -4688,20 +4694,20 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2016-12-10T14:24:45+00:00" + "time": "2017-01-02T20:31:54+00:00" }, { "name": "symfony/yaml", - "version": "v3.2.1", + "version": "v3.2.2", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "a7095af4b97a0955f85c8989106c249fa649011f" + "reference": "50eadbd7926e31842893c957eca362b21592a97d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/a7095af4b97a0955f85c8989106c249fa649011f", - "reference": "a7095af4b97a0955f85c8989106c249fa649011f", + "url": "https://api.github.com/repos/symfony/yaml/zipball/50eadbd7926e31842893c957eca362b21592a97d", + "reference": "50eadbd7926e31842893c957eca362b21592a97d", "shasum": "" }, "require": { @@ -4743,7 +4749,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2016-12-10T10:07:06+00:00" + "time": "2017-01-03T13:51:32+00:00" }, { "name": "webmozart/assert", diff --git a/config/upgrade.php b/config/upgrade.php index e88f92c76e..199596e690 100644 --- a/config/upgrade.php +++ b/config/upgrade.php @@ -12,5 +12,14 @@ declare(strict_types = 1); return [ - 'text' => [], + 'text' => [ + 'upgrade' => + [ + '4.3' => 'Make sure you run the migrations and clear your cache. If you need more help, please check Github or the Firefly III website.', + ], + 'install' => + [ + '4.3' => 'Welcome to Firefly! Make sure you follow the installation guide. If you need more help, please check Github or the Firefly III website. The installation guide has a FAQ which you should check out as well.', + ], + ], ]; From def3b3a155282d11369f6e3b735475491f0b3eec Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Fri, 20 Jan 2017 12:23:09 +0100 Subject: [PATCH 574/709] Fix for #539 --- app/Export/Entry/Entry.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Export/Entry/Entry.php b/app/Export/Entry/Entry.php index 53ace9643b..f9c324582b 100644 --- a/app/Export/Entry/Entry.php +++ b/app/Export/Entry/Entry.php @@ -74,15 +74,15 @@ final class Entry { $entry = new self; $entry->journal_id = $object->transaction_journal_id; - $entry->description = self::decrypt($object->journal_encrypted, $object->journal_description); + $entry->description = self::decrypt(intval($object->journal_encrypted), $object->journal_description); $entry->amount = $object->amount; $entry->date = $object->date; $entry->transaction_type = $object->transaction_type; $entry->currency_code = $object->transaction_currency_code; $entry->source_account_id = $object->account_id; - $entry->source_account_name = self::decrypt($object->account_name_encrypted, $object->account_name); + $entry->source_account_name = self::decrypt(intval($object->account_name_encrypted), $object->account_name); $entry->destination_account_id = $object->opposing_account_id; - $entry->destination_account_name = self::decrypt($object->opposing_account_encrypted, $object->opposing_account_name); + $entry->destination_account_name = self::decrypt(intval($object->opposing_account_encrypted), $object->opposing_account_name); $entry->category_id = $object->category_id ?? ''; $entry->category_name = $object->category_name ?? ''; $entry->budget_id = $object->budget_id ?? ''; From 0d1d360d188f3e4c0f2cd47e270d81f793f0216f Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Fri, 20 Jan 2017 12:23:52 +0100 Subject: [PATCH 575/709] =?UTF-8?q?Can=20now=20clone=20transaction=20#538.?= =?UTF-8?q?=20Wasn=E2=80=99t=20that=20difficult.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Transaction/SingleController.php | 39 ++++++++++++++++++- resources/lang/en_US/firefly.php | 4 ++ resources/views/transactions/show.twig | 25 ++++++++---- .../views/transactions/single/create.twig | 6 +-- routes/web.php | 2 +- 5 files changed, 64 insertions(+), 12 deletions(-) diff --git a/app/Http/Controllers/Transaction/SingleController.php b/app/Http/Controllers/Transaction/SingleController.php index 4ba6653d25..9c006617bd 100644 --- a/app/Http/Controllers/Transaction/SingleController.php +++ b/app/Http/Controllers/Transaction/SingleController.php @@ -82,6 +82,42 @@ class SingleController extends Controller } + public function cloneTransaction(TransactionJournal $journal) + { + $source = TransactionJournal::sourceAccountList($journal)->first(); + $destination = TransactionJournal::destinationAccountList($journal)->first(); + $budget = $journal->budgets()->first(); + $budgetId = is_null($budget) ? 0 : $budget->id; + $category = $journal->categories()->first(); + $categoryName = is_null($category) ? '' : $category->name; + $tags = join(',', $journal->tags()->get()->pluck('tag')->toArray()); + + + $preFilled = [ + 'description' => $journal->description, + 'source_account_id' => $source->id, + 'source_account_name' => $source->name, + 'destination_account_id' => $destination->id, + 'destination_account_name' => $destination->name, + 'amount' => TransactionJournal::amountPositive($journal), + 'date' => $journal->date->format('Y-m-d'), + 'budget_id' => $budgetId, + 'category' => $categoryName, + 'tags' => $tags, + 'interest_date' => $journal->getMeta('interest_date'), + 'book_date' => $journal->getMeta('book_date'), + 'process_date' => $journal->getMeta('process_date'), + 'due_date' => $journal->getMeta('due_date'), + 'payment_date' => $journal->getMeta('payment_date'), + 'invoice_date' => $journal->getMeta('invoice_date'), + 'internal_reference' => $journal->getMeta('internal_reference'), + 'notes' => $journal->getMeta('notes'), + ]; + Session::flash('preFilled', $preFilled); + + return redirect(route('transactions.create', [strtolower($journal->transactionType->type)])); + } + /** * @param string $what * @@ -114,7 +150,8 @@ class SingleController extends Controller asort($piggies); return view( - 'transactions.single.create', compact('assetAccounts', 'subTitleIcon', 'uploadSize', 'budgets', 'what', 'piggies', 'subTitle', 'optionalFields') + 'transactions.single.create', + compact('assetAccounts', 'subTitleIcon', 'uploadSize', 'budgets', 'what', 'piggies', 'subTitle', 'optionalFields', 'preFilled') ); } diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 61367d9b2c..06c60e8258 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -106,6 +106,10 @@ return [ 'pref_languages_locale' => 'For a language other than English to work properly, your operating system must be equipped with the correct locale-information. If these are not present, currency data, dates and amounts may be formatted wrong.', 'budget_in_period' => '":name" between :start and :end', 'budget_in_period_breadcrumb' => 'Between :start and :end', + 'clone_withdrawal' => 'Clone this withdrawal', + 'clone_deposit' => 'Clone this deposit', + 'clone_transfer' => 'Clone this transfer', + 'transaction_journal_other_options' => 'Other options', // repeat frequencies: 'repeat_freq_yearly' => 'yearly', diff --git a/resources/views/transactions/show.twig b/resources/views/transactions/show.twig index f20e920b98..c3a0ab7079 100644 --- a/resources/views/transactions/show.twig +++ b/resources/views/transactions/show.twig @@ -82,6 +82,15 @@ <td>{{ 'budgets'|_ }}</td> <td>{{ journalBudgets(journal)|raw }}</td> </tr> + + {% if journal.hasMeta('interest_date') %} + <tr> + <td>{{ trans('list.interest_date') }}</td> + <td>{{ journal.getMeta('interest_date').formatLocalized(monthAndDayFormat) }}</td> + </tr> + + {% endif %} + {% if journal.hasMeta('book_date') %} <tr> <td>{{ trans('list.book_date') }}</td> @@ -95,13 +104,7 @@ </tr> {% endif %} - {% if journal.hasMeta('interest_date') %} - <tr> - <td>{{ trans('list.interest_date') }}</td> - <td>{{ journal.getMeta('interest_date').formatLocalized(monthAndDayFormat) }}</td> - </tr> - {% endif %} {% if journal.hasMeta('due_date') %} <tr> <td>{{ trans('list.due_date') }}</td> @@ -211,7 +214,7 @@ {% if transactions|length == 1 %} <div class="box"> <div class="box-header with-border"> - <h3 class="box-title">{{ 'transaction_journal_convert_options'|_ }}</h3> + <h3 class="box-title">{{ 'transaction_journal_other_options'|_ }}</h3> </div> <div class="box-body"> {% if journal.transactionType.type != "Withdrawal" %} @@ -239,6 +242,14 @@ </a> </p> {% endif %} + {% if transactions|length == 1 %} + <p> + <i class="fa fa-copy" aria-hidden="true"></i> + <a href="{{ route('transactions.clone', [journal.id]) }}"> + {{ ('clone_'~journal.transactionType.type|lower)|_ }} + </a> + </p> + {% endif %} </div> </div> diff --git a/resources/views/transactions/single/create.twig b/resources/views/transactions/single/create.twig index b6f2edbfef..baa6544194 100644 --- a/resources/views/transactions/single/create.twig +++ b/resources/views/transactions/single/create.twig @@ -48,7 +48,7 @@ {{ ExpandedForm.amount('amount') }} <!-- ALWAYS SHOW DATE --> - {{ ExpandedForm.date('date', phpdate('Y-m-d')) }} + {{ ExpandedForm.date('date', preFilled.date|default(phpdate('Y-m-d'))) }} </div> <div class="box-footer"> <button type="submit" id="transaction-btn" class="btn btn-success pull-right"> @@ -65,9 +65,9 @@ <div class="box-body"> <!-- BUDGET ONLY WHEN CREATING A WITHDRAWAL --> {% if budgets|length > 1 %} - {{ ExpandedForm.select('budget_id', budgets, 0) }} + {{ ExpandedForm.select('budget_id', budgets, null) }} {% else %} - {{ ExpandedForm.select('budget_id', budgets, 0, {helpText: trans('firefly.no_budget_pointer')}) }} + {{ ExpandedForm.select('budget_id', budgets, null, {helpText: trans('firefly.no_budget_pointer')}) }} {% endif %} <!-- CATEGORY ALWAYS --> diff --git a/routes/web.php b/routes/web.php index 04835588af..5e644f4dda 100755 --- a/routes/web.php +++ b/routes/web.php @@ -634,6 +634,7 @@ Route::group( Route::post('store/{what}', ['uses' => 'SingleController@store', 'as' => 'store'])->where(['what' => 'withdrawal|deposit|transfer']); Route::post('update/{tj}', ['uses' => 'SingleController@update', 'as' => 'update']); Route::post('destroy/{tj}', ['uses' => 'SingleController@destroy', 'as' => 'destroy']); + Route::get('clone/{tj}', ['uses' => 'SingleController@cloneTransaction', 'as' => 'clone']); } ); @@ -667,7 +668,6 @@ Route::group( ['middleware' => 'user-full-auth', 'namespace' => 'Transaction', 'prefix' => 'transactions/convert', 'as' => 'transactions.convert.'], function () { Route::get('{transaction_type}/{tj}', ['uses' => 'ConvertController@index', 'as' => 'index']); Route::post('{transaction_type}/{tj}', ['uses' => 'ConvertController@postIndex', 'as' => 'index.post']); - } ); From 8f2b898b2bf4b7e63e12cbf33d677804142f470f Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Fri, 20 Jan 2017 16:22:19 +0100 Subject: [PATCH 576/709] Update some tests. --- tests/TestCase.php | 6 + .../Transaction/SingleControllerTest.php | 24 ---- tests/acceptance/NaughtyListTest.php | 123 ++++++++++++++++++ 3 files changed, 129 insertions(+), 24 deletions(-) create mode 100644 tests/acceptance/NaughtyListTest.php diff --git a/tests/TestCase.php b/tests/TestCase.php index 967575c5fb..f809d58ace 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -99,7 +99,13 @@ abstract class TestCase extends Illuminate\Foundation\Testing\TestCase */ public function naughtyStringProvider() { + /* + * If on Travis, return very small set. + */ + if (getenv('TRAVIS') == '1') { + return [['Default value']]; + } $path = realpath(__DIR__ . '/../resources/tests/blns.base64.json'); $content = file_get_contents($path); $array = json_decode($content); diff --git a/tests/acceptance/Controllers/Transaction/SingleControllerTest.php b/tests/acceptance/Controllers/Transaction/SingleControllerTest.php index f186514ecc..81e67a4e21 100644 --- a/tests/acceptance/Controllers/Transaction/SingleControllerTest.php +++ b/tests/acceptance/Controllers/Transaction/SingleControllerTest.php @@ -106,29 +106,6 @@ class SingleControllerTest extends TestCase $this->assertSessionHas('success'); } - /** - * @covers \FireflyIII\Http\Controllers\Transaction\SingleController::store - * @dataProvider naughtyStringProvider - */ - public function testStoreNaughty(string $description) - { - $this->session(['transactions.create.url' => 'http://localhost']); - $this->be($this->user()); - - $data = [ - 'what' => 'withdrawal', - 'amount' => '10', - 'amount_currency_id_amount' => 1, - 'source_account_id' => 1, - 'destination_account_name' => 'Some destination', - 'date' => '2016-01-01', - 'description' => $description, - ]; - $response = $this->call('post', route('transactions.store', ['withdrawal']), $data); - $this->assertNotEquals($response->getStatusCode(), 500); - - } - /** * @covers \FireflyIII\Http\Controllers\Transaction\SingleController::update */ @@ -161,5 +138,4 @@ class SingleControllerTest extends TestCase $this->see('<ol class="breadcrumb">'); } - } diff --git a/tests/acceptance/NaughtyListTest.php b/tests/acceptance/NaughtyListTest.php new file mode 100644 index 0000000000..d729ff362e --- /dev/null +++ b/tests/acceptance/NaughtyListTest.php @@ -0,0 +1,123 @@ +<?php +/** + * NaughtyListTest.php + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ +declare(strict_types = 1); + +use FireflyIII\Models\TransactionJournal; + +/** + * Class NaughtyListTest + */ +class NaughtyListTest extends TestCase +{ + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + public function setUp() + { + parent::setUp(); + + } + + /** + * @covers \FireflyIII\Http\Controllers\Transaction\MassController::update + * @dataProvider naughtyStringProvider + * + * @param string $description + */ + public function testMassUpdate(string $description) + { + $deposit = TransactionJournal::where('transaction_type_id', 2)->where('user_id', $this->user()->id) + ->whereNull('deleted_at') + ->first(); + $this->session(['transactions.mass-edit.url' => 'http://localhost']); + + $data = [ + 'journals' => [$deposit->id], + 'description' => [$deposit->id => $description], + 'amount' => [$deposit->id => 1600], + 'amount_currency_id_amount_' . $deposit->id => 1, + 'date' => [$deposit->id => '2014-07-24'], + 'source_account_name' => [$deposit->id => 'Job'], + 'destination_account_id' => [$deposit->id => 1], + 'category' => [$deposit->id => 'Salary'], + ]; + + $this->be($this->user()); + $response = $this->call('post', route('transactions.mass.update', [$deposit->id]), $data); + $this->assertNotEquals($response->getStatusCode(), 500); + + // visit them should show updated content + $this->call('get', route('transactions.show', [$deposit->id])); + $this->assertResponseOk(); + $this->see($description); + } + + /** + * @covers \FireflyIII\Http\Controllers\Transaction\SingleController::store + * @dataProvider naughtyStringProvider + * + * @param string $description + */ + public function testSingleStore(string $description) + { + $this->session(['transactions.create.url' => 'http://localhost']); + $this->be($this->user()); + + $data = [ + 'what' => 'withdrawal', + 'amount' => '10', + 'amount_currency_id_amount' => 1, + 'source_account_id' => 1, + 'destination_account_name' => 'Some destination', + 'date' => '2016-01-01', + 'description' => $description, + ]; + $response = $this->call('post', route('transactions.store', ['withdrawal']), $data); + $this->assertNotEquals($response->getStatusCode(), 500); + + } + + /** + * @covers \FireflyIII\Http\Controllers\Transaction\SingleController::update + * @dataProvider naughtyStringProvider + * + * @param string $description + */ + public function testSingleUpdate(string $description) + { + $this->session(['transactions.edit.url' => 'http://localhost']); + $this->be($this->user()); + $data = [ + 'id' => 123, + 'what' => 'withdrawal', + 'description' => $description, + 'source_account_id' => 1, + 'destination_account_name' => 'PLUS', + 'amount' => '123', + 'amount_currency_id_amount' => 1, + 'budget_id' => 1, + 'category' => 'Daily groceries', + 'tags' => '', + 'date' => '2016-01-01', + ]; + + $response = $this->call('post', route('transactions.update', [123]), $data); + $this->assertNotEquals($response->getStatusCode(), 500); + $this->assertSessionHas('success'); + + $this->call('get', route('transactions.show', [123])); + $this->assertResponseStatus(200); + $this->see($description); + // has bread crumb + $this->see('<ol class="breadcrumb">'); + + } + +} \ No newline at end of file From 6b57d4397a1a76f1ce080b646b7161463f2c7b52 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Fri, 20 Jan 2017 16:27:30 +0100 Subject: [PATCH 577/709] =?UTF-8?q?Try=20with=20=E2=80=9Ctrue=E2=80=9D=20i?= =?UTF-8?q?nstead=20of=20=E2=80=9C1=E2=80=9D.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/TestCase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/TestCase.php b/tests/TestCase.php index f809d58ace..c2a57c8e43 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -102,7 +102,7 @@ abstract class TestCase extends Illuminate\Foundation\Testing\TestCase /* * If on Travis, return very small set. */ - if (getenv('TRAVIS') == '1') { + if (getenv('TRAVIS') == 'true') { return [['Default value']]; } From 8635fe7ebbb23eb0de151a89941d098ed9a52080 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Fri, 20 Jan 2017 19:49:35 +0100 Subject: [PATCH 578/709] Update requests to use uniform methods. --- app/Export/Entry/Entry.php | 1 - app/Http/Requests/AccountFormRequest.php | 18 +++++----- app/Http/Requests/AttachmentFormRequest.php | 6 ++-- app/Http/Requests/BillFormRequest.php | 6 ++-- app/Http/Requests/BudgetFormRequest.php | 2 +- app/Http/Requests/CategoryFormRequest.php | 2 +- app/Http/Requests/CurrencyFormRequest.php | 6 ++-- app/Http/Requests/JournalFormRequest.php | 40 ++------------------- app/Http/Requests/PiggyBankFormRequest.php | 2 +- app/Http/Requests/Request.php | 38 ++++++++++++++++++-- app/Http/Requests/RuleFormRequest.php | 6 ++-- app/Http/Requests/RuleGroupFormRequest.php | 4 +-- 12 files changed, 64 insertions(+), 67 deletions(-) diff --git a/app/Export/Entry/Entry.php b/app/Export/Entry/Entry.php index f9c324582b..0afc40ada4 100644 --- a/app/Export/Entry/Entry.php +++ b/app/Export/Entry/Entry.php @@ -51,7 +51,6 @@ final class Entry public $destination_account_id; public $destination_account_name; - public $budget_id; public $budget_name; public $category_id; diff --git a/app/Http/Requests/AccountFormRequest.php b/app/Http/Requests/AccountFormRequest.php index 364898614c..95def39b47 100644 --- a/app/Http/Requests/AccountFormRequest.php +++ b/app/Http/Requests/AccountFormRequest.php @@ -39,21 +39,21 @@ class AccountFormRequest extends Request public function getAccountData(): array { return [ - 'name' => trim(strval($this->input('name'))), + 'name' => $this->getFieldOrEmptyString('name'), 'active' => intval($this->input('active')) === 1, - 'accountType' => $this->input('what'), + 'accountType' => $this->getFieldOrEmptyString('what'), 'currency_id' => intval($this->input('currency_id')), 'virtualBalance' => round($this->input('virtualBalance'), 12), 'virtualBalanceCurrency' => intval($this->input('amount_currency_id_virtualBalance')), - 'iban' => trim(strval($this->input('iban'))), - 'BIC' => trim(strval($this->input('BIC'))), - 'accountNumber' => trim(strval($this->input('accountNumber'))), - 'accountRole' => $this->input('accountRole'), + 'iban' => $this->getFieldOrEmptyString('iban'), + 'BIC' => $this->getFieldOrEmptyString('BIC'), + 'accountNumber' => $this->getFieldOrEmptyString('accountNumber'), + 'accountRole' => $this->getFieldOrEmptyString('accountRole'), 'openingBalance' => round($this->input('openingBalance'), 12), 'openingBalanceDate' => new Carbon((string)$this->input('openingBalanceDate')), 'openingBalanceCurrency' => intval($this->input('amount_currency_id_openingBalance')), - 'ccType' => $this->input('ccType'), - 'ccMonthlyPaymentDate' => $this->input('ccMonthlyPaymentDate'), + 'ccType' => $this->getFieldOrEmptyString('ccType'), + 'ccMonthlyPaymentDate' => $this->getFieldOrEmptyString('ccMonthlyPaymentDate'), ]; } @@ -72,7 +72,7 @@ class AccountFormRequest extends Request $idRule = ''; if (!is_null($repository->find(intval($this->get('id')))->id)) { $idRule = 'belongsToUser:accounts'; - $nameRule = 'required|min:1|uniqueAccountForUser:' . $this->get('id'); + $nameRule = 'required|min:1|uniqueAccountForUser:' . intval($this->get('id')); } return [ diff --git a/app/Http/Requests/AttachmentFormRequest.php b/app/Http/Requests/AttachmentFormRequest.php index 16028cab37..b2da7b7f83 100644 --- a/app/Http/Requests/AttachmentFormRequest.php +++ b/app/Http/Requests/AttachmentFormRequest.php @@ -36,9 +36,9 @@ class AttachmentFormRequest extends Request public function getAttachmentData(): array { return [ - 'title' => trim($this->input('title')), - 'description' => trim($this->input('description')), - 'notes' => trim($this->input('notes')), + 'title' => $this->getFieldOrEmptyString('title'), + 'description' => $this->getFieldOrEmptyString('description'), + 'notes' => $this->getFieldOrEmptyString('notes'), ]; } diff --git a/app/Http/Requests/BillFormRequest.php b/app/Http/Requests/BillFormRequest.php index 0b0b85e29d..2ef7c2ae88 100644 --- a/app/Http/Requests/BillFormRequest.php +++ b/app/Http/Requests/BillFormRequest.php @@ -38,14 +38,14 @@ class BillFormRequest extends Request public function getBillData() { return [ - 'name' => $this->get('name'), - 'match' => $this->get('match'), + 'name' => $this->getFieldOrEmptyString('name'), + 'match' => $this->getFieldOrEmptyString('match'), 'amount_min' => round($this->get('amount_min'), 12), 'amount_currency_id_amount_min' => intval($this->get('amount_currency_id_amount_min')), 'amount_currency_id_amount_max' => intval($this->get('amount_currency_id_amount_max')), 'amount_max' => round($this->get('amount_max'), 12), 'date' => new Carbon($this->get('date')), - 'repeat_freq' => $this->get('repeat_freq'), + 'repeat_freq' => $this->getFieldOrEmptyString('repeat_freq'), 'skip' => intval($this->get('skip')), 'automatch' => intval($this->get('automatch')) === 1, 'active' => intval($this->get('active')) === 1, diff --git a/app/Http/Requests/BudgetFormRequest.php b/app/Http/Requests/BudgetFormRequest.php index 3ca9d0fb37..cfe113aadc 100644 --- a/app/Http/Requests/BudgetFormRequest.php +++ b/app/Http/Requests/BudgetFormRequest.php @@ -37,7 +37,7 @@ class BudgetFormRequest extends Request public function getBudgetData(): array { return [ - 'name' => trim($this->input('name')), + 'name' => $this->getFieldOrEmptyString('name'), 'active' => intval($this->input('active')) == 1, ]; } diff --git a/app/Http/Requests/CategoryFormRequest.php b/app/Http/Requests/CategoryFormRequest.php index 34347608af..8a53b6a6ea 100644 --- a/app/Http/Requests/CategoryFormRequest.php +++ b/app/Http/Requests/CategoryFormRequest.php @@ -38,7 +38,7 @@ class CategoryFormRequest extends Request public function getCategoryData(): array { return [ - 'name' => trim($this->input('name')), + 'name' => $this->getFieldOrEmptyString('name'), ]; } diff --git a/app/Http/Requests/CurrencyFormRequest.php b/app/Http/Requests/CurrencyFormRequest.php index ffbfa7e399..d2131c1eff 100644 --- a/app/Http/Requests/CurrencyFormRequest.php +++ b/app/Http/Requests/CurrencyFormRequest.php @@ -36,9 +36,9 @@ class CurrencyFormRequest extends Request public function getCurrencyData() { return [ - 'name' => $this->get('name'), - 'code' => $this->get('code'), - 'symbol' => $this->get('symbol'), + 'name' => $this->getFieldOrEmptyString('name'), + 'code' => $this->getFieldOrEmptyString('code'), + 'symbol' => $this->getFieldOrEmptyString('symbol'), 'decimal_places' => intval($this->get('decimal_places')), ]; } diff --git a/app/Http/Requests/JournalFormRequest.php b/app/Http/Requests/JournalFormRequest.php index bde5f20166..864242d22c 100644 --- a/app/Http/Requests/JournalFormRequest.php +++ b/app/Http/Requests/JournalFormRequest.php @@ -54,8 +54,8 @@ class JournalFormRequest extends Request 'due_date' => $this->getDateOrNull('due_date'), 'payment_date' => $this->getDateOrNull('payment_date'), 'invoice_date' => $this->getDateOrNull('invoice_date'), - 'internal_reference' => trim(strval($this->get('internal_reference'))), - 'notes' => trim(strval($this->get('notes'))), + 'internal_reference' => $this->getFieldOrEmptyString('internal_reference'), + 'notes' => $this->getFieldOrEmptyString('notes'), // transaction / journal data: 'description' => $this->getFieldOrEmptyString('description'), @@ -152,40 +152,4 @@ class JournalFormRequest extends Request { return $this->get($field) ? new Carbon($this->get($field)) : null; } - - /** - * @param string $field - * - * @return string - */ - private function getFieldOrEmptyString(string $field): string - { - $string = $this->get($field) ?? ''; - - $search = [ - "\xa0", // non-breaking space - "\u{1680}", // OGHAM SPACE MARK - "\u{180E}", // MONGOLIAN VOWEL SEPARATOR - "\u{2000}", // EN QUAD - "\u{2001}", // EM QUAD - "\u{2002}", //EN SPACE - "\u{2003}", //EM SPACE - "\u{2004}", //THREE-PER-EM SPACE - "\u{2005}", //FOUR-PER-EM SPACE - "\u{2006}", //SIX-PER-EM SPACE - "\u{2007}", //FIGURE SPACE - "\u{2008}", //PUNCTUATION SPACE - "\u{2009}", //THIN SPACE - "\u{200A}", //HAIR SPACE - "\u{200B}", //ZERO WIDTH SPACE - "\u{202F}", //NARROW NO-BREAK SPACE - "\u{205F}", //MEDIUM MATHEMATICAL SPACE - "\u{3000}", //IDEOGRAPHIC SPACE - "\u{FEFF}", // ZERO WIDTH NO -BREAK SPACE - ]; - $replace = "\x20"; // plain old normal space - $string = str_replace($search, $replace, $string); - - return trim($string); - } } diff --git a/app/Http/Requests/PiggyBankFormRequest.php b/app/Http/Requests/PiggyBankFormRequest.php index d0e6889d62..70e7ce9d7a 100644 --- a/app/Http/Requests/PiggyBankFormRequest.php +++ b/app/Http/Requests/PiggyBankFormRequest.php @@ -38,7 +38,7 @@ class PiggyBankFormRequest extends Request public function getPiggyBankData(): array { return [ - 'name' => trim($this->get('name')), + 'name' => $this->getFieldOrEmptyString('name'), 'startdate' => new Carbon, 'account_id' => intval($this->get('account_id')), 'targetamount' => round($this->get('targetamount'), 12), diff --git a/app/Http/Requests/Request.php b/app/Http/Requests/Request.php index 84cb318d3e..8a137ff254 100644 --- a/app/Http/Requests/Request.php +++ b/app/Http/Requests/Request.php @@ -20,7 +20,41 @@ use Illuminate\Foundation\Http\FormRequest; * * @package FireflyIII\Http\Requests */ -abstract class Request extends FormRequest +class Request extends FormRequest { - // + /** + * @param string $field + * + * @return string + */ + protected function getFieldOrEmptyString(string $field): string + { + $string = $this->get($field) ?? ''; + + $search = [ + "\u{00A0}", // non-breaking space + "\u{1680}", // OGHAM SPACE MARK + "\u{180E}", // MONGOLIAN VOWEL SEPARATOR + "\u{2000}", // EN QUAD + "\u{2001}", // EM QUAD + "\u{2002}", // EN SPACE + "\u{2003}", // EM SPACE + "\u{2004}", // THREE-PER-EM SPACE + "\u{2005}", // FOUR-PER-EM SPACE + "\u{2006}", // SIX-PER-EM SPACE + "\u{2007}", // FIGURE SPACE + "\u{2008}", // PUNCTUATION SPACE + "\u{2009}", // THIN SPACE + "\u{200A}", // HAIR SPACE + "\u{200B}", // ZERO WIDTH SPACE + "\u{202F}", // NARROW NO-BREAK SPACE + "\u{205F}", // MEDIUM MATHEMATICAL SPACE + "\u{3000}", // IDEOGRAPHIC SPACE + "\u{FEFF}", // ZERO WIDTH NO -BREAK SPACE + ]; + $replace = "\x20"; // plain old normal space + $string = str_replace($search, $replace, $string); + + return trim($string); + } } diff --git a/app/Http/Requests/RuleFormRequest.php b/app/Http/Requests/RuleFormRequest.php index 814f0c560d..5906d62967 100644 --- a/app/Http/Requests/RuleFormRequest.php +++ b/app/Http/Requests/RuleFormRequest.php @@ -38,10 +38,10 @@ class RuleFormRequest extends Request public function getRuleData(): array { return [ - 'title' => trim($this->get('title')), + 'title' => $this->getFieldOrEmptyString('title'), 'active' => intval($this->get('active')) == 1, - 'trigger' => trim($this->get('trigger')), - 'description' => trim($this->get('description')), + 'trigger' => $this->getFieldOrEmptyString('trigger'), + 'description' => $this->getFieldOrEmptyString('description'), 'rule-triggers' => $this->get('rule-trigger'), 'rule-trigger-values' => $this->get('rule-trigger-value'), 'rule-trigger-stop' => $this->get('rule-trigger-stop'), diff --git a/app/Http/Requests/RuleGroupFormRequest.php b/app/Http/Requests/RuleGroupFormRequest.php index b5420c5ce5..ca0de4c947 100644 --- a/app/Http/Requests/RuleGroupFormRequest.php +++ b/app/Http/Requests/RuleGroupFormRequest.php @@ -38,8 +38,8 @@ class RuleGroupFormRequest extends Request public function getRuleGroupData(): array { return [ - 'title' => trim($this->input('title')), - 'description' => trim($this->input('description')), + 'title' => $this->getFieldOrEmptyString('title'), + 'description' => $this->getFieldOrEmptyString('description'), ]; } From 50e39a4a75dc0e1eb4b816dc487490a9cfb3c37b Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Fri, 20 Jan 2017 19:50:22 +0100 Subject: [PATCH 579/709] Update rule controller to have some auto complete functionality. --- app/Http/Controllers/JsonController.php | 92 +++++++++++------ .../Journal/JournalRepository.php | 8 ++ .../Journal/JournalRepositoryInterface.php | 6 ++ public/js/ff/rules/create-edit.js | 98 +++++++++++++++++++ public/js/ff/rules/create.js | 1 - resources/views/rules/partials/trigger.twig | 2 +- resources/views/rules/rule/create.twig | 3 + routes/web.php | 3 + 8 files changed, 179 insertions(+), 34 deletions(-) diff --git a/app/Http/Controllers/JsonController.php b/app/Http/Controllers/JsonController.php index d38012e6a2..9c384b4166 100644 --- a/app/Http/Controllers/JsonController.php +++ b/app/Http/Controllers/JsonController.php @@ -21,6 +21,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI; +use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Http\Request; @@ -61,6 +62,27 @@ class JsonController extends Controller return Response::json(['html' => $view]); } + /** + * Returns a JSON list of all accounts. + * + * @param AccountRepositoryInterface $repository + * + * @return \Illuminate\Http\JsonResponse + * + */ + public function allAccounts(AccountRepositoryInterface $repository) + { + $return = array_unique( + $repository->getAccountsByType( + [AccountType::REVENUE, AccountType::EXPENSE, AccountType::BENEFICIARY, AccountType::DEFAULT, AccountType::ASSET] + )->pluck('name')->toArray() + ); + sort($return); + + return Response::json($return); + + } + /** * @param BillRepositoryInterface $repository * @@ -166,11 +188,8 @@ class JsonController extends Controller */ public function categories(CRI $repository) { - $list = $repository->getCategories(); - $return = []; - foreach ($list as $entry) { - $return[] = $entry->name; - } + $return = array_unique($repository->getCategories()->pluck('name')->toArray()); + sort($return); return Response::json($return); } @@ -195,14 +214,10 @@ class JsonController extends Controller */ public function expenseAccounts(AccountRepositoryInterface $repository) { - $list = $repository->getAccountsByType([AccountType::EXPENSE, AccountType::BENEFICIARY]); - $return = []; - foreach ($list as $entry) { - $return[] = $entry->name; - } + $return = array_unique($repository->getAccountsByType([AccountType::EXPENSE, AccountType::BENEFICIARY])->pluck('name')->toArray()); + sort($return); return Response::json($return); - } /** @@ -213,14 +228,10 @@ class JsonController extends Controller */ public function revenueAccounts(AccountRepositoryInterface $repository) { - $list = $repository->getAccountsByType([AccountType::REVENUE]); - $return = []; - foreach ($list as $entry) { - $return[] = $entry->name; - } + $return = array_unique($repository->getAccountsByType([AccountType::REVENUE])->pluck('name')->toArray()); + sort($return); return Response::json($return); - } /** @@ -232,11 +243,8 @@ class JsonController extends Controller */ public function tags(TagRepositoryInterface $tagRepository) { - $list = $tagRepository->get(); - $return = []; - foreach ($list as $entry) { - $return[] = $entry->tag; - } + $return = array_unique($tagRepository->get()->pluck('tag')->toArray()); + sort($return); return Response::json($return); @@ -270,6 +278,22 @@ class JsonController extends Controller return Response::json(['steps' => $steps, 'template' => $template]); } + /** + * @param JournalCollectorInterface $collector + * + * @return \Illuminate\Http\JsonResponse + */ + public function allTransactionJournals(JournalCollectorInterface $collector) + { + $collector->setLimit(100)->setPage(1); + $return = array_unique($collector->getJournals()->pluck('description')->toArray()); + sort($return); + + return Response::json($return); + + + } + /** * @param JournalCollectorInterface $collector * @param string $what @@ -278,25 +302,29 @@ class JsonController extends Controller */ public function transactionJournals(JournalCollectorInterface $collector, string $what) { - $descriptions = []; $type = config('firefly.transactionTypesByWhat.' . $what); $types = [$type]; - // use journal collector instead: $collector->setTypes($types)->setLimit(100)->setPage(1); - $journals = $collector->getJournals(); - foreach ($journals as $j) { - $descriptions[] = $j->description; - } + $return = array_unique($collector->getJournals()->pluck('description')->toArray()); + sort($return); - $descriptions = array_unique($descriptions); - sort($descriptions); - - return Response::json($descriptions); + return Response::json($return); } + /** + * + */ + public function transactionTypes(JournalRepositoryInterface $repository) + { + $return = array_unique($repository->getTransactionTypes()->pluck('type')->toArray()); + sort($return); + + return Response::json($return); + } + /** * @param Request $request * diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php index 2093793c8d..7dcc7996db 100644 --- a/app/Repositories/Journal/JournalRepository.php +++ b/app/Repositories/Journal/JournalRepository.php @@ -137,6 +137,14 @@ class JournalRepository implements JournalRepositoryInterface return $entry; } + /** + * @return Collection + */ + public function getTransactionTypes(): Collection + { + return TransactionType::orderBy('type', 'ASC')->get(); + } + /** * @param array $data * diff --git a/app/Repositories/Journal/JournalRepositoryInterface.php b/app/Repositories/Journal/JournalRepositoryInterface.php index fb9779eec3..ab17ab22a2 100644 --- a/app/Repositories/Journal/JournalRepositoryInterface.php +++ b/app/Repositories/Journal/JournalRepositoryInterface.php @@ -16,6 +16,7 @@ namespace FireflyIII\Repositories\Journal; use FireflyIII\Models\Account; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; +use Illuminate\Support\Collection; use Illuminate\Support\MessageBag; /** @@ -36,6 +37,11 @@ interface JournalRepositoryInterface */ public function convert(TransactionJournal $journal, TransactionType $type, Account $source, Account $destination): MessageBag; + /** + * @return Collection + */ + public function getTransactionTypes(): Collection; + /** * Deletes a journal. * diff --git a/public/js/ff/rules/create-edit.js b/public/js/ff/rules/create-edit.js index cd36a7b1ad..1205cfd728 100644 --- a/public/js/ff/rules/create-edit.js +++ b/public/js/ff/rules/create-edit.js @@ -11,25 +11,47 @@ var triggerCount = 0; var actionCount = 0; +/** + * This method triggers when a new trigger must be added to the form. + */ function addNewTrigger() { "use strict"; triggerCount++; + // get the HTML for the new trigger $.getJSON('json/trigger', {count: triggerCount}).done(function (data) { + + // append it: $('tbody.rule-trigger-tbody').append(data.html); + // update all "remove trigger"-buttons so they will respond correctly + // and remove the trigger. $('.remove-trigger').unbind('click').click(function (e) { removeTrigger(e); return false; }); + // update all "select trigger type" dropdown buttons so they will respond correctly + $('select[name^="rule-trigger["]').unbind('change').change(function (e) { + var target = $(e.target); + updateTriggerAutoComplete(target) + }); + + // update all "select trigger type" dropdowns + // so the accompanying text-box has the correct autocomplete. + onAddNewTrigger(); + + }).fail(function () { alert('Cannot get a new trigger.'); }); } +/** + * Method triggers when a new action must be added to the form.. + */ function addNewAction() { "use strict"; actionCount++; @@ -49,6 +71,11 @@ function addNewAction() { }); } +/** + * Method fires when a trigger must be removed from the form. + * + * @param e + */ function removeTrigger(e) { "use strict"; var target = $(e.target); @@ -64,6 +91,11 @@ function removeTrigger(e) { } } +/** + * Method fires when an action must be removed from the form. + * + * @param e + */ function removeAction(e) { "use strict"; var target = $(e.target); @@ -79,6 +111,72 @@ function removeAction(e) { } } +/** + * Method fires when a new trigger is added. It will update ALL trigger value input fields. + */ +function onAddNewTrigger() { + "use strict"; + console.log('updateTriggerAutoComplete'); + $.each($('.rule-trigger-holder'), function (i, v) { + var holder = $(v); + var select = holder.find('select'); + console.log('Now at input #' + i); + updateTriggerAutoComplete(select); + }); +} + +/** + * Creates a nice auto complete trigger depending on the type of the select list value thing. + * + * @param selectList + */ +function updateTriggerAutoComplete(selectList) { + // the actual row this select list is in: + var parent = selectList.parent().parent(); + // the text input we're looking for: + var input = parent.find('input[name^="rule-trigger-value["]'); + switch (selectList.val()) { + default: + input.typeahead('destroy'); + console.log('Cannot or will not add autocomplete to "' + selectList.val() + '"'); + break; + case 'from_account_starts': + case 'from_account_ends': + case 'from_account_is': + case 'from_account_contains': + case 'to_account_starts': + case 'to_account_ends': + case 'to_account_is': + case 'to_account_contains': + createAutoComplete(input, 'json/all-accounts'); + break; + case 'transaction_type': + createAutoComplete(input, 'json/transaction-types'); + break; + case 'description_starts': + case 'description_ends': + case 'description_contains': + case 'description_is': + createAutoComplete(input, 'json/transaction-journals/all'); + break; + } +} + +/** + * Create actual autocomplete + * @param input + * @param URI + */ +function createAutoComplete(input, URI) { + console.log('Will create auto-complete for "' + URI + '".'); + input.typeahead('destroy'); + $.getJSON(URI).done(function (data) { + input.typeahead({source: data}); + console.log('Created new auto-complete!'); + }); + +} + function testRuleTriggers() { "use strict"; diff --git a/public/js/ff/rules/create.js b/public/js/ff/rules/create.js index ffa65be20e..2ffb875eb6 100644 --- a/public/js/ff/rules/create.js +++ b/public/js/ff/rules/create.js @@ -20,7 +20,6 @@ $(function () { addNewAction(); } - $('.add_rule_trigger').click(function () { addNewTrigger(); diff --git a/resources/views/rules/partials/trigger.twig b/resources/views/rules/partials/trigger.twig index 2f60699ea4..ea9ea7b9e6 100644 --- a/resources/views/rules/partials/trigger.twig +++ b/resources/views/rules/partials/trigger.twig @@ -1,4 +1,4 @@ -<tr data-count="{{ count }}"> +<tr data-count="{{ count }}" class="rule-trigger-holder"> <td style="width:40px;"> <a href="#" class="btn btn-danger btn-sm remove-trigger"><i class="fa fa-trash"></i></a> </td> diff --git a/resources/views/rules/rule/create.twig b/resources/views/rules/rule/create.twig index 481473712c..f7b290a54f 100644 --- a/resources/views/rules/rule/create.twig +++ b/resources/views/rules/rule/create.twig @@ -127,6 +127,7 @@ {% endblock %} {% block scripts %} + <script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js"></script> <script type="text/javascript" src="js/ff/rules/create-edit.js"></script> <script type="text/javascript"> var triggerCount = {{ triggerCount }}; @@ -134,3 +135,5 @@ </script> <script type="text/javascript" src="js/ff/rules/create.js"></script> {% endblock %} +{% block styles %} +{% endblock %} diff --git a/routes/web.php b/routes/web.php index 5e644f4dda..489b7b5762 100755 --- a/routes/web.php +++ b/routes/web.php @@ -380,6 +380,7 @@ Route::group( Route::group( ['middleware' => 'user-full-auth', 'prefix' => 'json', 'as' => 'json.'], function () { Route::get('expense-accounts', ['uses' => 'JsonController@expenseAccounts', 'as' => 'expense-accounts']); + Route::get('all-accounts', ['uses' => 'JsonController@allAccounts', 'as' => 'all-accounts']); Route::get('revenue-accounts', ['uses' => 'JsonController@revenueAccounts', 'as' => 'revenue-accounts']); Route::get('categories', ['uses' => 'JsonController@categories', 'as' => 'categories']); Route::get('tags', ['uses' => 'JsonController@tags', 'as' => 'tags']); @@ -388,7 +389,9 @@ Route::group( Route::get('box/out', ['uses' => 'JsonController@boxOut', 'as' => 'box.out']); Route::get('box/bills-unpaid', ['uses' => 'JsonController@boxBillsUnpaid', 'as' => 'box.paid']); Route::get('box/bills-paid', ['uses' => 'JsonController@boxBillsPaid', 'as' => 'box.unpaid']); + Route::get('transaction-journals/all', ['uses' => 'JsonController@allTransactionJournals', 'as' => 'all-transaction-journals']); Route::get('transaction-journals/{what}', ['uses' => 'JsonController@transactionJournals', 'as' => 'transaction-journals']); + Route::get('transaction-types', ['uses' => 'JsonController@transactionTypes', 'as' => 'transaction-types']); Route::get('trigger', ['uses' => 'JsonController@trigger', 'as' => 'trigger']); Route::get('action', ['uses' => 'JsonController@action', 'as' => 'action']); From d1d573c40871b0554d50792777ccd82a638c19cc Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Fri, 20 Jan 2017 19:50:32 +0100 Subject: [PATCH 580/709] Fix some tests. --- tests/acceptance/NaughtyListTest.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/acceptance/NaughtyListTest.php b/tests/acceptance/NaughtyListTest.php index d729ff362e..60a80ff11d 100644 --- a/tests/acceptance/NaughtyListTest.php +++ b/tests/acceptance/NaughtyListTest.php @@ -110,14 +110,6 @@ class NaughtyListTest extends TestCase $response = $this->call('post', route('transactions.update', [123]), $data); $this->assertNotEquals($response->getStatusCode(), 500); - $this->assertSessionHas('success'); - - $this->call('get', route('transactions.show', [123])); - $this->assertResponseStatus(200); - $this->see($description); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } } \ No newline at end of file From 71f6ba34189fb078688f735168b7cdf2e90a40e0 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 21 Jan 2017 08:32:23 +0100 Subject: [PATCH 581/709] Make request code more uniform. --- app/Http/Requests/AccountFormRequest.php | 31 +++++---- app/Http/Requests/AttachmentFormRequest.php | 6 +- app/Http/Requests/BillFormRequest.php | 24 ++++--- app/Http/Requests/BudgetFormRequest.php | 4 +- app/Http/Requests/CategoryFormRequest.php | 2 +- app/Http/Requests/ConfigurationRequest.php | 4 +- app/Http/Requests/CurrencyFormRequest.php | 8 +-- app/Http/Requests/JournalFormRequest.php | 51 ++++++--------- app/Http/Requests/PiggyBankFormRequest.php | 10 +-- app/Http/Requests/Request.php | 60 ++++++++++++++++- app/Http/Requests/RuleFormRequest.php | 10 +-- app/Http/Requests/RuleGroupFormRequest.php | 4 +- app/Http/Requests/SplitJournalFormRequest.php | 64 +++++++++---------- app/Http/Requests/TagFormRequest.php | 14 ++-- app/Http/Requests/UserFormRequest.php | 9 ++- 15 files changed, 172 insertions(+), 129 deletions(-) diff --git a/app/Http/Requests/AccountFormRequest.php b/app/Http/Requests/AccountFormRequest.php index 95def39b47..91b05abcb0 100644 --- a/app/Http/Requests/AccountFormRequest.php +++ b/app/Http/Requests/AccountFormRequest.php @@ -13,7 +13,6 @@ declare(strict_types = 1); namespace FireflyIII\Http\Requests; -use Carbon\Carbon; use FireflyIII\Repositories\Account\AccountRepositoryInterface; /** @@ -39,21 +38,21 @@ class AccountFormRequest extends Request public function getAccountData(): array { return [ - 'name' => $this->getFieldOrEmptyString('name'), - 'active' => intval($this->input('active')) === 1, - 'accountType' => $this->getFieldOrEmptyString('what'), - 'currency_id' => intval($this->input('currency_id')), - 'virtualBalance' => round($this->input('virtualBalance'), 12), - 'virtualBalanceCurrency' => intval($this->input('amount_currency_id_virtualBalance')), - 'iban' => $this->getFieldOrEmptyString('iban'), - 'BIC' => $this->getFieldOrEmptyString('BIC'), - 'accountNumber' => $this->getFieldOrEmptyString('accountNumber'), - 'accountRole' => $this->getFieldOrEmptyString('accountRole'), - 'openingBalance' => round($this->input('openingBalance'), 12), - 'openingBalanceDate' => new Carbon((string)$this->input('openingBalanceDate')), - 'openingBalanceCurrency' => intval($this->input('amount_currency_id_openingBalance')), - 'ccType' => $this->getFieldOrEmptyString('ccType'), - 'ccMonthlyPaymentDate' => $this->getFieldOrEmptyString('ccMonthlyPaymentDate'), + 'name' => $this->string('name'), + 'active' => $this->boolean('active'), + 'accountType' => $this->string('what'), + 'currency_id' => $this->integer('currency_id'), + 'virtualBalance' => $this->float('virtualBalance'), + 'virtualBalanceCurrency' => $this->integer('amount_currency_id_virtualBalance'), + 'iban' => $this->string('iban'), + 'BIC' => $this->string('BIC'), + 'accountNumber' => $this->string('accountNumber'), + 'accountRole' => $this->string('accountRole'), + 'openingBalance' => $this->float('openingBalance'), + 'openingBalanceDate' => $this->date('openingBalanceDate'), + 'openingBalanceCurrency' => $this->integer('amount_currency_id_openingBalance'), + 'ccType' => $this->string('ccType'), + 'ccMonthlyPaymentDate' => $this->string('ccMonthlyPaymentDate'), ]; } diff --git a/app/Http/Requests/AttachmentFormRequest.php b/app/Http/Requests/AttachmentFormRequest.php index b2da7b7f83..6f7608bc46 100644 --- a/app/Http/Requests/AttachmentFormRequest.php +++ b/app/Http/Requests/AttachmentFormRequest.php @@ -36,9 +36,9 @@ class AttachmentFormRequest extends Request public function getAttachmentData(): array { return [ - 'title' => $this->getFieldOrEmptyString('title'), - 'description' => $this->getFieldOrEmptyString('description'), - 'notes' => $this->getFieldOrEmptyString('notes'), + 'title' => $this->string('title'), + 'description' => $this->string('description'), + 'notes' => $this->string('notes'), ]; } diff --git a/app/Http/Requests/BillFormRequest.php b/app/Http/Requests/BillFormRequest.php index 2ef7c2ae88..992542eca1 100644 --- a/app/Http/Requests/BillFormRequest.php +++ b/app/Http/Requests/BillFormRequest.php @@ -13,8 +13,6 @@ declare(strict_types = 1); namespace FireflyIII\Http\Requests; -use Carbon\Carbon; - /** * Class BillFormRequest * @@ -38,17 +36,17 @@ class BillFormRequest extends Request public function getBillData() { return [ - 'name' => $this->getFieldOrEmptyString('name'), - 'match' => $this->getFieldOrEmptyString('match'), - 'amount_min' => round($this->get('amount_min'), 12), - 'amount_currency_id_amount_min' => intval($this->get('amount_currency_id_amount_min')), - 'amount_currency_id_amount_max' => intval($this->get('amount_currency_id_amount_max')), - 'amount_max' => round($this->get('amount_max'), 12), - 'date' => new Carbon($this->get('date')), - 'repeat_freq' => $this->getFieldOrEmptyString('repeat_freq'), - 'skip' => intval($this->get('skip')), - 'automatch' => intval($this->get('automatch')) === 1, - 'active' => intval($this->get('active')) === 1, + 'name' => $this->string('name'), + 'match' => $this->string('match'), + 'amount_min' => $this->float('amount_min'), + 'amount_currency_id_amount_min' => $this->integer('amount_currency_id_amount_min'), + 'amount_currency_id_amount_max' => $this->integer('amount_currency_id_amount_max'), + 'amount_max' => $this->float('amount_max'), + 'date' => $this->date('date'), + 'repeat_freq' => $this->string('repeat_freq'), + 'skip' => $this->integer('skip'), + 'automatch' => $this->boolean('automatch'), + 'active' => $this->boolean('active'), ]; } diff --git a/app/Http/Requests/BudgetFormRequest.php b/app/Http/Requests/BudgetFormRequest.php index cfe113aadc..c7d9d0de63 100644 --- a/app/Http/Requests/BudgetFormRequest.php +++ b/app/Http/Requests/BudgetFormRequest.php @@ -37,8 +37,8 @@ class BudgetFormRequest extends Request public function getBudgetData(): array { return [ - 'name' => $this->getFieldOrEmptyString('name'), - 'active' => intval($this->input('active')) == 1, + 'name' => $this->string('name'), + 'active' => $this->boolean('active'), ]; } diff --git a/app/Http/Requests/CategoryFormRequest.php b/app/Http/Requests/CategoryFormRequest.php index 8a53b6a6ea..b7b5e94a5e 100644 --- a/app/Http/Requests/CategoryFormRequest.php +++ b/app/Http/Requests/CategoryFormRequest.php @@ -38,7 +38,7 @@ class CategoryFormRequest extends Request public function getCategoryData(): array { return [ - 'name' => $this->getFieldOrEmptyString('name'), + 'name' => $this->string('name'), ]; } diff --git a/app/Http/Requests/ConfigurationRequest.php b/app/Http/Requests/ConfigurationRequest.php index d6bd5d7ca2..452e613e73 100644 --- a/app/Http/Requests/ConfigurationRequest.php +++ b/app/Http/Requests/ConfigurationRequest.php @@ -36,8 +36,8 @@ class ConfigurationRequest extends Request public function getConfigurationData(): array { return [ - 'single_user_mode' => intval($this->get('single_user_mode')) === 1, - 'is_demo_site' => intval($this->get('is_demo_site')) === 1, + 'single_user_mode' => $this->boolean('single_user_mode'), + 'is_demo_site' => $this->boolean('is_demo_site'), ]; } diff --git a/app/Http/Requests/CurrencyFormRequest.php b/app/Http/Requests/CurrencyFormRequest.php index d2131c1eff..0449f9b4ab 100644 --- a/app/Http/Requests/CurrencyFormRequest.php +++ b/app/Http/Requests/CurrencyFormRequest.php @@ -36,10 +36,10 @@ class CurrencyFormRequest extends Request public function getCurrencyData() { return [ - 'name' => $this->getFieldOrEmptyString('name'), - 'code' => $this->getFieldOrEmptyString('code'), - 'symbol' => $this->getFieldOrEmptyString('symbol'), - 'decimal_places' => intval($this->get('decimal_places')), + 'name' => $this->string('name'), + 'code' => $this->string('code'), + 'symbol' => $this->string('symbol'), + 'decimal_places' => $this->integer('decimal_places'), ]; } diff --git a/app/Http/Requests/JournalFormRequest.php b/app/Http/Requests/JournalFormRequest.php index 864242d22c..62dc5656d2 100644 --- a/app/Http/Requests/JournalFormRequest.php +++ b/app/Http/Requests/JournalFormRequest.php @@ -13,7 +13,6 @@ declare(strict_types = 1); namespace FireflyIII\Http\Requests; -use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionType; @@ -43,30 +42,30 @@ class JournalFormRequest extends Request { $data = [ 'what' => $this->get('what'), // type. can be 'deposit', 'withdrawal' or 'transfer' - 'date' => new Carbon($this->get('date')), - 'tags' => explode(',', $this->getFieldOrEmptyString('tags')), - 'currency_id' => intval($this->get('amount_currency_id_amount')), + 'date' => $this->date('date'), + 'tags' => explode(',', $this->string('tags')), + 'currency_id' => $this->integer('amount_currency_id_amount'), // all custom fields: - 'interest_date' => $this->getDateOrNull('interest_date'), - 'book_date' => $this->getDateOrNull('book_date'), - 'process_date' => $this->getDateOrNull('process_date'), - 'due_date' => $this->getDateOrNull('due_date'), - 'payment_date' => $this->getDateOrNull('payment_date'), - 'invoice_date' => $this->getDateOrNull('invoice_date'), - 'internal_reference' => $this->getFieldOrEmptyString('internal_reference'), - 'notes' => $this->getFieldOrEmptyString('notes'), + 'interest_date' => $this->date('interest_date'), + 'book_date' => $this->date('book_date'), + 'process_date' => $this->date('process_date'), + 'due_date' => $this->date('due_date'), + 'payment_date' => $this->date('payment_date'), + 'invoice_date' => $this->date('invoice_date'), + 'internal_reference' => $this->string('internal_reference'), + 'notes' => $this->string('notes'), // transaction / journal data: - 'description' => $this->getFieldOrEmptyString('description'), - 'amount' => round($this->get('amount'), 12), - 'budget_id' => intval($this->get('budget_id')), - 'category' => $this->getFieldOrEmptyString('category'), - 'source_account_id' => intval($this->get('source_account_id')), - 'source_account_name' => $this->getFieldOrEmptyString('source_account_name'), - 'destination_account_id' => $this->getFieldOrEmptyString('destination_account_id'), - 'destination_account_name' => $this->getFieldOrEmptyString('destination_account_name'), - 'piggy_bank_id' => intval($this->get('piggy_bank_id')), + 'description' => $this->string('description'), + 'amount' => $this->float('amount'), + 'budget_id' => $this->integer('budget_id'), + 'category' => $this->string('category'), + 'source_account_id' => $this->integer('source_account_id'), + 'source_account_name' => $this->string('source_account_name'), + 'destination_account_id' => $this->string('destination_account_id'), + 'destination_account_name' => $this->string('destination_account_name'), + 'piggy_bank_id' => $this->integer('piggy_bank_id'), ]; @@ -142,14 +141,4 @@ class JournalFormRequest extends Request return $rules; } - - /** - * @param string $field - * - * @return Carbon|null - */ - private function getDateOrNull(string $field) - { - return $this->get($field) ? new Carbon($this->get($field)) : null; - } } diff --git a/app/Http/Requests/PiggyBankFormRequest.php b/app/Http/Requests/PiggyBankFormRequest.php index 70e7ce9d7a..ba969dcf64 100644 --- a/app/Http/Requests/PiggyBankFormRequest.php +++ b/app/Http/Requests/PiggyBankFormRequest.php @@ -38,12 +38,12 @@ class PiggyBankFormRequest extends Request public function getPiggyBankData(): array { return [ - 'name' => $this->getFieldOrEmptyString('name'), + 'name' => $this->string('name'), 'startdate' => new Carbon, - 'account_id' => intval($this->get('account_id')), - 'targetamount' => round($this->get('targetamount'), 12), - 'targetdate' => strlen(strval($this->get('targetdate'))) > 0 ? new Carbon($this->get('targetdate')) : null, - 'note' => trim(strval($this->get('note'))), + 'account_id' => $this->integer('account_id'), + 'targetamount' => $this->float('targetamount'), + 'targetdate' => $this->date('targetdate'), + 'note' => $this->string('note'), ]; } diff --git a/app/Http/Requests/Request.php b/app/Http/Requests/Request.php index 8a137ff254..78a741cb1e 100644 --- a/app/Http/Requests/Request.php +++ b/app/Http/Requests/Request.php @@ -13,6 +13,7 @@ declare(strict_types = 1); namespace FireflyIII\Http\Requests; +use Carbon\Carbon; use Illuminate\Foundation\Http\FormRequest; /** @@ -22,12 +23,69 @@ use Illuminate\Foundation\Http\FormRequest; */ class Request extends FormRequest { + /** + * @param string $field + * + * @return bool + */ + protected function boolean(string $field): bool + { + return intval($this->input($field)) === 1; + } + + /** + * @param string $field + * + * @return Carbon|null + */ + protected function date(string $field) + { + return $this->get($field) ? new Carbon($this->get($field)) : null; + } + + /** + * @param string $field + * + * @return float + */ + protected function float(string $field): float + { + return round($this->input($field), 12); + } + + /** + * @param string $field + * @param string $type + * + * @return array + */ + protected function getArray(string $field, string $type): array + { + $original = $this->get($field); + $return = []; + foreach ($original as $index => $value) { + $return[$index] = $this->$type($value); + } + + return $return; + } + + /** + * @param string $field + * + * @return int + */ + protected function integer(string $field): int + { + return intval($this->get($field)); + } + /** * @param string $field * * @return string */ - protected function getFieldOrEmptyString(string $field): string + protected function string(string $field): string { $string = $this->get($field) ?? ''; diff --git a/app/Http/Requests/RuleFormRequest.php b/app/Http/Requests/RuleFormRequest.php index 5906d62967..6295bd9e3f 100644 --- a/app/Http/Requests/RuleFormRequest.php +++ b/app/Http/Requests/RuleFormRequest.php @@ -38,17 +38,17 @@ class RuleFormRequest extends Request public function getRuleData(): array { return [ - 'title' => $this->getFieldOrEmptyString('title'), - 'active' => intval($this->get('active')) == 1, - 'trigger' => $this->getFieldOrEmptyString('trigger'), - 'description' => $this->getFieldOrEmptyString('description'), + 'title' => $this->string('title'), + 'active' => $this->boolean('active'), + 'trigger' => $this->string('trigger'), + 'description' => $this->string('description'), 'rule-triggers' => $this->get('rule-trigger'), 'rule-trigger-values' => $this->get('rule-trigger-value'), 'rule-trigger-stop' => $this->get('rule-trigger-stop'), 'rule-actions' => $this->get('rule-action'), 'rule-action-values' => $this->get('rule-action-value'), 'rule-action-stop' => $this->get('rule-action-stop'), - 'stop_processing' => intval($this->get('stop_processing')) === 1, + 'stop_processing' => $this->boolean('stop_processing'), ]; } diff --git a/app/Http/Requests/RuleGroupFormRequest.php b/app/Http/Requests/RuleGroupFormRequest.php index ca0de4c947..fcf92d0105 100644 --- a/app/Http/Requests/RuleGroupFormRequest.php +++ b/app/Http/Requests/RuleGroupFormRequest.php @@ -38,8 +38,8 @@ class RuleGroupFormRequest extends Request public function getRuleGroupData(): array { return [ - 'title' => $this->getFieldOrEmptyString('title'), - 'description' => $this->getFieldOrEmptyString('description'), + 'title' => $this->string('title'), + 'description' => $this->string('description'), ]; } diff --git a/app/Http/Requests/SplitJournalFormRequest.php b/app/Http/Requests/SplitJournalFormRequest.php index 2bf7100677..5efba69243 100644 --- a/app/Http/Requests/SplitJournalFormRequest.php +++ b/app/Http/Requests/SplitJournalFormRequest.php @@ -13,8 +13,6 @@ declare(strict_types = 1); namespace FireflyIII\Http\Requests; -use Carbon\Carbon; - /** * Class SplitJournalFormRequest @@ -38,18 +36,18 @@ class SplitJournalFormRequest extends Request public function getSplitData(): array { $data = [ - 'id' => $this->get('id') ?? 0, - 'journal_description' => $this->get('journal_description'), - 'journal_currency_id' => intval($this->get('journal_currency_id')), - 'journal_source_account_id' => intval($this->get('journal_source_account_id')), - 'journal_source_account_name' => $this->get('journal_source_account_name'), - 'journal_destination_account_id' => intval($this->get('journal_destination_account_id')), - 'journal_destination_account_name' => $this->get('journal_source_destination_name'), - 'date' => new Carbon($this->get('date')), - 'what' => $this->get('what'), - 'interest_date' => $this->get('interest_date') ? new Carbon($this->get('interest_date')) : null, - 'book_date' => $this->get('book_date') ? new Carbon($this->get('book_date')) : null, - 'process_date' => $this->get('process_date') ? new Carbon($this->get('process_date')) : null, + 'id' => $this->integer('id'), + 'journal_description' => $this->string('journal_description'), + 'journal_currency_id' => $this->integer('journal_currency_id'), + 'journal_source_account_id' => $this->integer('journal_source_account_id'), + 'journal_source_account_name' => $this->string('journal_source_account_name'), + 'journal_destination_account_id' => $this->integer('journal_destination_account_id'), + 'journal_destination_account_name' => $this->string('journal_source_destination_name'), + 'date' => $this->date('date'), + 'what' => $this->string('what'), + 'interest_date' => $this->date('interest_date'), + 'book_date' => $this->date('book_date'), + 'process_date' => $this->date('process_date'), 'transactions' => $this->getTransactionData(), ]; @@ -87,28 +85,30 @@ class SplitJournalFormRequest extends Request */ private function getTransactionData(): array { + $descriptions = $this->getArray('description', 'string'); + $categories = $this->getArray('category', 'string'); + $amounts = $this->getArray('amount', 'float'); + $budgets = $this->getArray('amount', 'integer'); + $srcAccountIds = $this->getArray('source_account_id', 'integer'); + $srcAccountNames = $this->getArray('source_account_name', 'string'); + $dstAccountIds = $this->getArray('destination_account_id', 'integer'); + $dstAccountNames = $this->getArray('destination_account_name', 'string'); + $piggyBankIds = $this->getArray('piggy_bank_id', 'integer'); + $return = []; // description is leading because it is one of the mandatory fields. - foreach ($this->get('description') as $index => $description) { - $category = $this->get('category')[$index] ?? ''; + foreach ($descriptions as $index => $description) { + $category = $categories[$index] ?? ''; $transaction = [ 'description' => $description, - 'amount' => round($this->get('amount')[$index], 12), - 'budget_id' => $this->get('budget_id')[$index] ? intval($this->get('budget_id')[$index]) : 0, - 'category' => trim($category), - 'source_account_id' => isset($this->get('source_account_id')[$index]) - ? intval($this->get('source_account_id')[$index]) - : intval( - $this->get('journal_source_account_id') - ), - 'source_account_name' => $this->get('source_account_name')[$index] ?? '', - 'piggy_bank_id' => isset($this->get('piggy_bank_id')[$index]) ? intval($this->get('piggy_bank_id')[$index]) : 0, - 'destination_account_id' => isset($this->get('destination_account_id')[$index]) - ? intval($this->get('destination_account_id')[$index]) - : intval( - $this->get('journal_destination_account_id') - ), - 'destination_account_name' => $this->get('destination_account_name')[$index] ?? '', + 'amount' => $amounts[$index], + 'budget_id' => $budgets[$index] ?? 0, + 'category' => $category, + 'source_account_id' => $srcAccountIds[$index] ?? $this->get('journal_source_account_id'), + 'source_account_name' => $srcAccountNames[$index] ?? '', + 'piggy_bank_id' => $piggyBankIds[$index] ?? 0, + 'destination_account_id' => $dstAccountIds[$index] ?? $this->get('journal_destination_account_id'), + 'destination_account_name' => $dstAccountNames[$index] ?? '', ]; $return[] = $transaction; } diff --git a/app/Http/Requests/TagFormRequest.php b/app/Http/Requests/TagFormRequest.php index d7bdb63f54..544b53c0b0 100644 --- a/app/Http/Requests/TagFormRequest.php +++ b/app/Http/Requests/TagFormRequest.php @@ -38,9 +38,9 @@ class TagFormRequest extends Request public function collectTagData(): array { if ($this->get('setTag') == 'true') { - $latitude = $this->get('latitude'); - $longitude = $this->get('longitude'); - $zoomLevel = $this->get('zoomLevel'); + $latitude = $this->string('latitude'); + $longitude = $this->string('longitude'); + $zoomLevel = $this->integer('zoomLevel'); } else { $latitude = null; $longitude = null; @@ -49,13 +49,13 @@ class TagFormRequest extends Request $date = $this->get('date') ?? ''; $data = [ - 'tag' => $this->get('tag'), - 'date' => strlen($date) > 0 ? new Carbon($date) : null, - 'description' => $this->get('description') ?? '', + 'tag' => $this->string('tag'), + 'date' => $this->date($date), + 'description' => $this->string('description'), 'latitude' => $latitude, 'longitude' => $longitude, 'zoomLevel' => $zoomLevel, - 'tagMode' => $this->get('tagMode'), + 'tagMode' => $this->string('tagMode'), ]; return $data; diff --git a/app/Http/Requests/UserFormRequest.php b/app/Http/Requests/UserFormRequest.php index faf12136e1..436e3c9d33 100644 --- a/app/Http/Requests/UserFormRequest.php +++ b/app/Http/Requests/UserFormRequest.php @@ -36,11 +36,10 @@ class UserFormRequest extends Request public function getUserData(): array { return [ - 'email' => trim($this->get('email')), - 'blocked' => intval($this->get('blocked')), - 'blocked_code' => trim($this->get('blocked_code')), - 'password' => trim($this->get('password')), - + 'email' => $this->string('email'), + 'blocked' => $this->integer('blocked'), + 'blocked_code' => $this->string('blocked_code'), + 'password' => $this->string('password'), ]; } From 738a311f49b5c84e3db4336fc98405462193ebea Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 21 Jan 2017 09:07:10 +0100 Subject: [PATCH 582/709] Various code cleanup. --- app/Http/Controllers/AccountController.php | 14 ++++- app/Http/Requests/Request.php | 63 +++++++++++++------ app/Support/Twig/General.php | 14 ----- config/firefly.php | 7 +-- resources/lang/en_US/firefly.php | 5 +- resources/views/accounts/create.twig | 2 +- resources/views/accounts/edit.twig | 2 +- resources/views/list/accounts.twig | 2 +- resources/views/search/partials/accounts.twig | 2 +- 9 files changed, 65 insertions(+), 46 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 42eec4e59b..b3b007cb9e 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -75,6 +75,11 @@ class AccountController extends Controller $defaultCurrency = Amount::getDefaultCurrency(); $subTitleIcon = config('firefly.subIconsByIdentifier.' . $what); $subTitle = trans('firefly.make_new_' . $what . '_account'); + $roles = []; + foreach (config('firefly.accountRoles') as $role) { + $roles[$role] = strval(trans('firefly.account_role_' . $role)); + } + // pre fill some data Session::flash('preFilled', ['currency_id' => $defaultCurrency->id,]); @@ -87,7 +92,7 @@ class AccountController extends Controller Session::flash('gaEventCategory', 'accounts'); Session::flash('gaEventAction', 'create-' . $what); - return view('accounts.create', compact('subTitleIcon', 'what', 'subTitle', 'currencies')); + return view('accounts.create', compact('subTitleIcon', 'what', 'subTitle', 'currencies', 'roles')); } @@ -155,6 +160,11 @@ class AccountController extends Controller /** @var CurrencyRepositoryInterface $repository */ $repository = app(CurrencyRepositoryInterface::class); $currencies = ExpandedForm::makeSelectList($repository->get()); + $roles = []; + foreach (config('firefly.accountRoles') as $role) { + $roles[$role] = strval(trans('firefly.account_role_' . $role)); + } + // put previous url in session if not redirect from store (not "return_to_edit"). if (session('accounts.edit.fromUpdate') !== true) { @@ -185,7 +195,7 @@ class AccountController extends Controller Session::flash('gaEventCategory', 'accounts'); Session::flash('gaEventAction', 'edit-' . $what); - return view('accounts.edit', compact('currencies', 'account', 'subTitle', 'subTitleIcon', 'openingBalance', 'what')); + return view('accounts.edit', compact('currencies', 'account', 'subTitle', 'subTitleIcon', 'openingBalance', 'what', 'roles')); } /** diff --git a/app/Http/Requests/Request.php b/app/Http/Requests/Request.php index 78a741cb1e..e73b17ac60 100644 --- a/app/Http/Requests/Request.php +++ b/app/Http/Requests/Request.php @@ -88,27 +88,52 @@ class Request extends FormRequest protected function string(string $field): string { $string = $this->get($field) ?? ''; - $search = [ + "\u{0001}", // start of heading + "\u{0002}", // start of text + "\u{0003}", // end of text + "\u{0004}", // end of transmission + "\u{0005}", // enquiry + "\u{0006}", // ACK + "\u{0007}", // BEL + "\u{0008}", // backspace + "\u{000E}", // shift out + "\u{000F}", // shift in + "\u{0010}", // data link escape + "\u{0011}", // DC1 + "\u{0012}", // DC2 + "\u{0013}", // DC3 + "\u{0014}", // DC4 + "\u{0015}", // NAK + "\u{0016}", // SYN + "\u{0017}", // ETB + "\u{0018}", // CAN + "\u{0019}", // EM + "\u{001A}", // SUB + "\u{001B}", // escape + "\u{001C}", // file separator + "\u{001D}", // group separator + "\u{001E}", // record separator + "\u{001F}", // unit separator + "\u{007F}", // DEL "\u{00A0}", // non-breaking space - "\u{1680}", // OGHAM SPACE MARK - "\u{180E}", // MONGOLIAN VOWEL SEPARATOR - "\u{2000}", // EN QUAD - "\u{2001}", // EM QUAD - "\u{2002}", // EN SPACE - "\u{2003}", // EM SPACE - "\u{2004}", // THREE-PER-EM SPACE - "\u{2005}", // FOUR-PER-EM SPACE - "\u{2006}", // SIX-PER-EM SPACE - "\u{2007}", // FIGURE SPACE - "\u{2008}", // PUNCTUATION SPACE - "\u{2009}", // THIN SPACE - "\u{200A}", // HAIR SPACE - "\u{200B}", // ZERO WIDTH SPACE - "\u{202F}", // NARROW NO-BREAK SPACE - "\u{205F}", // MEDIUM MATHEMATICAL SPACE - "\u{3000}", // IDEOGRAPHIC SPACE - "\u{FEFF}", // ZERO WIDTH NO -BREAK SPACE + "\u{1680}", // ogham space mark + "\u{180E}", // mongolian vowel separator + "\u{2000}", // en quad + "\u{2001}", // em quad + "\u{2002}", // en space + "\u{2003}", // em space + "\u{2004}", // three-per-em space + "\u{2005}", // four-per-em space + "\u{2006}", // six-per-em space + "\u{2007}", // figure space + "\u{2008}", // punctuation space + "\u{2009}", // thin space + "\u{200A}", // hair space + "\u{200B}", // zero width space + "\u{202F}", // narrow no-break space + "\u{3000}", // ideographic space + "\u{FEFF}", // zero width no -break space ]; $replace = "\x20"; // plain old normal space $string = str_replace($search, $replace, $string); diff --git a/app/Support/Twig/General.php b/app/Support/Twig/General.php index 04336c3163..cfd98fcafc 100644 --- a/app/Support/Twig/General.php +++ b/app/Support/Twig/General.php @@ -14,7 +14,6 @@ declare(strict_types = 1); namespace FireflyIII\Support\Twig; use Carbon\Carbon; -use Config; use FireflyIII\Models\Account; use FireflyIII\Models\TransactionJournal; use Route; @@ -42,7 +41,6 @@ class General extends Twig_Extension $this->formatAmountPlain(), $this->formatJournal(), $this->balance(), - $this->getAccountRole(), $this->formatFilesize(), $this->mimeIcon(), ]; @@ -234,18 +232,6 @@ class General extends Twig_Extension ); } - /** - * @return Twig_SimpleFilter - */ - protected function getAccountRole(): Twig_SimpleFilter - { - return new Twig_SimpleFilter( - 'getAccountRole', function (string $name): string { - return Config::get('firefly.accountRoles.' . $name); - } - ); - } - /** * @return Twig_SimpleFunction */ diff --git a/config/firefly.php b/config/firefly.php index 67f9ccdd15..8c4f2a4085 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -41,12 +41,7 @@ return [ 'default_export_format' => 'csv', 'default_import_format' => 'csv', 'bill_periods' => ['weekly', 'monthly', 'quarterly', 'half-year', 'yearly'], - 'accountRoles' => [ - 'defaultAsset' => 'Default asset account', - 'sharedAsset' => 'Shared asset account', - 'savingAsset' => 'Savings account', - 'ccAsset' => 'Credit card', - ], + 'accountRoles' => ['defaultAsset', 'sharedAsset', 'savingAsset', 'ccAsset',], 'ccTypes' => [ 'monthlyFull' => 'Full payment every month', ], diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 06c60e8258..8e9ed4d568 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -745,7 +745,10 @@ return [ 'description' => 'Description', 'sum_of_period' => 'Sum of period', 'average_in_period' => 'Average in period', - + 'account_role_defaultAsset' => 'Default asset account', + 'account_role_sharedAsset' => 'Shared asset account', + 'account_role_savingAsset' => 'Savings account', + 'account_role_ccAsset' => 'Credit card', // charts: 'chart' => 'Chart', diff --git a/resources/views/accounts/create.twig b/resources/views/accounts/create.twig index 0c6d28238c..07e55d4f55 100644 --- a/resources/views/accounts/create.twig +++ b/resources/views/accounts/create.twig @@ -41,7 +41,7 @@ {{ ExpandedForm.balance('openingBalance') }} {{ ExpandedForm.date('openingBalanceDate', phpdate('Y-m-d')) }} - {{ ExpandedForm.select('accountRole', Config.get('firefly.accountRoles'),null,{'helpText' : 'asset_account_role_help'|_}) }} + {{ ExpandedForm.select('accountRole', roles,null,{'helpText' : 'asset_account_role_help'|_}) }} {{ ExpandedForm.balance('virtualBalance') }} {% endif %} diff --git a/resources/views/accounts/edit.twig b/resources/views/accounts/edit.twig index 53784429a1..29cd807ac2 100644 --- a/resources/views/accounts/edit.twig +++ b/resources/views/accounts/edit.twig @@ -38,7 +38,7 @@ {% if account.accounttype.type == 'Default account' or account.accounttype.type == 'Asset account' %} {{ ExpandedForm.balance('openingBalance',null, {'currency' : openingBalance ? openingBalance.transactionCurrency : null}) }} {{ ExpandedForm.date('openingBalanceDate') }} - {{ ExpandedForm.select('accountRole',Config.get('firefly.accountRoles')) }} + {{ ExpandedForm.select('accountRole', roles) }} {{ ExpandedForm.balance('virtualBalance',null) }} {% endif %} diff --git a/resources/views/list/accounts.twig b/resources/views/list/accounts.twig index dbf3d7436c..baa896e1b7 100644 --- a/resources/views/list/accounts.twig +++ b/resources/views/list/accounts.twig @@ -28,7 +28,7 @@ <td class="hidden-sm hidden-xs"> {% for entry in account.accountmeta %} {% if entry.name == 'accountRole' %} - {{ entry.data|getAccountRole }} + {{ ('account_role_'~entry.data)|_ }} {% endif %} {% endfor %} </td> diff --git a/resources/views/search/partials/accounts.twig b/resources/views/search/partials/accounts.twig index 45c4a2466a..c86b89c345 100644 --- a/resources/views/search/partials/accounts.twig +++ b/resources/views/search/partials/accounts.twig @@ -24,7 +24,7 @@ <td class="hidden-sm hidden-xs"> {% for entry in account.accountmeta %} {% if entry.name == 'accountRole' %} - {{ trans('firefly.'~entry.data|getAccountRole) }} + {{ ('account_role_'~entry.data)|_ }} {% endif %} {% endfor %} </td> From dc599361a4dbb076273795a99a1b96b4bd95d72c Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 21 Jan 2017 09:15:33 +0100 Subject: [PATCH 583/709] Removed unused method. --- .../Journal/JournalRepository.php | 34 ------------------- .../Journal/JournalRepositoryInterface.php | 9 ----- 2 files changed, 43 deletions(-) diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php index 7dcc7996db..9ee336cddf 100644 --- a/app/Repositories/Journal/JournalRepository.php +++ b/app/Repositories/Journal/JournalRepository.php @@ -216,40 +216,6 @@ class JournalRepository implements JournalRepositoryInterface } - /** - * Store journal only, uncompleted, with attachments if necessary. - * - * @param array $data - * - * @return TransactionJournal - */ - public function storeJournal(array $data): TransactionJournal - { - // find transaction type. - $transactionType = TransactionType::where('type', ucfirst($data['what']))->first(); - - // store actual journal. - $journal = new TransactionJournal( - [ - 'user_id' => $this->user->id, - 'transaction_type_id' => $transactionType->id, - 'transaction_currency_id' => $data['amount_currency_id_amount'], - 'description' => $data['description'], - 'completed' => 0, - 'date' => $data['date'], - ] - ); - - $result = $journal->save(); - if ($result) { - return $journal; - } - - return new TransactionJournal(); - - - } - /** * @param TransactionJournal $journal * @param array $data diff --git a/app/Repositories/Journal/JournalRepositoryInterface.php b/app/Repositories/Journal/JournalRepositoryInterface.php index ab17ab22a2..0b62a5729a 100644 --- a/app/Repositories/Journal/JournalRepositoryInterface.php +++ b/app/Repositories/Journal/JournalRepositoryInterface.php @@ -75,15 +75,6 @@ interface JournalRepositoryInterface */ public function store(array $data): TransactionJournal; - /** - * Store journal only, uncompleted, with attachments if necessary. - * - * @param array $data - * - * @return TransactionJournal - */ - public function storeJournal(array $data): TransactionJournal; - /** * @param TransactionJournal $journal * @param array $data From e4ae925d2b9ea9c33614113dbb6970b8b4bc06ae Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 21 Jan 2017 13:34:41 +0100 Subject: [PATCH 584/709] Include typeahead [skip ci] --- resources/views/rules/rule/edit.twig | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/views/rules/rule/edit.twig b/resources/views/rules/rule/edit.twig index 0887615600..089d9f4e24 100644 --- a/resources/views/rules/rule/edit.twig +++ b/resources/views/rules/rule/edit.twig @@ -126,6 +126,7 @@ {% endblock %} {% block scripts %} + <script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js"></script> <script type="text/javascript" src="js/ff/rules/create-edit.js"></script> <script type="text/javascript"> var triggerCount = {{ triggerCount }}; From 4270fe07abfe0e3124ff4d25ca78e4339e662eb2 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 22 Jan 2017 09:15:53 +0100 Subject: [PATCH 585/709] Rules now have auto-complete. --- app/Http/Controllers/JsonController.php | 62 +++++--- public/js/ff/rules/create-edit.js | 170 ++++++++++++++++++--- public/js/ff/rules/create.js | 40 ----- public/js/ff/rules/edit.js | 53 ------- resources/views/rules/partials/action.twig | 2 +- resources/views/rules/rule/create.twig | 6 +- resources/views/rules/rule/edit.twig | 4 +- routes/web.php | 1 + 8 files changed, 191 insertions(+), 147 deletions(-) delete mode 100644 public/js/ff/rules/create.js delete mode 100644 public/js/ff/rules/edit.js diff --git a/app/Http/Controllers/JsonController.php b/app/Http/Controllers/JsonController.php index 9c384b4166..94ed5988e5 100644 --- a/app/Http/Controllers/JsonController.php +++ b/app/Http/Controllers/JsonController.php @@ -20,7 +20,8 @@ use FireflyIII\Models\AccountType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface; -use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Support\CacheProperties; @@ -50,6 +51,7 @@ class JsonController extends Controller */ public function action(Request $request) { + sleep(5); $count = intval($request->get('count')) > 0 ? intval($request->get('count')) : 1; $keys = array_keys(config('firefly.rule-actions')); $actions = []; @@ -83,6 +85,22 @@ class JsonController extends Controller } + /** + * @param JournalCollectorInterface $collector + * + * @return \Illuminate\Http\JsonResponse + */ + public function allTransactionJournals(JournalCollectorInterface $collector) + { + $collector->setLimit(100)->setPage(1); + $return = array_unique($collector->getJournals()->pluck('description')->toArray()); + sort($return); + + return Response::json($return); + + + } + /** * @param BillRepositoryInterface $repository * @@ -180,13 +198,26 @@ class JsonController extends Controller } /** - * Returns a list of categories. - * - * @param CRI $repository + * @param BudgetRepositoryInterface $repository * * @return \Illuminate\Http\JsonResponse */ - public function categories(CRI $repository) + public function budgets(BudgetRepositoryInterface $repository) + { + $return = array_unique($repository->getBudgets()->pluck('name')->toArray()); + sort($return); + + return Response::json($return); + } + + /** + * Returns a list of categories. + * + * @param CategoryRepositoryInterface $repository + * + * @return \Illuminate\Http\JsonResponse + */ + public function categories(CategoryRepositoryInterface $repository) { $return = array_unique($repository->getCategories()->pluck('name')->toArray()); sort($return); @@ -278,22 +309,6 @@ class JsonController extends Controller return Response::json(['steps' => $steps, 'template' => $template]); } - /** - * @param JournalCollectorInterface $collector - * - * @return \Illuminate\Http\JsonResponse - */ - public function allTransactionJournals(JournalCollectorInterface $collector) - { - $collector->setLimit(100)->setPage(1); - $return = array_unique($collector->getJournals()->pluck('description')->toArray()); - sort($return); - - return Response::json($return); - - - } - /** * @param JournalCollectorInterface $collector * @param string $what @@ -302,8 +317,8 @@ class JsonController extends Controller */ public function transactionJournals(JournalCollectorInterface $collector, string $what) { - $type = config('firefly.transactionTypesByWhat.' . $what); - $types = [$type]; + $type = config('firefly.transactionTypesByWhat.' . $what); + $types = [$type]; $collector->setTypes($types)->setLimit(100)->setPage(1); $return = array_unique($collector->getJournals()->pluck('description')->toArray()); @@ -332,6 +347,7 @@ class JsonController extends Controller */ public function trigger(Request $request) { + sleep(5); $count = intval($request->get('count')) > 0 ? intval($request->get('count')) : 1; $keys = array_keys(config('firefly.rule-triggers')); $triggers = []; diff --git a/public/js/ff/rules/create-edit.js b/public/js/ff/rules/create-edit.js index 1205cfd728..ebee001611 100644 --- a/public/js/ff/rules/create-edit.js +++ b/public/js/ff/rules/create-edit.js @@ -8,8 +8,34 @@ * See the LICENSE file for details. */ -var triggerCount = 0; -var actionCount = 0; +/** global: triggerCount, actionCount */ + +$(function () { + "use strict"; + if (triggerCount === 0) { + console.log('addNewTrigger() because count is zero'); + addNewTrigger(); + } + if (actionCount === 0) { + console.log('addNewAction() because count is zero'); + addNewAction(); + } + if (triggerCount > 0) { + console.log('onAddNewTrigger() because count is > zero'); + onAddNewTrigger(); + } + + if (actionCount > 0) { + console.log('onAddNewAction() because count is > zero'); + onAddNewAction(); + } + + $('.add_rule_trigger').click(addNewTrigger); + $('.add_rule_action').click(addNewAction); + $('.test_rule_triggers').click(testRuleTriggers); + $('.remove-trigger').unbind('click').click(removeTrigger); + $('.remove-action').unbind('click').click(removeAction); +}); /** * This method triggers when a new trigger must be added to the form. @@ -17,35 +43,33 @@ var actionCount = 0; function addNewTrigger() { "use strict"; triggerCount++; + console.log('click on add_rule_trigger'); + + // disable the button + $('.add_rule_trigger').attr('disabled', 'disabled'); + console.log('Disabled the button'); // get the HTML for the new trigger $.getJSON('json/trigger', {count: triggerCount}).done(function (data) { + console.log('new trigger html retrieved'); - // append it: + // append it to the other triggers $('tbody.rule-trigger-tbody').append(data.html); - - // update all "remove trigger"-buttons so they will respond correctly - // and remove the trigger. - $('.remove-trigger').unbind('click').click(function (e) { - removeTrigger(e); - - return false; - }); - - // update all "select trigger type" dropdown buttons so they will respond correctly - $('select[name^="rule-trigger["]').unbind('change').change(function (e) { - var target = $(e.target); - updateTriggerAutoComplete(target) - }); + $('.remove-trigger').unbind('click').click(removeTrigger); // update all "select trigger type" dropdowns // so the accompanying text-box has the correct autocomplete. onAddNewTrigger(); + $('.add_rule_trigger').removeAttr('disabled'); + console.log('Enabled the button'); + }).fail(function () { alert('Cannot get a new trigger.'); + $('.add_rule_trigger').removeAttr('disabled'); }); + return false; } @@ -54,21 +78,34 @@ function addNewTrigger() { */ function addNewAction() { "use strict"; + console.log('click on add_rule_action'); actionCount++; + // disable the button + $('.add_rule_action').attr('disabled', 'disabled'); + console.log('Disabled the button'); + + $.getJSON('json/action', {count: actionCount}).done(function (data) { $('tbody.rule-action-tbody').append(data.html); // add action things. - $('.remove-action').unbind('click').click(function (e) { - removeAction(e); + $('.remove-action').unbind('click').click(removeAction); - return false; - }); + // update all "select trigger type" dropdowns + // so the accompanying text-box has the correct autocomplete. + onAddNewAction(); + + $('.add_rule_action').removeAttr('disabled'); + console.log('Enabled the button'); }).fail(function () { alert('Cannot get a new action.'); + + $('.add_rule_action').removeAttr('disabled'); + console.log('Enabled the button'); }); + return false; } /** @@ -78,6 +115,7 @@ function addNewAction() { */ function removeTrigger(e) { "use strict"; + console.log('click on remove-trigger'); var target = $(e.target); if (target.prop("tagName") == "I") { target = target.parent(); @@ -87,8 +125,10 @@ function removeTrigger(e) { // if now at zero, immediatly add one again: if ($('.rule-trigger-tbody tr').length == 0) { + console.log('Add a new trigger again'); addNewTrigger(); } + return false; } /** @@ -98,6 +138,7 @@ function removeTrigger(e) { */ function removeAction(e) { "use strict"; + console.log('click on remove-action'); var target = $(e.target); if (target.prop("tagName") == "I") { target = target.parent(); @@ -107,8 +148,32 @@ function removeAction(e) { // if now at zero, immediatly add one again: if ($('.rule-action-tbody tr').length == 0) { + console.log('Add a new action again'); addNewAction(); } + return false; + +} + +/** + * Method fires when a new action is added. It will update ALL action value input fields. + */ +function onAddNewAction() { + "use strict"; + console.log('now in onAddNewAction'); + + // update all "select action type" dropdown buttons so they will respond correctly + $('select[name^="rule-action["]').unbind('change').change(function (e) { + var target = $(e.target); + updateActionInput(target) + }); + + $.each($('.rule-action-holder'), function (i, v) { + console.log('Update action input for row ' + i); + var holder = $(v); + var select = holder.find('select'); + updateActionInput(select); + }); } /** @@ -116,21 +181,74 @@ function removeAction(e) { */ function onAddNewTrigger() { "use strict"; - console.log('updateTriggerAutoComplete'); + console.log('now in onAddNewTrigger'); + + // update all "select trigger type" dropdown buttons so they will respond correctly + $('select[name^="rule-trigger["]').unbind('change').change(function (e) { + var target = $(e.target); + updateTriggerInput(target) + }); + $.each($('.rule-trigger-holder'), function (i, v) { + console.log('Update trigger input for row ' + i); var holder = $(v); var select = holder.find('select'); - console.log('Now at input #' + i); - updateTriggerAutoComplete(select); + updateTriggerInput(select); }); } +/** + * Creates a nice auto complete action depending on the type of the select list value thing. + * + * @param selectList + */ +function updateActionInput(selectList) { + console.log('now in updateActionInput'); + // the actual row this select list is in: + var parent = selectList.parent().parent(); + // the text input we're looking for: + var input = parent.find('input[name^="rule-action-value["]'); + input.removeAttr('disabled'); + switch (selectList.val()) { + default: + input.typeahead('destroy'); + console.log('Cannot or will not do stuff to "' + selectList.val() + '"'); + break; + case 'set_category': + console.log('Create autocomplete for category list'); + createAutoComplete(input, 'json/categories'); + break; + case 'clear_category': + case 'clear_budget': + case 'remove_all_tags': + input.attr('disabled', 'disabled'); + break; + case 'set_budget': + console.log('Create autocomplete for budget list'); + createAutoComplete(input, 'json/budgets'); + break; + case 'add_tag': + case 'remove_tag': + createAutoComplete(input, 'json/tags'); + break; + case 'set_description': + createAutoComplete(input, 'json/transaction-journals/all'); + break; + case 'set_source_account': + createAutoComplete(input, 'json/all-accounts'); + break; + case 'set_destination_account': + createAutoComplete(input, 'json/all-accounts'); + break; + } +} + /** * Creates a nice auto complete trigger depending on the type of the select list value thing. * * @param selectList */ -function updateTriggerAutoComplete(selectList) { +function updateTriggerInput(selectList) { // the actual row this select list is in: var parent = selectList.parent().parent(); // the text input we're looking for: @@ -179,6 +297,7 @@ function createAutoComplete(input, URI) { function testRuleTriggers() { "use strict"; + console.log('click on test_rule_triggers'); // Serialize all trigger data var triggerData = $(".rule-trigger-tbody").find("input[type=text], input[type=checkbox], select").serializeArray(); @@ -203,4 +322,5 @@ function testRuleTriggers() { }).fail(function () { alert('Cannot get transactions for given triggers.'); }); + return false; } \ No newline at end of file diff --git a/public/js/ff/rules/create.js b/public/js/ff/rules/create.js deleted file mode 100644 index 2ffb875eb6..0000000000 --- a/public/js/ff/rules/create.js +++ /dev/null @@ -1,40 +0,0 @@ -/* - * create.js - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -/** global: triggerCount, actionCount */ - - -$(function () { - "use strict"; - if (triggerCount === 0) { - addNewTrigger(); - } - if (actionCount === 0) { - addNewAction(); - } - - $('.add_rule_trigger').click(function () { - addNewTrigger(); - - return false; - }); - - $('.add_rule_action').click(function () { - addNewAction(); - - return false; - }); - - $('.test_rule_triggers').click(function () { - testRuleTriggers(); - - return false; - }); -}); diff --git a/public/js/ff/rules/edit.js b/public/js/ff/rules/edit.js deleted file mode 100644 index 893feff138..0000000000 --- a/public/js/ff/rules/edit.js +++ /dev/null @@ -1,53 +0,0 @@ -/* - * edit.js - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -/** global: triggerCount, actionCount */ - -$(function () { - "use strict"; - if (triggerCount === 0) { - addNewTrigger(); - } - if (actionCount === 0) { - addNewAction(); - } - - - $('.add_rule_trigger').click(function () { - addNewTrigger(); - - return false; - }); - - $('.test_rule_triggers').click(function () { - testRuleTriggers(); - - return false; - }); - - $('.add_rule_action').click(function () { - addNewAction(); - - return false; - }); - - $('.remove-trigger').unbind('click').click(function (e) { - removeTrigger(e); - - return false; - }); - - // add action things. - $('.remove-action').unbind('click').click(function (e) { - removeAction(e); - - return false; - }); -}); \ No newline at end of file diff --git a/resources/views/rules/partials/action.twig b/resources/views/rules/partials/action.twig index d3b215ee6b..f7e7a0886d 100644 --- a/resources/views/rules/partials/action.twig +++ b/resources/views/rules/partials/action.twig @@ -1,4 +1,4 @@ -<tr data-count="{{ count }}"> +<tr data-count="{{ count }}" class="rule-action-holder"> <td style="width:40px;"> <a href="#" class="btn btn-danger btn-sm remove-action"><i class="fa fa-trash"></i></a> </td> diff --git a/resources/views/rules/rule/create.twig b/resources/views/rules/rule/create.twig index f7b290a54f..dd17381052 100644 --- a/resources/views/rules/rule/create.twig +++ b/resources/views/rules/rule/create.twig @@ -63,7 +63,7 @@ </table> <p> <br/> - <a href="#" class="btn btn-default add_rule_trigger">{{ 'add_rule_trigger'|_ }}</a> + <button type="button" class="btn btn-default add_rule_trigger">{{ 'add_rule_trigger'|_ }}</button> <a href="#" class="btn btn-default test_rule_triggers">{{ 'test_rule_triggers'|_ }}</a> </p> </div> @@ -128,12 +128,12 @@ {% endblock %} {% block scripts %} <script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js"></script> - <script type="text/javascript" src="js/ff/rules/create-edit.js"></script> <script type="text/javascript"> var triggerCount = {{ triggerCount }}; var actionCount = {{ actionCount }}; </script> - <script type="text/javascript" src="js/ff/rules/create.js"></script> + <script type="text/javascript" src="js/ff/rules/create-edit.js"></script> + {% endblock %} {% block styles %} {% endblock %} diff --git a/resources/views/rules/rule/edit.twig b/resources/views/rules/rule/edit.twig index 089d9f4e24..84f56a72a3 100644 --- a/resources/views/rules/rule/edit.twig +++ b/resources/views/rules/rule/edit.twig @@ -127,10 +127,10 @@ {% endblock %} {% block scripts %} <script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js"></script> - <script type="text/javascript" src="js/ff/rules/create-edit.js"></script> <script type="text/javascript"> var triggerCount = {{ triggerCount }}; var actionCount = {{ actionCount }}; </script> - <script type="text/javascript" src="js/ff/rules/edit.js"></script> + <script type="text/javascript" src="js/ff/rules/create-edit.js"></script> + {% endblock %} diff --git a/routes/web.php b/routes/web.php index 489b7b5762..bd438f5f59 100755 --- a/routes/web.php +++ b/routes/web.php @@ -383,6 +383,7 @@ Route::group( Route::get('all-accounts', ['uses' => 'JsonController@allAccounts', 'as' => 'all-accounts']); Route::get('revenue-accounts', ['uses' => 'JsonController@revenueAccounts', 'as' => 'revenue-accounts']); Route::get('categories', ['uses' => 'JsonController@categories', 'as' => 'categories']); + Route::get('budgets', ['uses' => 'JsonController@budgets', 'as' => 'budgets']); Route::get('tags', ['uses' => 'JsonController@tags', 'as' => 'tags']); Route::get('tour', ['uses' => 'JsonController@tour', 'as' => 'tour']); Route::get('box/in', ['uses' => 'JsonController@boxIn', 'as' => 'box.in']); From baefd4f93b6ece0a16cb1e1fe31586aee5d5b6e9 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 22 Jan 2017 11:23:40 +0100 Subject: [PATCH 586/709] Two new rule triggers --- app/Rules/Triggers/BudgetIs.php | 95 ++++++++++++++++++++++ app/Rules/Triggers/CategoryIs.php | 95 ++++++++++++++++++++++ config/firefly.php | 2 + tests/acceptance/NaughtyListTest.php | 115 --------------------------- 4 files changed, 192 insertions(+), 115 deletions(-) create mode 100644 app/Rules/Triggers/BudgetIs.php create mode 100644 app/Rules/Triggers/CategoryIs.php delete mode 100644 tests/acceptance/NaughtyListTest.php diff --git a/app/Rules/Triggers/BudgetIs.php b/app/Rules/Triggers/BudgetIs.php new file mode 100644 index 0000000000..fa444f3ffc --- /dev/null +++ b/app/Rules/Triggers/BudgetIs.php @@ -0,0 +1,95 @@ +<?php +/** + * BudgetIs.php + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +declare(strict_types = 1); + +namespace FireflyIII\Rules\Triggers; + +use FireflyIII\Models\Transaction; +use FireflyIII\Models\TransactionJournal; +use Log; + +/** + * Class BudgetIs + * + * @package FireflyIII\Rules\Triggers + */ +final class BudgetIs extends AbstractTrigger implements TriggerInterface +{ + + /** + * A trigger is said to "match anything", or match any given transaction, + * when the trigger value is very vague or has no restrictions. Easy examples + * are the "AmountMore"-trigger combined with an amount of 0: any given transaction + * has an amount of more than zero! Other examples are all the "Description"-triggers + * which have hard time handling empty trigger values such as "" or "*" (wild cards). + * + * If the user tries to create such a trigger, this method MUST return true so Firefly III + * can stop the storing / updating the trigger. If the trigger is in any way restrictive + * (even if it will still include 99.9% of the users transactions), this method MUST return + * false. + * + * @param null $value + * + * @return bool + */ + public static function willMatchEverything($value = null) + { + if (!is_null($value)) { + return false; + } + Log::error(sprintf('Cannot use %s with a null value.', self::class)); + + return true; + } + + /** + * @param TransactionJournal $journal + * + * @return bool + */ + public function triggered(TransactionJournal $journal): bool + { + $budget = $journal->budgets()->first(); + if (!is_null($budget)) { + $name = strtolower($budget->name); + // match on journal: + if ($name === strtolower($this->triggerValue)) { + Log::debug(sprintf('RuleTrigger BudgetIs for journal #%d: "%s" is "%s", return true.', $journal->id, $name, $this->triggerValue)); + + return true; + } + } + + if (is_null($budget)) { + // perhaps transactions have this budget? + /** @var Transaction $transaction */ + foreach ($journal->transactions as $transaction) { + $budget = $transaction->budgets()->first(); + if (!is_null($budget)) { + $name = strtolower($budget->name); + if ($name === strtolower($this->triggerValue)) { + Log::debug( + sprintf( + 'RuleTrigger BudgetIs for journal #%d (transaction #%d): "%s" is "%s", return true.', + $journal->id, $transaction->id, $name, $this->triggerValue + ) + ); + + return true; + } + } + } + } + + Log::debug(sprintf('RuleTrigger BudgetIs for journal #%d: does not have budget "%s", return false.', $journal->id, $this->triggerValue)); + + return false; + } +} diff --git a/app/Rules/Triggers/CategoryIs.php b/app/Rules/Triggers/CategoryIs.php new file mode 100644 index 0000000000..28dcddb086 --- /dev/null +++ b/app/Rules/Triggers/CategoryIs.php @@ -0,0 +1,95 @@ +<?php +/** + * CategoryIs.php + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +declare(strict_types = 1); + +namespace FireflyIII\Rules\Triggers; + +use FireflyIII\Models\Transaction; +use FireflyIII\Models\TransactionJournal; +use Log; + +/** + * Class CategoryIs + * + * @package FireflyIII\Rules\Triggers + */ +final class CategoryIs extends AbstractTrigger implements TriggerInterface +{ + + /** + * A trigger is said to "match anything", or match any given transaction, + * when the trigger value is very vague or has no restrictions. Easy examples + * are the "AmountMore"-trigger combined with an amount of 0: any given transaction + * has an amount of more than zero! Other examples are all the "Description"-triggers + * which have hard time handling empty trigger values such as "" or "*" (wild cards). + * + * If the user tries to create such a trigger, this method MUST return true so Firefly III + * can stop the storing / updating the trigger. If the trigger is in any way restrictive + * (even if it will still include 99.9% of the users transactions), this method MUST return + * false. + * + * @param null $value + * + * @return bool + */ + public static function willMatchEverything($value = null) + { + if (!is_null($value)) { + return false; + } + Log::error(sprintf('Cannot use %s with a null value.', self::class)); + + return true; + } + + /** + * @param TransactionJournal $journal + * + * @return bool + */ + public function triggered(TransactionJournal $journal): bool + { + $category = $journal->categories()->first(); + if (!is_null($category)) { + $name = strtolower($category->name); + // match on journal: + if ($name === strtolower($this->triggerValue)) { + Log::debug(sprintf('RuleTrigger CategoryIs for journal #%d: "%s" is "%s", return true.', $journal->id, $name, $this->triggerValue)); + + return true; + } + } + + if (is_null($category)) { + // perhaps transactions have this category? + /** @var Transaction $transaction */ + foreach ($journal->transactions as $transaction) { + $category = $transaction->categories()->first(); + if (!is_null($category)) { + $name = strtolower($category->name); + if ($name === strtolower($this->triggerValue)) { + Log::debug( + sprintf( + 'RuleTrigger CategoryIs for journal #%d (transaction #%d): "%s" is "%s", return true.', + $journal->id, $transaction->id, $name, $this->triggerValue + ) + ); + + return true; + } + } + } + } + + Log::debug(sprintf('RuleTrigger CategoryIs for journal #%d: does not have category "%s", return false.', $journal->id, $this->triggerValue)); + + return false; + } +} diff --git a/config/firefly.php b/config/firefly.php index 8c4f2a4085..a68e72bb74 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -174,6 +174,8 @@ return [ 'description_ends' => 'FireflyIII\Rules\Triggers\DescriptionEnds', 'description_contains' => 'FireflyIII\Rules\Triggers\DescriptionContains', 'description_is' => 'FireflyIII\Rules\Triggers\DescriptionIs', + 'category_is' => 'FireflyIII\Rules\Triggers\CategoryIs', + 'budget_is' => 'FireflyIII\Rules\Triggers\BudgetIs', ], 'rule-actions' => [ 'set_category' => 'FireflyIII\Rules\Actions\SetCategory', diff --git a/tests/acceptance/NaughtyListTest.php b/tests/acceptance/NaughtyListTest.php deleted file mode 100644 index 60a80ff11d..0000000000 --- a/tests/acceptance/NaughtyListTest.php +++ /dev/null @@ -1,115 +0,0 @@ -<?php -/** - * NaughtyListTest.php - * Copyright (c) 2017 thegrumpydictator@gmail.com - * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ -declare(strict_types = 1); - -use FireflyIII\Models\TransactionJournal; - -/** - * Class NaughtyListTest - */ -class NaughtyListTest extends TestCase -{ - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\MassController::update - * @dataProvider naughtyStringProvider - * - * @param string $description - */ - public function testMassUpdate(string $description) - { - $deposit = TransactionJournal::where('transaction_type_id', 2)->where('user_id', $this->user()->id) - ->whereNull('deleted_at') - ->first(); - $this->session(['transactions.mass-edit.url' => 'http://localhost']); - - $data = [ - 'journals' => [$deposit->id], - 'description' => [$deposit->id => $description], - 'amount' => [$deposit->id => 1600], - 'amount_currency_id_amount_' . $deposit->id => 1, - 'date' => [$deposit->id => '2014-07-24'], - 'source_account_name' => [$deposit->id => 'Job'], - 'destination_account_id' => [$deposit->id => 1], - 'category' => [$deposit->id => 'Salary'], - ]; - - $this->be($this->user()); - $response = $this->call('post', route('transactions.mass.update', [$deposit->id]), $data); - $this->assertNotEquals($response->getStatusCode(), 500); - - // visit them should show updated content - $this->call('get', route('transactions.show', [$deposit->id])); - $this->assertResponseOk(); - $this->see($description); - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\SingleController::store - * @dataProvider naughtyStringProvider - * - * @param string $description - */ - public function testSingleStore(string $description) - { - $this->session(['transactions.create.url' => 'http://localhost']); - $this->be($this->user()); - - $data = [ - 'what' => 'withdrawal', - 'amount' => '10', - 'amount_currency_id_amount' => 1, - 'source_account_id' => 1, - 'destination_account_name' => 'Some destination', - 'date' => '2016-01-01', - 'description' => $description, - ]; - $response = $this->call('post', route('transactions.store', ['withdrawal']), $data); - $this->assertNotEquals($response->getStatusCode(), 500); - - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\SingleController::update - * @dataProvider naughtyStringProvider - * - * @param string $description - */ - public function testSingleUpdate(string $description) - { - $this->session(['transactions.edit.url' => 'http://localhost']); - $this->be($this->user()); - $data = [ - 'id' => 123, - 'what' => 'withdrawal', - 'description' => $description, - 'source_account_id' => 1, - 'destination_account_name' => 'PLUS', - 'amount' => '123', - 'amount_currency_id_amount' => 1, - 'budget_id' => 1, - 'category' => 'Daily groceries', - 'tags' => '', - 'date' => '2016-01-01', - ]; - - $response = $this->call('post', route('transactions.update', [123]), $data); - $this->assertNotEquals($response->getStatusCode(), 500); - } - -} \ No newline at end of file From 1c2c6bb1d00487bfac8c0e7cfbfc2e46f66eacfb Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 22 Jan 2017 11:23:56 +0100 Subject: [PATCH 587/709] Various small changes. --- bootstrap/app.php | 1 - resources/lang/en_US/firefly.php | 2 ++ resources/views/partials/control-bar.twig | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/bootstrap/app.php b/bootstrap/app.php index 88ef952626..b026728e73 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -25,7 +25,6 @@ declare(strict_types = 1); bcscale(12); - $app = new Illuminate\Foundation\Application( realpath(__DIR__.'/../') ); diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 8e9ed4d568..84cd608220 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -256,6 +256,8 @@ return [ 'rule_trigger_description_ends_choice' => 'Description ends with..', 'rule_trigger_description_contains_choice' => 'Description contains..', 'rule_trigger_description_is_choice' => 'Description is..', + 'rule_trigger_category_is_choice' => 'Category is..', + 'rule_trigger_budget_is_choice' => 'Budget is..', 'rule_trigger_store_journal' => 'When a transaction is created', 'rule_trigger_update_journal' => 'When a transaction is updated', 'rule_action_set_category' => 'Set category to ":action_value"', diff --git a/resources/views/partials/control-bar.twig b/resources/views/partials/control-bar.twig index de0eaeed02..813549c0aa 100644 --- a/resources/views/partials/control-bar.twig +++ b/resources/views/partials/control-bar.twig @@ -63,7 +63,7 @@ <li> <a href="{{ route('budgets.create') }}"> - <i class="menu-icon fa fa-download bg-red"></i> + <i class="menu-icon fa fa-tasks bg-red"></i> <div class="menu-info"> <h4 class="control-sidebar-subheading">{{ 'new_budget'|_ }}</h4> @@ -73,7 +73,7 @@ <li> <a href="{{ route('categories.create') }}"> - <i class="menu-icon fa fa-download bg-red"></i> + <i class="menu-icon fa fa-bar-chart bg-red"></i> <div class="menu-info"> <h4 class="control-sidebar-subheading">{{ 'new_category'|_ }}</h4> @@ -92,7 +92,7 @@ </li> <li> <a href="{{ route('bills.create') }}"> - <i class="menu-icon fa fa-download bg-purple"></i> + <i class="menu-icon fa fa-calendar-o bg-purple"></i> <div class="menu-info"> <h4 class="control-sidebar-subheading">{{ 'new_bill'|_ }}</h4> From e4b1812b463e61ef664d13098fa0d3493a844062 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 23 Jan 2017 18:03:17 +0100 Subject: [PATCH 588/709] Fix small layout thing [skip ci] --- resources/views/list/piggy-banks.twig | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/resources/views/list/piggy-banks.twig b/resources/views/list/piggy-banks.twig index 7fa3188f14..c754498b8f 100644 --- a/resources/views/list/piggy-banks.twig +++ b/resources/views/list/piggy-banks.twig @@ -4,14 +4,17 @@ <th colspan="2"> </th> <th>{{ 'piggy_bank'|_ }}</th> <th style="text-align: right;">{{ 'saved_so_far'|_ }}</th> - <th colspan="3"> </th> - <th style="text-align: right;">{{ 'target_amount'|_ }}</th> - <th style="text-align: right;">{{ 'left_to_save'|_ }}</th> + <th colspan="3" class="hidden-sm hidden-xs"> </th> + <th style="text-align: right;" class="hidden-sm hidden-xs">{{ 'target_amount'|_ }}</th> + <th style="text-align: right;" class="hidden-sm hidden-xs">{{ 'left_to_save'|_ }}</th> </tr> </thead> <tbody> {% for piggyBank in piggyBanks %} <tr data-id="{{ piggyBank.id }}"> + <td class="visible-xs visible-sm hidden-md hidden-lg"> +   + </td> <td class="visible-xs visible-sm hidden-md hidden-lg"> <div class="btn-group btn-group-xs"> <a href="{{ route('piggy-banks.remove-money-mobile', piggyBank.id) }}" class="btn btn-default btn-xs"><i class="fa fa-minus"></i></a> From 6c14e9d0835bbf470b4777dbdcf59815d150ad0a Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Tue, 24 Jan 2017 07:26:06 +0100 Subject: [PATCH 589/709] No sleep for the wicked [skip ci] --- app/Http/Controllers/JsonController.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/Http/Controllers/JsonController.php b/app/Http/Controllers/JsonController.php index 94ed5988e5..4d95c51fc9 100644 --- a/app/Http/Controllers/JsonController.php +++ b/app/Http/Controllers/JsonController.php @@ -51,7 +51,6 @@ class JsonController extends Controller */ public function action(Request $request) { - sleep(5); $count = intval($request->get('count')) > 0 ? intval($request->get('count')) : 1; $keys = array_keys(config('firefly.rule-actions')); $actions = []; @@ -347,7 +346,6 @@ class JsonController extends Controller */ public function trigger(Request $request) { - sleep(5); $count = intval($request->get('count')) > 0 ? intval($request->get('count')) : 1; $keys = array_keys(config('firefly.rule-triggers')); $triggers = []; From 872e8f2de660abe473b30072f5e3429d9e9fa851 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Tue, 24 Jan 2017 07:37:29 +0100 Subject: [PATCH 590/709] Hip new multi select. --- public/css/bootstrap-multiselect.css | 1 + public/js/ff/reports/index.js | 6 +- public/js/lib/bootstrap-multiselect.js | 1716 ++++++++++++++++++++++++ resources/views/reports/index.twig | 57 +- 4 files changed, 1760 insertions(+), 20 deletions(-) create mode 100755 public/css/bootstrap-multiselect.css create mode 100755 public/js/lib/bootstrap-multiselect.js diff --git a/public/css/bootstrap-multiselect.css b/public/css/bootstrap-multiselect.css new file mode 100755 index 0000000000..5acaf9f7ab --- /dev/null +++ b/public/css/bootstrap-multiselect.css @@ -0,0 +1 @@ +span.multiselect-native-select{position:relative}span.multiselect-native-select select{border:0!important;clip:rect(0 0 0 0)!important;height:1px!important;margin:-1px -1px -1px -3px!important;overflow:hidden!important;padding:0!important;position:absolute!important;width:1px!important;left:50%;top:30px}.multiselect-container{position:absolute;list-style-type:none;margin:0;padding:0}.multiselect-container .input-group{margin:5px}.multiselect-container>li{padding:0}.multiselect-container>li>a.multiselect-all label{font-weight:700}.multiselect-container>li.multiselect-group label{margin:0;padding:3px 20px 3px 20px;height:100%;font-weight:700}.multiselect-container>li.multiselect-group-clickable label{cursor:pointer}.multiselect-container>li>a{padding:0}.multiselect-container>li>a>label{margin:0;height:100%;cursor:pointer;font-weight:400;padding:3px 20px 3px 40px}.multiselect-container>li>a>label.radio,.multiselect-container>li>a>label.checkbox{margin:0}.multiselect-container>li>a>label>input[type=checkbox]{margin-bottom:5px}.btn-group>.btn-group:nth-child(2)>.multiselect.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.form-inline .multiselect-container label.checkbox,.form-inline .multiselect-container label.radio{padding:3px 20px 3px 40px}.form-inline .multiselect-container li a label.checkbox input[type=checkbox],.form-inline .multiselect-container li a label.radio input[type=radio]{margin-left:-20px;margin-right:0} diff --git a/public/js/ff/reports/index.js b/public/js/ff/reports/index.js index 77c15f0578..295cb659fe 100644 --- a/public/js/ff/reports/index.js +++ b/public/js/ff/reports/index.js @@ -26,6 +26,7 @@ $(function () { } ); + // set values from cookies, if any: if (!(readCookie('report-type') === null)) { $('select[name="report_type"]').val(readCookie('report-type')); @@ -34,10 +35,13 @@ $(function () { if ((readCookie('report-accounts') !== null)) { var arr = readCookie('report-accounts').split(','); arr.forEach(function (val) { - $('input[class="account-checkbox"][type="checkbox"][value="' + val + '"]').prop('checked', true); + $('#inputAccounts').find('option[value="' + val + '"]').prop('selected', true); }); } + // make account select a hip new bootstrap multi-select thing. + $('#inputAccounts').multiselect(); + // set date: var startStr = readCookie('report-start'); var endStr = readCookie('report-end'); diff --git a/public/js/lib/bootstrap-multiselect.js b/public/js/lib/bootstrap-multiselect.js new file mode 100755 index 0000000000..9a50a18a0d --- /dev/null +++ b/public/js/lib/bootstrap-multiselect.js @@ -0,0 +1,1716 @@ +/** + * Bootstrap Multiselect (https://github.com/davidstutz/bootstrap-multiselect) + * + * Apache License, Version 2.0: + * Copyright (c) 2012 - 2015 David Stutz + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a + * copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * BSD 3-Clause License: + * Copyright (c) 2012 - 2015 David Stutz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of David Stutz nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +!function ($) { + "use strict";// jshint ;_; + + if (typeof ko !== 'undefined' && ko.bindingHandlers && !ko.bindingHandlers.multiselect) { + ko.bindingHandlers.multiselect = { + after: ['options', 'value', 'selectedOptions', 'enable', 'disable'], + + init: function(element, valueAccessor, allBindings, viewModel, bindingContext) { + var $element = $(element); + var config = ko.toJS(valueAccessor()); + + $element.multiselect(config); + + if (allBindings.has('options')) { + var options = allBindings.get('options'); + if (ko.isObservable(options)) { + ko.computed({ + read: function() { + options(); + setTimeout(function() { + var ms = $element.data('multiselect'); + if (ms) + ms.updateOriginalOptions();//Not sure how beneficial this is. + $element.multiselect('rebuild'); + }, 1); + }, + disposeWhenNodeIsRemoved: element + }); + } + } + + //value and selectedOptions are two-way, so these will be triggered even by our own actions. + //It needs some way to tell if they are triggered because of us or because of outside change. + //It doesn't loop but it's a waste of processing. + if (allBindings.has('value')) { + var value = allBindings.get('value'); + if (ko.isObservable(value)) { + ko.computed({ + read: function() { + value(); + setTimeout(function() { + $element.multiselect('refresh'); + }, 1); + }, + disposeWhenNodeIsRemoved: element + }).extend({ rateLimit: 100, notifyWhenChangesStop: true }); + } + } + + //Switched from arrayChange subscription to general subscription using 'refresh'. + //Not sure performance is any better using 'select' and 'deselect'. + if (allBindings.has('selectedOptions')) { + var selectedOptions = allBindings.get('selectedOptions'); + if (ko.isObservable(selectedOptions)) { + ko.computed({ + read: function() { + selectedOptions(); + setTimeout(function() { + $element.multiselect('refresh'); + }, 1); + }, + disposeWhenNodeIsRemoved: element + }).extend({ rateLimit: 100, notifyWhenChangesStop: true }); + } + } + + var setEnabled = function (enable) { + setTimeout(function () { + if (enable) + $element.multiselect('enable'); + else + $element.multiselect('disable'); + }); + }; + + if (allBindings.has('enable')) { + var enable = allBindings.get('enable'); + if (ko.isObservable(enable)) { + ko.computed({ + read: function () { + setEnabled(enable()); + }, + disposeWhenNodeIsRemoved: element + }).extend({ rateLimit: 100, notifyWhenChangesStop: true }); + } else { + setEnabled(enable); + } + } + + if (allBindings.has('disable')) { + var disable = allBindings.get('disable'); + if (ko.isObservable(disable)) { + ko.computed({ + read: function () { + setEnabled(!disable()); + }, + disposeWhenNodeIsRemoved: element + }).extend({ rateLimit: 100, notifyWhenChangesStop: true }); + } else { + setEnabled(!disable); + } + } + + ko.utils.domNodeDisposal.addDisposeCallback(element, function() { + $element.multiselect('destroy'); + }); + }, + + update: function(element, valueAccessor, allBindings, viewModel, bindingContext) { + var $element = $(element); + var config = ko.toJS(valueAccessor()); + + $element.multiselect('setOptions', config); + $element.multiselect('rebuild'); + } + }; + } + + function forEach(array, callback) { + for (var index = 0; index < array.length; ++index) { + callback(array[index], index); + } + } + + /** + * Constructor to create a new multiselect using the given select. + * + * @param {jQuery} select + * @param {Object} options + * @returns {Multiselect} + */ + function Multiselect(select, options) { + + this.$select = $(select); + this.options = this.mergeOptions($.extend({}, options, this.$select.data())); + + // Placeholder via data attributes + if (this.$select.attr("data-placeholder")) { + this.options.nonSelectedText = this.$select.data("placeholder"); + } + + // Initialization. + // We have to clone to create a new reference. + this.originalOptions = this.$select.clone()[0].options; + this.query = ''; + this.searchTimeout = null; + this.lastToggledInput = null; + + this.options.multiple = this.$select.attr('multiple') === "multiple"; + this.options.onChange = $.proxy(this.options.onChange, this); + this.options.onSelectAll = $.proxy(this.options.onSelectAll, this); + this.options.onDeselectAll = $.proxy(this.options.onDeselectAll, this); + this.options.onDropdownShow = $.proxy(this.options.onDropdownShow, this); + this.options.onDropdownHide = $.proxy(this.options.onDropdownHide, this); + this.options.onDropdownShown = $.proxy(this.options.onDropdownShown, this); + this.options.onDropdownHidden = $.proxy(this.options.onDropdownHidden, this); + this.options.onInitialized = $.proxy(this.options.onInitialized, this); + this.options.onFiltering = $.proxy(this.options.onFiltering, this); + + // Build select all if enabled. + this.buildContainer(); + this.buildButton(); + this.buildDropdown(); + this.buildSelectAll(); + this.buildDropdownOptions(); + this.buildFilter(); + + this.updateButtonText(); + this.updateSelectAll(true); + + if (this.options.enableClickableOptGroups && this.options.multiple) { + this.updateOptGroups(); + } + + this.options.wasDisabled = this.$select.prop('disabled'); + if (this.options.disableIfEmpty && $('option', this.$select).length <= 0) { + this.disable(); + } + + this.$select.wrap('<span class="multiselect-native-select" />').after(this.$container); + this.options.onInitialized(this.$select, this.$container); + } + + Multiselect.prototype = { + + defaults: { + /** + * Default text function will either print 'None selected' in case no + * option is selected or a list of the selected options up to a length + * of 3 selected options. + * + * @param {jQuery} options + * @param {jQuery} select + * @returns {String} + */ + buttonText: function(options, select) { + if (this.disabledText.length > 0 + && (select.prop('disabled') || (options.length == 0 && this.disableIfEmpty))) { + + return this.disabledText; + } + else if (options.length === 0) { + return this.nonSelectedText; + } + else if (this.allSelectedText + && options.length === $('option', $(select)).length + && $('option', $(select)).length !== 1 + && this.multiple) { + + if (this.selectAllNumber) { + return this.allSelectedText + ' (' + options.length + ')'; + } + else { + return this.allSelectedText; + } + } + else if (options.length > this.numberDisplayed) { + return options.length + ' ' + this.nSelectedText; + } + else { + var selected = ''; + var delimiter = this.delimiterText; + + options.each(function() { + var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).text(); + selected += label + delimiter; + }); + + return selected.substr(0, selected.length - this.delimiterText.length); + } + }, + /** + * Updates the title of the button similar to the buttonText function. + * + * @param {jQuery} options + * @param {jQuery} select + * @returns {@exp;selected@call;substr} + */ + buttonTitle: function(options, select) { + if (options.length === 0) { + return this.nonSelectedText; + } + else { + var selected = ''; + var delimiter = this.delimiterText; + + options.each(function () { + var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).text(); + selected += label + delimiter; + }); + return selected.substr(0, selected.length - this.delimiterText.length); + } + }, + checkboxName: function(option) { + return false; // no checkbox name + }, + /** + * Create a label. + * + * @param {jQuery} element + * @returns {String} + */ + optionLabel: function(element){ + return $(element).attr('label') || $(element).text(); + }, + /** + * Create a class. + * + * @param {jQuery} element + * @returns {String} + */ + optionClass: function(element) { + return $(element).attr('class') || ''; + }, + /** + * Triggered on change of the multiselect. + * + * Not triggered when selecting/deselecting options manually. + * + * @param {jQuery} option + * @param {Boolean} checked + */ + onChange : function(option, checked) { + + }, + /** + * Triggered when the dropdown is shown. + * + * @param {jQuery} event + */ + onDropdownShow: function(event) { + + }, + /** + * Triggered when the dropdown is hidden. + * + * @param {jQuery} event + */ + onDropdownHide: function(event) { + + }, + /** + * Triggered after the dropdown is shown. + * + * @param {jQuery} event + */ + onDropdownShown: function(event) { + + }, + /** + * Triggered after the dropdown is hidden. + * + * @param {jQuery} event + */ + onDropdownHidden: function(event) { + + }, + /** + * Triggered on select all. + */ + onSelectAll: function() { + + }, + /** + * Triggered on deselect all. + */ + onDeselectAll: function() { + + }, + /** + * Triggered after initializing. + * + * @param {jQuery} $select + * @param {jQuery} $container + */ + onInitialized: function($select, $container) { + + }, + /** + * Triggered on filtering. + * + * @param {jQuery} $filter + */ + onFiltering: function($filter) { + + }, + enableHTML: false, + buttonClass: 'btn btn-default', + inheritClass: false, + buttonWidth: 'auto', + buttonContainer: '<div class="btn-group" />', + dropRight: false, + dropUp: false, + selectedClass: 'active', + // Maximum height of the dropdown menu. + // If maximum height is exceeded a scrollbar will be displayed. + maxHeight: false, + includeSelectAllOption: false, + includeSelectAllIfMoreThan: 0, + selectAllText: ' Select all', + selectAllValue: 'multiselect-all', + selectAllName: false, + selectAllNumber: true, + selectAllJustVisible: true, + enableFiltering: false, + enableCaseInsensitiveFiltering: false, + enableFullValueFiltering: false, + enableClickableOptGroups: false, + enableCollapsibleOptGroups: false, + filterPlaceholder: 'Search', + // possible options: 'text', 'value', 'both' + filterBehavior: 'text', + includeFilterClearBtn: true, + preventInputChangeEvent: false, + nonSelectedText: 'None selected', + nSelectedText: 'selected', + allSelectedText: 'All selected', + numberDisplayed: 3, + disableIfEmpty: false, + disabledText: '', + delimiterText: ', ', + templates: { + button: '<button type="button" class="multiselect dropdown-toggle" data-toggle="dropdown"><span class="multiselect-selected-text"></span> <b class="caret"></b></button>', + ul: '<ul class="multiselect-container dropdown-menu"></ul>', + filter: '<li class="multiselect-item multiselect-filter"><div class="input-group"><span class="input-group-addon"><i class="glyphicon glyphicon-search"></i></span><input class="form-control multiselect-search" type="text"></div></li>', + filterClearBtn: '<span class="input-group-btn"><button class="btn btn-default multiselect-clear-filter" type="button"><i class="glyphicon glyphicon-remove-circle"></i></button></span>', + li: '<li><a tabindex="0"><label></label></a></li>', + divider: '<li class="multiselect-item divider"></li>', + liGroup: '<li class="multiselect-item multiselect-group"><label></label></li>' + } + }, + + constructor: Multiselect, + + /** + * Builds the container of the multiselect. + */ + buildContainer: function() { + this.$container = $(this.options.buttonContainer); + this.$container.on('show.bs.dropdown', this.options.onDropdownShow); + this.$container.on('hide.bs.dropdown', this.options.onDropdownHide); + this.$container.on('shown.bs.dropdown', this.options.onDropdownShown); + this.$container.on('hidden.bs.dropdown', this.options.onDropdownHidden); + }, + + /** + * Builds the button of the multiselect. + */ + buildButton: function() { + this.$button = $(this.options.templates.button).addClass(this.options.buttonClass); + if (this.$select.attr('class') && this.options.inheritClass) { + this.$button.addClass(this.$select.attr('class')); + } + // Adopt active state. + if (this.$select.prop('disabled')) { + this.disable(); + } + else { + this.enable(); + } + + // Manually add button width if set. + if (this.options.buttonWidth && this.options.buttonWidth !== 'auto') { + this.$button.css({ + 'width' : '100%', //this.options.buttonWidth, + 'overflow' : 'hidden', + 'text-overflow' : 'ellipsis' + }); + this.$container.css({ + 'width': this.options.buttonWidth + }); + } + + // Keep the tab index from the select. + var tabindex = this.$select.attr('tabindex'); + if (tabindex) { + this.$button.attr('tabindex', tabindex); + } + + this.$container.prepend(this.$button); + }, + + /** + * Builds the ul representing the dropdown menu. + */ + buildDropdown: function() { + + // Build ul. + this.$ul = $(this.options.templates.ul); + + if (this.options.dropRight) { + this.$ul.addClass('pull-right'); + } + + // Set max height of dropdown menu to activate auto scrollbar. + if (this.options.maxHeight) { + // TODO: Add a class for this option to move the css declarations. + this.$ul.css({ + 'max-height': this.options.maxHeight + 'px', + 'overflow-y': 'auto', + 'overflow-x': 'hidden' + }); + } + + if (this.options.dropUp) { + + var height = Math.min(this.options.maxHeight, $('option[data-role!="divider"]', this.$select).length*26 + $('option[data-role="divider"]', this.$select).length*19 + (this.options.includeSelectAllOption ? 26 : 0) + (this.options.enableFiltering || this.options.enableCaseInsensitiveFiltering ? 44 : 0)); + var moveCalc = height + 34; + + this.$ul.css({ + 'max-height': height + 'px', + 'overflow-y': 'auto', + 'overflow-x': 'hidden', + 'margin-top': "-" + moveCalc + 'px' + }); + } + + this.$container.append(this.$ul); + }, + + /** + * Build the dropdown options and binds all necessary events. + * + * Uses createDivider and createOptionValue to create the necessary options. + */ + buildDropdownOptions: function() { + + this.$select.children().each($.proxy(function(index, element) { + + var $element = $(element); + // Support optgroups and options without a group simultaneously. + var tag = $element.prop('tagName') + .toLowerCase(); + + if ($element.prop('value') === this.options.selectAllValue) { + return; + } + + if (tag === 'optgroup') { + this.createOptgroup(element); + } + else if (tag === 'option') { + + if ($element.data('role') === 'divider') { + this.createDivider(); + } + else { + this.createOptionValue(element); + } + + } + + // Other illegal tags will be ignored. + }, this)); + + // Bind the change event on the dropdown elements. + $('li:not(.multiselect-group) input', this.$ul).on('change', $.proxy(function(event) { + var $target = $(event.target); + + var checked = $target.prop('checked') || false; + var isSelectAllOption = $target.val() === this.options.selectAllValue; + + // Apply or unapply the configured selected class. + if (this.options.selectedClass) { + if (checked) { + $target.closest('li') + .addClass(this.options.selectedClass); + } + else { + $target.closest('li') + .removeClass(this.options.selectedClass); + } + } + + // Get the corresponding option. + var value = $target.val(); + var $option = this.getOptionByValue(value); + + var $optionsNotThis = $('option', this.$select).not($option); + var $checkboxesNotThis = $('input', this.$container).not($target); + + if (isSelectAllOption) { + + if (checked) { + this.selectAll(this.options.selectAllJustVisible, true); + } + else { + this.deselectAll(this.options.selectAllJustVisible, true); + } + } + else { + if (checked) { + $option.prop('selected', true); + + if (this.options.multiple) { + // Simply select additional option. + $option.prop('selected', true); + } + else { + // Unselect all other options and corresponding checkboxes. + if (this.options.selectedClass) { + $($checkboxesNotThis).closest('li').removeClass(this.options.selectedClass); + } + + $($checkboxesNotThis).prop('checked', false); + $optionsNotThis.prop('selected', false); + + // It's a single selection, so close. + this.$button.click(); + } + + if (this.options.selectedClass === "active") { + $optionsNotThis.closest("a").css("outline", ""); + } + } + else { + // Unselect option. + $option.prop('selected', false); + } + + // To prevent select all from firing onChange: #575 + this.options.onChange($option, checked); + + // Do not update select all or optgroups on select all change! + this.updateSelectAll(); + + if (this.options.enableClickableOptGroups && this.options.multiple) { + this.updateOptGroups(); + } + } + + this.$select.change(); + this.updateButtonText(); + + if(this.options.preventInputChangeEvent) { + return false; + } + }, this)); + + $('li a', this.$ul).on('mousedown', function(e) { + if (e.shiftKey) { + // Prevent selecting text by Shift+click + return false; + } + }); + + $('li a', this.$ul).on('touchstart click', $.proxy(function(event) { + event.stopPropagation(); + + var $target = $(event.target); + + if (event.shiftKey && this.options.multiple) { + if($target.is("label")){ // Handles checkbox selection manually (see https://github.com/davidstutz/bootstrap-multiselect/issues/431) + event.preventDefault(); + $target = $target.find("input"); + $target.prop("checked", !$target.prop("checked")); + } + var checked = $target.prop('checked') || false; + + if (this.lastToggledInput !== null && this.lastToggledInput !== $target) { // Make sure we actually have a range + var from = $target.closest("li").index(); + var to = this.lastToggledInput.closest("li").index(); + + if (from > to) { // Swap the indices + var tmp = to; + to = from; + from = tmp; + } + + // Make sure we grab all elements since slice excludes the last index + ++to; + + // Change the checkboxes and underlying options + var range = this.$ul.find("li").slice(from, to).find("input"); + + range.prop('checked', checked); + + if (this.options.selectedClass) { + range.closest('li') + .toggleClass(this.options.selectedClass, checked); + } + + for (var i = 0, j = range.length; i < j; i++) { + var $checkbox = $(range[i]); + + var $option = this.getOptionByValue($checkbox.val()); + + $option.prop('selected', checked); + } + } + + // Trigger the select "change" event + $target.trigger("change"); + } + + // Remembers last clicked option + if($target.is("input") && !$target.closest("li").is(".multiselect-item")){ + this.lastToggledInput = $target; + } + + $target.blur(); + }, this)); + + // Keyboard support. + this.$container.off('keydown.multiselect').on('keydown.multiselect', $.proxy(function(event) { + if ($('input[type="text"]', this.$container).is(':focus')) { + return; + } + + if (event.keyCode === 9 && this.$container.hasClass('open')) { + this.$button.click(); + } + else { + var $items = $(this.$container).find("li:not(.divider):not(.disabled) a").filter(":visible"); + + if (!$items.length) { + return; + } + + var index = $items.index($items.filter(':focus')); + + // Navigation up. + if (event.keyCode === 38 && index > 0) { + index--; + } + // Navigate down. + else if (event.keyCode === 40 && index < $items.length - 1) { + index++; + } + else if (!~index) { + index = 0; + } + + var $current = $items.eq(index); + $current.focus(); + + if (event.keyCode === 32 || event.keyCode === 13) { + var $checkbox = $current.find('input'); + + $checkbox.prop("checked", !$checkbox.prop("checked")); + $checkbox.change(); + } + + event.stopPropagation(); + event.preventDefault(); + } + }, this)); + + if (this.options.enableClickableOptGroups && this.options.multiple) { + $("li.multiselect-group input", this.$ul).on("change", $.proxy(function(event) { + event.stopPropagation(); + + var $target = $(event.target); + var checked = $target.prop('checked') || false; + + var $li = $(event.target).closest('li'); + var $group = $li.nextUntil("li.multiselect-group") + .not('.multiselect-filter-hidden') + .not('.disabled'); + + var $inputs = $group.find("input"); + + var values = []; + var $options = []; + + if (this.options.selectedClass) { + if (checked) { + $li.addClass(this.options.selectedClass); + } + else { + $li.removeClass(this.options.selectedClass); + } + } + + $.each($inputs, $.proxy(function(index, input) { + var value = $(input).val(); + var $option = this.getOptionByValue(value); + + if (checked) { + $(input).prop('checked', true); + $(input).closest('li') + .addClass(this.options.selectedClass); + + $option.prop('selected', true); + } + else { + $(input).prop('checked', false); + $(input).closest('li') + .removeClass(this.options.selectedClass); + + $option.prop('selected', false); + } + + $options.push(this.getOptionByValue(value)); + }, this)) + + // Cannot use select or deselect here because it would call updateOptGroups again. + + this.options.onChange($options, checked); + + this.updateButtonText(); + this.updateSelectAll(); + }, this)); + } + + if (this.options.enableCollapsibleOptGroups && this.options.multiple) { + $("li.multiselect-group .caret-container", this.$ul).on("click", $.proxy(function(event) { + var $li = $(event.target).closest('li'); + var $inputs = $li.nextUntil("li.multiselect-group") + .not('.multiselect-filter-hidden'); + + var visible = true; + $inputs.each(function() { + visible = visible && $(this).is(':visible'); + }); + + if (visible) { + $inputs.hide() + .addClass('multiselect-collapsible-hidden'); + } + else { + $inputs.show() + .removeClass('multiselect-collapsible-hidden'); + } + }, this)); + + $("li.multiselect-all", this.$ul).css('background', '#f3f3f3').css('border-bottom', '1px solid #eaeaea'); + $("li.multiselect-all > a > label.checkbox", this.$ul).css('padding', '3px 20px 3px 35px'); + $("li.multiselect-group > a > input", this.$ul).css('margin', '4px 0px 5px -20px'); + } + }, + + /** + * Create an option using the given select option. + * + * @param {jQuery} element + */ + createOptionValue: function(element) { + var $element = $(element); + if ($element.is(':selected')) { + $element.prop('selected', true); + } + + // Support the label attribute on options. + var label = this.options.optionLabel(element); + var classes = this.options.optionClass(element); + var value = $element.val(); + var inputType = this.options.multiple ? "checkbox" : "radio"; + + var $li = $(this.options.templates.li); + var $label = $('label', $li); + $label.addClass(inputType); + $li.addClass(classes); + + if (this.options.enableHTML) { + $label.html(" " + label); + } + else { + $label.text(" " + label); + } + + var $checkbox = $('<input/>').attr('type', inputType); + + var name = this.options.checkboxName($element); + if (name) { + $checkbox.attr('name', name); + } + + $label.prepend($checkbox); + + var selected = $element.prop('selected') || false; + $checkbox.val(value); + + if (value === this.options.selectAllValue) { + $li.addClass("multiselect-item multiselect-all"); + $checkbox.parent().parent() + .addClass('multiselect-all'); + } + + $label.attr('title', $element.attr('title')); + + this.$ul.append($li); + + if ($element.is(':disabled')) { + $checkbox.attr('disabled', 'disabled') + .prop('disabled', true) + .closest('a') + .attr("tabindex", "-1") + .closest('li') + .addClass('disabled'); + } + + $checkbox.prop('checked', selected); + + if (selected && this.options.selectedClass) { + $checkbox.closest('li') + .addClass(this.options.selectedClass); + } + }, + + /** + * Creates a divider using the given select option. + * + * @param {jQuery} element + */ + createDivider: function(element) { + var $divider = $(this.options.templates.divider); + this.$ul.append($divider); + }, + + /** + * Creates an optgroup. + * + * @param {jQuery} group + */ + createOptgroup: function(group) { + var label = $(group).attr("label"); + var value = $(group).attr("value"); + var $li = $('<li class="multiselect-item multiselect-group"><a href="javascript:void(0);"><label><b></b></label></a></li>'); + + var classes = this.options.optionClass(group); + $li.addClass(classes); + + if (this.options.enableHTML) { + $('label b', $li).html(" " + label); + } + else { + $('label b', $li).text(" " + label); + } + + if (this.options.enableCollapsibleOptGroups && this.options.multiple) { + $('a', $li).append('<span class="caret-container"><b class="caret"></b></span>'); + } + + if (this.options.enableClickableOptGroups && this.options.multiple) { + $('a label', $li).prepend('<input type="checkbox" value="' + value + '"/>'); + } + + if ($(group).is(':disabled')) { + $li.addClass('disabled'); + } + + this.$ul.append($li); + + $("option", group).each($.proxy(function($, group) { + this.createOptionValue(group); + }, this)) + }, + + /** + * Build the select all. + * + * Checks if a select all has already been created. + */ + buildSelectAll: function() { + if (typeof this.options.selectAllValue === 'number') { + this.options.selectAllValue = this.options.selectAllValue.toString(); + } + + var alreadyHasSelectAll = this.hasSelectAll(); + + if (!alreadyHasSelectAll && this.options.includeSelectAllOption && this.options.multiple + && $('option', this.$select).length > this.options.includeSelectAllIfMoreThan) { + + // Check whether to add a divider after the select all. + if (this.options.includeSelectAllDivider) { + this.$ul.prepend($(this.options.templates.divider)); + } + + var $li = $(this.options.templates.li); + $('label', $li).addClass("checkbox"); + + if (this.options.enableHTML) { + $('label', $li).html(" " + this.options.selectAllText); + } + else { + $('label', $li).text(" " + this.options.selectAllText); + } + + if (this.options.selectAllName) { + $('label', $li).prepend('<input type="checkbox" name="' + this.options.selectAllName + '" />'); + } + else { + $('label', $li).prepend('<input type="checkbox" />'); + } + + var $checkbox = $('input', $li); + $checkbox.val(this.options.selectAllValue); + + $li.addClass("multiselect-item multiselect-all"); + $checkbox.parent().parent() + .addClass('multiselect-all'); + + this.$ul.prepend($li); + + $checkbox.prop('checked', false); + } + }, + + /** + * Builds the filter. + */ + buildFilter: function() { + + // Build filter if filtering OR case insensitive filtering is enabled and the number of options exceeds (or equals) enableFilterLength. + if (this.options.enableFiltering || this.options.enableCaseInsensitiveFiltering) { + var enableFilterLength = Math.max(this.options.enableFiltering, this.options.enableCaseInsensitiveFiltering); + + if (this.$select.find('option').length >= enableFilterLength) { + + this.$filter = $(this.options.templates.filter); + $('input', this.$filter).attr('placeholder', this.options.filterPlaceholder); + + // Adds optional filter clear button + if(this.options.includeFilterClearBtn) { + var clearBtn = $(this.options.templates.filterClearBtn); + clearBtn.on('click', $.proxy(function(event){ + clearTimeout(this.searchTimeout); + + this.$filter.find('.multiselect-search').val(''); + $('li', this.$ul).show().removeClass('multiselect-filter-hidden'); + + this.updateSelectAll(); + + if (this.options.enableClickableOptGroups && this.options.multiple) { + this.updateOptGroups(); + } + + }, this)); + this.$filter.find('.input-group').append(clearBtn); + } + + this.$ul.prepend(this.$filter); + + this.$filter.val(this.query).on('click', function(event) { + event.stopPropagation(); + }).on('input keydown', $.proxy(function(event) { + // Cancel enter key default behaviour + if (event.which === 13) { + event.preventDefault(); + } + + // This is useful to catch "keydown" events after the browser has updated the control. + clearTimeout(this.searchTimeout); + + this.searchTimeout = this.asyncFunction($.proxy(function() { + + if (this.query !== event.target.value) { + this.query = event.target.value; + + var currentGroup, currentGroupVisible; + $.each($('li', this.$ul), $.proxy(function(index, element) { + var value = $('input', element).length > 0 ? $('input', element).val() : ""; + var text = $('label', element).text(); + + var filterCandidate = ''; + if ((this.options.filterBehavior === 'text')) { + filterCandidate = text; + } + else if ((this.options.filterBehavior === 'value')) { + filterCandidate = value; + } + else if (this.options.filterBehavior === 'both') { + filterCandidate = text + '\n' + value; + } + + if (value !== this.options.selectAllValue && text) { + + // By default lets assume that element is not + // interesting for this search. + var showElement = false; + + if (this.options.enableCaseInsensitiveFiltering) { + filterCandidate = filterCandidate.toLowerCase(); + this.query = this.query.toLowerCase(); + } + + if (this.options.enableFullValueFiltering && this.options.filterBehavior !== 'both') { + var valueToMatch = filterCandidate.trim().substring(0, this.query.length); + if (this.query.indexOf(valueToMatch) > -1) { + showElement = true; + } + } + else if (filterCandidate.indexOf(this.query) > -1) { + showElement = true; + } + + // Toggle current element (group or group item) according to showElement boolean. + $(element).toggle(showElement) + .toggleClass('multiselect-filter-hidden', !showElement); + + // Differentiate groups and group items. + if ($(element).hasClass('multiselect-group')) { + // Remember group status. + currentGroup = element; + currentGroupVisible = showElement; + } + else { + // Show group name when at least one of its items is visible. + if (showElement) { + $(currentGroup).show() + .removeClass('multiselect-filter-hidden'); + } + + // Show all group items when group name satisfies filter. + if (!showElement && currentGroupVisible) { + $(element).show() + .removeClass('multiselect-filter-hidden'); + } + } + } + }, this)); + } + + this.updateSelectAll(); + + if (this.options.enableClickableOptGroups && this.options.multiple) { + this.updateOptGroups(); + } + + this.options.onFiltering(event.target); + + }, this), 300, this); + }, this)); + } + } + }, + + /** + * Unbinds the whole plugin. + */ + destroy: function() { + this.$container.remove(); + this.$select.show(); + + // reset original state + this.$select.prop('disabled', this.options.wasDisabled); + + this.$select.data('multiselect', null); + }, + + /** + * Refreshs the multiselect based on the selected options of the select. + */ + refresh: function () { + var inputs = $.map($('li input', this.$ul), $); + + $('option', this.$select).each($.proxy(function (index, element) { + var $elem = $(element); + var value = $elem.val(); + var $input; + for (var i = inputs.length; 0 < i--; /**/) { + if (value !== ($input = inputs[i]).val()) + continue; // wrong li + + if ($elem.is(':selected')) { + $input.prop('checked', true); + + if (this.options.selectedClass) { + $input.closest('li') + .addClass(this.options.selectedClass); + } + } + else { + $input.prop('checked', false); + + if (this.options.selectedClass) { + $input.closest('li') + .removeClass(this.options.selectedClass); + } + } + + if ($elem.is(":disabled")) { + $input.attr('disabled', 'disabled') + .prop('disabled', true) + .closest('li') + .addClass('disabled'); + } + else { + $input.prop('disabled', false) + .closest('li') + .removeClass('disabled'); + } + break; // assumes unique values + } + }, this)); + + this.updateButtonText(); + this.updateSelectAll(); + + if (this.options.enableClickableOptGroups && this.options.multiple) { + this.updateOptGroups(); + } + }, + + /** + * Select all options of the given values. + * + * If triggerOnChange is set to true, the on change event is triggered if + * and only if one value is passed. + * + * @param {Array} selectValues + * @param {Boolean} triggerOnChange + */ + select: function(selectValues, triggerOnChange) { + if(!$.isArray(selectValues)) { + selectValues = [selectValues]; + } + + for (var i = 0; i < selectValues.length; i++) { + var value = selectValues[i]; + + if (value === null || value === undefined) { + continue; + } + + var $option = this.getOptionByValue(value); + var $checkbox = this.getInputByValue(value); + + if($option === undefined || $checkbox === undefined) { + continue; + } + + if (!this.options.multiple) { + this.deselectAll(false); + } + + if (this.options.selectedClass) { + $checkbox.closest('li') + .addClass(this.options.selectedClass); + } + + $checkbox.prop('checked', true); + $option.prop('selected', true); + + if (triggerOnChange) { + this.options.onChange($option, true); + } + } + + this.updateButtonText(); + this.updateSelectAll(); + + if (this.options.enableClickableOptGroups && this.options.multiple) { + this.updateOptGroups(); + } + }, + + /** + * Clears all selected items. + */ + clearSelection: function () { + this.deselectAll(false); + this.updateButtonText(); + this.updateSelectAll(); + + if (this.options.enableClickableOptGroups && this.options.multiple) { + this.updateOptGroups(); + } + }, + + /** + * Deselects all options of the given values. + * + * If triggerOnChange is set to true, the on change event is triggered, if + * and only if one value is passed. + * + * @param {Array} deselectValues + * @param {Boolean} triggerOnChange + */ + deselect: function(deselectValues, triggerOnChange) { + if(!$.isArray(deselectValues)) { + deselectValues = [deselectValues]; + } + + for (var i = 0; i < deselectValues.length; i++) { + var value = deselectValues[i]; + + if (value === null || value === undefined) { + continue; + } + + var $option = this.getOptionByValue(value); + var $checkbox = this.getInputByValue(value); + + if($option === undefined || $checkbox === undefined) { + continue; + } + + if (this.options.selectedClass) { + $checkbox.closest('li') + .removeClass(this.options.selectedClass); + } + + $checkbox.prop('checked', false); + $option.prop('selected', false); + + if (triggerOnChange) { + this.options.onChange($option, false); + } + } + + this.updateButtonText(); + this.updateSelectAll(); + + if (this.options.enableClickableOptGroups && this.options.multiple) { + this.updateOptGroups(); + } + }, + + /** + * Selects all enabled & visible options. + * + * If justVisible is true or not specified, only visible options are selected. + * + * @param {Boolean} justVisible + * @param {Boolean} triggerOnSelectAll + */ + selectAll: function (justVisible, triggerOnSelectAll) { + + var justVisible = typeof justVisible === 'undefined' ? true : justVisible; + var allLis = $("li:not(.divider):not(.disabled):not(.multiselect-group)", this.$ul); + var visibleLis = $("li:not(.divider):not(.disabled):not(.multiselect-group):not(.multiselect-filter-hidden):not(.multiselect-collapisble-hidden)", this.$ul).filter(':visible'); + + if(justVisible) { + $('input:enabled' , visibleLis).prop('checked', true); + visibleLis.addClass(this.options.selectedClass); + + $('input:enabled' , visibleLis).each($.proxy(function(index, element) { + var value = $(element).val(); + var option = this.getOptionByValue(value); + $(option).prop('selected', true); + }, this)); + } + else { + $('input:enabled' , allLis).prop('checked', true); + allLis.addClass(this.options.selectedClass); + + $('input:enabled' , allLis).each($.proxy(function(index, element) { + var value = $(element).val(); + var option = this.getOptionByValue(value); + $(option).prop('selected', true); + }, this)); + } + + $('li input[value="' + this.options.selectAllValue + '"]', this.$ul).prop('checked', true); + + if (this.options.enableClickableOptGroups && this.options.multiple) { + this.updateOptGroups(); + } + + if (triggerOnSelectAll) { + this.options.onSelectAll(); + } + }, + + /** + * Deselects all options. + * + * If justVisible is true or not specified, only visible options are deselected. + * + * @param {Boolean} justVisible + */ + deselectAll: function (justVisible, triggerOnDeselectAll) { + + var justVisible = typeof justVisible === 'undefined' ? true : justVisible; + var allLis = $("li:not(.divider):not(.disabled):not(.multiselect-group)", this.$ul); + var visibleLis = $("li:not(.divider):not(.disabled):not(.multiselect-group):not(.multiselect-filter-hidden):not(.multiselect-collapisble-hidden)", this.$ul).filter(':visible'); + + if(justVisible) { + $('input[type="checkbox"]:enabled' , visibleLis).prop('checked', false); + visibleLis.removeClass(this.options.selectedClass); + + $('input[type="checkbox"]:enabled' , visibleLis).each($.proxy(function(index, element) { + var value = $(element).val(); + var option = this.getOptionByValue(value); + $(option).prop('selected', false); + }, this)); + } + else { + $('input[type="checkbox"]:enabled' , allLis).prop('checked', false); + allLis.removeClass(this.options.selectedClass); + + $('input[type="checkbox"]:enabled' , allLis).each($.proxy(function(index, element) { + var value = $(element).val(); + var option = this.getOptionByValue(value); + $(option).prop('selected', false); + }, this)); + } + + $('li input[value="' + this.options.selectAllValue + '"]', this.$ul).prop('checked', false); + + if (this.options.enableClickableOptGroups && this.options.multiple) { + this.updateOptGroups(); + } + + if (triggerOnDeselectAll) { + this.options.onDeselectAll(); + } + }, + + /** + * Rebuild the plugin. + * + * Rebuilds the dropdown, the filter and the select all option. + */ + rebuild: function() { + this.$ul.html(''); + + // Important to distinguish between radios and checkboxes. + this.options.multiple = this.$select.attr('multiple') === "multiple"; + + this.buildSelectAll(); + this.buildDropdownOptions(); + this.buildFilter(); + + this.updateButtonText(); + this.updateSelectAll(true); + + if (this.options.enableClickableOptGroups && this.options.multiple) { + this.updateOptGroups(); + } + + if (this.options.disableIfEmpty && $('option', this.$select).length <= 0) { + this.disable(); + } + else { + this.enable(); + } + + if (this.options.dropRight) { + this.$ul.addClass('pull-right'); + } + }, + + /** + * The provided data will be used to build the dropdown. + */ + dataprovider: function(dataprovider) { + + var groupCounter = 0; + var $select = this.$select.empty(); + + $.each(dataprovider, function (index, option) { + var $tag; + + if ($.isArray(option.children)) { // create optiongroup tag + groupCounter++; + + $tag = $('<optgroup/>').attr({ + label: option.label || 'Group ' + groupCounter, + disabled: !!option.disabled + }); + + forEach(option.children, function(subOption) { // add children option tags + var attributes = { + value: subOption.value, + label: subOption.label || subOption.value, + title: subOption.title, + selected: !!subOption.selected, + disabled: !!subOption.disabled + }; + + //Loop through attributes object and add key-value for each attribute + for (var key in subOption.attributes) { + attributes['data-' + key] = subOption.attributes[key]; + } + //Append original attributes + new data attributes to option + $tag.append($('<option/>').attr(attributes)); + }); + } + else { + + var attributes = { + 'value': option.value, + 'label': option.label || option.value, + 'title': option.title, + 'class': option.class, + 'selected': !!option.selected, + 'disabled': !!option.disabled + }; + //Loop through attributes object and add key-value for each attribute + for (var key in option.attributes) { + attributes['data-' + key] = option.attributes[key]; + } + //Append original attributes + new data attributes to option + $tag = $('<option/>').attr(attributes); + + $tag.text(option.label || option.value); + } + + $select.append($tag); + }); + + this.rebuild(); + }, + + /** + * Enable the multiselect. + */ + enable: function() { + this.$select.prop('disabled', false); + this.$button.prop('disabled', false) + .removeClass('disabled'); + }, + + /** + * Disable the multiselect. + */ + disable: function() { + this.$select.prop('disabled', true); + this.$button.prop('disabled', true) + .addClass('disabled'); + }, + + /** + * Set the options. + * + * @param {Array} options + */ + setOptions: function(options) { + this.options = this.mergeOptions(options); + }, + + /** + * Merges the given options with the default options. + * + * @param {Array} options + * @returns {Array} + */ + mergeOptions: function(options) { + return $.extend(true, {}, this.defaults, this.options, options); + }, + + /** + * Checks whether a select all checkbox is present. + * + * @returns {Boolean} + */ + hasSelectAll: function() { + return $('li.multiselect-all', this.$ul).length > 0; + }, + + /** + * Update opt groups. + */ + updateOptGroups: function() { + var $groups = $('li.multiselect-group', this.$ul) + var selectedClass = this.options.selectedClass; + + $groups.each(function() { + var $options = $(this).nextUntil('li.multiselect-group') + .not('.multiselect-filter-hidden') + .not('.disabled'); + + var checked = true; + $options.each(function() { + var $input = $('input', this); + + if (!$input.prop('checked')) { + checked = false; + } + }); + + if (selectedClass) { + if (checked) { + $(this).addClass(selectedClass); + } + else { + $(this).removeClass(selectedClass); + } + } + + $('input', this).prop('checked', checked); + }); + }, + + /** + * Updates the select all checkbox based on the currently displayed and selected checkboxes. + */ + updateSelectAll: function(notTriggerOnSelectAll) { + if (this.hasSelectAll()) { + var allBoxes = $("li:not(.multiselect-item):not(.multiselect-filter-hidden):not(.multiselect-group):not(.disabled) input:enabled", this.$ul); + var allBoxesLength = allBoxes.length; + var checkedBoxesLength = allBoxes.filter(":checked").length; + var selectAllLi = $("li.multiselect-all", this.$ul); + var selectAllInput = selectAllLi.find("input"); + + if (checkedBoxesLength > 0 && checkedBoxesLength === allBoxesLength) { + selectAllInput.prop("checked", true); + selectAllLi.addClass(this.options.selectedClass); + } + else { + selectAllInput.prop("checked", false); + selectAllLi.removeClass(this.options.selectedClass); + } + } + }, + + /** + * Update the button text and its title based on the currently selected options. + */ + updateButtonText: function() { + var options = this.getSelected(); + + // First update the displayed button text. + if (this.options.enableHTML) { + $('.multiselect .multiselect-selected-text', this.$container).html(this.options.buttonText(options, this.$select)); + } + else { + $('.multiselect .multiselect-selected-text', this.$container).text(this.options.buttonText(options, this.$select)); + } + + // Now update the title attribute of the button. + $('.multiselect', this.$container).attr('title', this.options.buttonTitle(options, this.$select)); + }, + + /** + * Get all selected options. + * + * @returns {jQUery} + */ + getSelected: function() { + return $('option', this.$select).filter(":selected"); + }, + + /** + * Gets a select option by its value. + * + * @param {String} value + * @returns {jQuery} + */ + getOptionByValue: function (value) { + + var options = $('option', this.$select); + var valueToCompare = value.toString(); + + for (var i = 0; i < options.length; i = i + 1) { + var option = options[i]; + if (option.value === valueToCompare) { + return $(option); + } + } + }, + + /** + * Get the input (radio/checkbox) by its value. + * + * @param {String} value + * @returns {jQuery} + */ + getInputByValue: function (value) { + + var checkboxes = $('li input:not(.multiselect-search)', this.$ul); + var valueToCompare = value.toString(); + + for (var i = 0; i < checkboxes.length; i = i + 1) { + var checkbox = checkboxes[i]; + if (checkbox.value === valueToCompare) { + return $(checkbox); + } + } + }, + + /** + * Used for knockout integration. + */ + updateOriginalOptions: function() { + this.originalOptions = this.$select.clone()[0].options; + }, + + asyncFunction: function(callback, timeout, self) { + var args = Array.prototype.slice.call(arguments, 3); + return setTimeout(function() { + callback.apply(self || window, args); + }, timeout); + }, + + setAllSelectedText: function(allSelectedText) { + this.options.allSelectedText = allSelectedText; + this.updateButtonText(); + } + }; + + $.fn.multiselect = function(option, parameter, extraOptions) { + return this.each(function() { + var data = $(this).data('multiselect'); + var options = typeof option === 'object' && option; + + // Initialize the multiselect. + if (!data) { + data = new Multiselect(this, options); + $(this).data('multiselect', data); + } + + // Call multiselect method. + if (typeof option === 'string') { + data[option](parameter, extraOptions); + + if (option === 'destroy') { + $(this).data('multiselect', false); + } + } + }); + }; + + $.fn.multiselect.Constructor = Multiselect; + + $(function() { + $("select[data-role=multiselect]").multiselect(); + }); + +}(window.jQuery); diff --git a/resources/views/reports/index.twig b/resources/views/reports/index.twig index 9d3ddeffe9..88f520b41a 100644 --- a/resources/views/reports/index.twig +++ b/resources/views/reports/index.twig @@ -35,17 +35,15 @@ <label for="inputAccounts" class="col-sm-3 control-label">{{ 'report_included_accounts'|_ }}</label> <div class="col-sm-9"> - {% for account in accounts %} - <div class="checkbox"> - <label> - <input type="checkbox" class="account-checkbox" name="accounts[]" value="{{ account.id }}"> - {{ account.name }} - {% if account.getMeta('accountRole') == 'sharedAsset' %} - ({{ 'shared'|_|lower }}) - {% endif %} - </label> - </div> - {% endfor %} + <select id="inputAccounts" name="accounts[]" multiple="multiple" class="form-control"> + {% for account in accounts %} + <option + value="{{ account.id }}" + label="{{ account.name }}{% if account.getMeta('accountRole') == 'sharedAsset' %} ({{ 'shared'|_|lower }}){% endif %}"> + {{ account.name }}{% if account.getMeta('accountRole') == 'sharedAsset' %} ({{ 'shared'|_|lower }}){% endif %} + </option> + {% endfor %} + </select> </div> </div> @@ -121,22 +119,38 @@ <h4>{{ ('quick_link_default_report')|_ }}</h4> <ul> - <li><a href="{{ route('reports.report.default',[accountList, 'currentMonthStart','currentMonthEnd']) }}">{{ 'report_this_month_quick'|_ }}</a></li> - <li><a href="{{ route('reports.report.default',[accountList, 'currentYearStart','currentYearEnd']) }}">{{ 'report_this_year_quick'|_ }}</a></li> + <li> + <a href="{{ route('reports.report.default',[accountList, 'currentMonthStart','currentMonthEnd']) }}">{{ 'report_this_month_quick'|_ }}</a> + </li> + <li> + <a href="{{ route('reports.report.default',[accountList, 'currentYearStart','currentYearEnd']) }}">{{ 'report_this_year_quick'|_ }}</a> + </li> {% if customFiscalYear == 1 %} - <li><a href="{{ route('reports.report.default',[accountList, 'currentFiscalYearStart','currentFiscalYearEnd']) }}">{{ 'report_this_fiscal_year_quick'|_ }}</a></li> + <li> + <a href="{{ route('reports.report.default',[accountList, 'currentFiscalYearStart','currentFiscalYearEnd']) }}">{{ 'report_this_fiscal_year_quick'|_ }}</a> + </li> {% endif %} - <li><a href="{{ route('reports.report.default',[accountList, start.format('Ymd'),'currentMonthEnd']) }}">{{ 'report_all_time_quick'|_ }}</a></li> + <li> + <a href="{{ route('reports.report.default',[accountList, start.format('Ymd'),'currentMonthEnd']) }}">{{ 'report_all_time_quick'|_ }}</a> + </li> </ul> <h4>{{ ('quick_link_audit_report')|_ }}</h4> <ul> - <li><a href="{{ route('reports.report.audit',[accountList, 'currentMonthStart','currentMonthEnd']) }}">{{ 'report_this_month_quick'|_ }}</a></li> - <li><a href="{{ route('reports.report.audit',[accountList, 'currentYearStart','currentYearEnd']) }}">{{ 'report_this_year_quick'|_ }}</a></li> + <li> + <a href="{{ route('reports.report.audit',[accountList, 'currentMonthStart','currentMonthEnd']) }}">{{ 'report_this_month_quick'|_ }}</a> + </li> + <li> + <a href="{{ route('reports.report.audit',[accountList, 'currentYearStart','currentYearEnd']) }}">{{ 'report_this_year_quick'|_ }}</a> + </li> {% if customFiscalYear == 1 %} - <li><a href="{{ route('reports.report.audit',[accountList, 'currentFiscalYearStart','currentFiscalYearEnd']) }}">{{ 'report_this_fiscal_year_quick'|_ }}</a></li> + <li> + <a href="{{ route('reports.report.audit',[accountList, 'currentFiscalYearStart','currentFiscalYearEnd']) }}">{{ 'report_this_fiscal_year_quick'|_ }}</a> + </li> {% endif %} - <li><a href="{{ route('reports.report.audit',[accountList, start.format('Ymd'),'currentMonthEnd']) }}">{{ 'report_all_time_quick'|_ }}</a></li> + <li> + <a href="{{ route('reports.report.audit',[accountList, start.format('Ymd'),'currentMonthEnd']) }}">{{ 'report_all_time_quick'|_ }}</a> + </li> </ul> {#<h4>{{ ('quick_link_category_report')|_ }}</h4>#} @@ -191,11 +205,16 @@ {% endblock %} +{% block styles %} + <link href="css/bootstrap-multiselect.css" rel="stylesheet" type="text/css"/> +{% endblock %} + {% block scripts %} <script type="text/javascript"> var minDate = "{{ start.format('m/d/Y') }}"; var picker; </script> + <script type="text/javascript" src="js/lib/bootstrap-multiselect.js"></script> <script type="text/javascript" src="js/ff/reports/index.js"></script> {% endblock %} From 7140ba76d5fbbc7eff97693533194eab4fddba74 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Tue, 24 Jan 2017 07:53:46 +0100 Subject: [PATCH 591/709] Small language things [skip ci] --- public/js/ff/reports/index.js | 10 ++++++++-- resources/lang/en_US/firefly.php | 3 +++ resources/views/reports/index.twig | 2 ++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/public/js/ff/reports/index.js b/public/js/ff/reports/index.js index 295cb659fe..416ae4c144 100644 --- a/public/js/ff/reports/index.js +++ b/public/js/ff/reports/index.js @@ -8,7 +8,13 @@ * See the LICENSE file for details. */ -/** global: minDate */ +/** global: minDate, nonSelectedText, allSelectedText */ + +var defaultMultiSelect = { + disableIfEmpty: true, + nonSelectedText: nonSelectedText, + allSelectedText: allSelectedText +}; $(function () { "use strict"; @@ -40,7 +46,7 @@ $(function () { } // make account select a hip new bootstrap multi-select thing. - $('#inputAccounts').multiselect(); + $('#inputAccounts').multiselect(defaultMultiSelect); // set date: var startStr = readCookie('report-start'); diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 84cd608220..6d326a1dd2 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -110,6 +110,9 @@ return [ 'clone_deposit' => 'Clone this deposit', 'clone_transfer' => 'Clone this transfer', 'transaction_journal_other_options' => 'Other options', + 'multi_select_no_selection' => 'None selected', + 'multi_select_all_selected' => 'All selected', + // repeat frequencies: 'repeat_freq_yearly' => 'yearly', diff --git a/resources/views/reports/index.twig b/resources/views/reports/index.twig index 88f520b41a..16ab2f19dd 100644 --- a/resources/views/reports/index.twig +++ b/resources/views/reports/index.twig @@ -213,6 +213,8 @@ <script type="text/javascript"> var minDate = "{{ start.format('m/d/Y') }}"; var picker; + var nonSelectedText = "{{ 'multi_select_no_selection'|_ }}"; + var allSelectedText = "{{ 'multi_select_all_selected'|_ }}"; </script> <script type="text/javascript" src="js/lib/bootstrap-multiselect.js"></script> From 90c16e2a07842e009c1732231ccfb01aa4e51cc0 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Tue, 24 Jan 2017 11:49:05 +0100 Subject: [PATCH 592/709] Update travis.yml. --- .travis.yml | 14 +++++++++++++- .../Commands/UpgradeFireflyInstructions.php | 4 ++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index bcba948aa5..d6dc7db792 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,19 @@ language: php sudo: false php: - - '7.0' + - 7.0 + - 7.1 + - nightly + - hhvm + +matrix: + allow_failures: + - php: hhvm + +cache: + directories: + - vendor + - $HOME/.composer/cache install: - phpenv config-rm xdebug.ini diff --git a/app/Console/Commands/UpgradeFireflyInstructions.php b/app/Console/Commands/UpgradeFireflyInstructions.php index 5e76a21cd5..a88c8ab79c 100644 --- a/app/Console/Commands/UpgradeFireflyInstructions.php +++ b/app/Console/Commands/UpgradeFireflyInstructions.php @@ -67,7 +67,7 @@ class UpgradeFireflyInstructions extends Command { $parts = explode("\n", wordwrap($text)); foreach ($parts as $string) { - $this->line('| ' . sprintf("%-77s", $string) . '|'); + $this->line('| ' . sprintf('%-77s', $string) . '|'); } } @@ -80,7 +80,7 @@ class UpgradeFireflyInstructions extends Command { $parts = explode("\n", wordwrap($text)); foreach ($parts as $string) { - $this->info('| ' . sprintf("%-77s", $string) . '|'); + $this->info('| ' . sprintf('%-77s', $string) . '|'); } } From d55dfe27dc422fadd84c1e3c4f08678c71bbc6a2 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Tue, 24 Jan 2017 12:08:06 +0100 Subject: [PATCH 593/709] Updated test script --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d6dc7db792..d1a11cc97b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,4 @@ language: php -sudo: false php: - 7.0 - 7.1 @@ -19,6 +18,7 @@ install: - phpenv config-rm xdebug.ini - rm composer.lock - composer update --no-scripts + - cp .env.testing .env - php artisan clear-compiled - php artisan optimize - php artisan env From 8602febe9d5651f3aad9a918f5e47d0a2197797b Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Tue, 24 Jan 2017 12:15:52 +0100 Subject: [PATCH 594/709] If/then thing for HHVM --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d1a11cc97b..052b08ee3d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ cache: - $HOME/.composer/cache install: - - phpenv config-rm xdebug.ini + - if [[ "$(php -v | grep 'PHP 7')" ]]; then phpenv config-rm xdebug.ini; fi - rm composer.lock - composer update --no-scripts - cp .env.testing .env From bc1079364ddc0687c5a4e1f641ecd2b7a274f4cb Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Tue, 24 Jan 2017 12:24:02 +0100 Subject: [PATCH 595/709] This should fix the nightly. No more hhvm. --- .travis.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 052b08ee3d..9d994ebbb7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,11 +3,6 @@ php: - 7.0 - 7.1 - nightly - - hhvm - -matrix: - allow_failures: - - php: hhvm cache: directories: @@ -15,7 +10,8 @@ cache: - $HOME/.composer/cache install: - - if [[ "$(php -v | grep 'PHP 7')" ]]; then phpenv config-rm xdebug.ini; fi + - if [[ "$(php -v | grep 'PHP 7.0')" ]]; then phpenv config-rm xdebug.ini; fi + - if [[ "$(php -v | grep 'PHP 7.1')" ]]; then phpenv config-rm xdebug.ini; fi - rm composer.lock - composer update --no-scripts - cp .env.testing .env From 76b32df622a8e3a06e6016b2ab5f7ec279692258 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Tue, 24 Jan 2017 12:33:16 +0100 Subject: [PATCH 596/709] Remove nightly. --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9d994ebbb7..040654a669 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ language: php php: - 7.0 - 7.1 - - nightly cache: directories: @@ -10,8 +9,7 @@ cache: - $HOME/.composer/cache install: - - if [[ "$(php -v | grep 'PHP 7.0')" ]]; then phpenv config-rm xdebug.ini; fi - - if [[ "$(php -v | grep 'PHP 7.1')" ]]; then phpenv config-rm xdebug.ini; fi + - if [[ "$(php -v | grep 'PHP 7')" ]]; then phpenv config-rm xdebug.ini; fi - rm composer.lock - composer update --no-scripts - cp .env.testing .env From e8303bd0596e598082c16c13c10fa674bbf1f468 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Tue, 24 Jan 2017 15:07:01 +0100 Subject: [PATCH 597/709] Expand the multi-select to various other fields. --- public/js/ff/reports/index.js | 19 +++++++++++++------ resources/lang/en_US/firefly.php | 1 + resources/views/reports/index.twig | 2 +- resources/views/reports/options/budget.twig | 10 +++++----- resources/views/reports/options/category.twig | 10 +++++----- 5 files changed, 25 insertions(+), 17 deletions(-) diff --git a/public/js/ff/reports/index.js b/public/js/ff/reports/index.js index 416ae4c144..b57faf8b04 100644 --- a/public/js/ff/reports/index.js +++ b/public/js/ff/reports/index.js @@ -8,12 +8,16 @@ * See the LICENSE file for details. */ -/** global: minDate, nonSelectedText, allSelectedText */ +/** global: minDate, nonSelectedText, allSelectedText, filterPlaceholder */ var defaultMultiSelect = { disableIfEmpty: true, nonSelectedText: nonSelectedText, - allSelectedText: allSelectedText + allSelectedText: allSelectedText, + includeSelectAllOption: true, + enableFiltering: true, + enableCaseInsensitiveFiltering: true, + filterPlaceholder: filterPlaceholder }; $(function () { @@ -33,11 +37,12 @@ $(function () { ); - // set values from cookies, if any: + // set report type from cookie, if any: if (!(readCookie('report-type') === null)) { $('select[name="report_type"]').val(readCookie('report-type')); } + // set accounts from cookie if ((readCookie('report-accounts') !== null)) { var arr = readCookie('report-accounts').split(','); arr.forEach(function (val) { @@ -48,7 +53,7 @@ $(function () { // make account select a hip new bootstrap multi-select thing. $('#inputAccounts').multiselect(defaultMultiSelect); - // set date: + // set date from cookie var startStr = readCookie('report-start'); var endStr = readCookie('report-end'); if (startStr !== null && endStr !== null && startStr.length == 8 && endStr.length == 8) { @@ -91,16 +96,18 @@ function setOptionalFromCookies() { if ((readCookie('report-categories') !== null)) { arr = readCookie('report-categories').split(','); arr.forEach(function (val) { - $('input[class="category-checkbox"][type="checkbox"][value="' + val + '"]').prop('checked', true); + $('#inputCategories').find('option[value="' + val + '"]').prop('selected', true); }); + $('#inputCategories').multiselect(defaultMultiSelect); } // 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); + $('#inputBudgets').find('option[value="' + val + '"]').prop('selected', true); }); + $('#inputBudgets').multiselect(defaultMultiSelect); } } diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 6d326a1dd2..abee277d95 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -112,6 +112,7 @@ return [ 'transaction_journal_other_options' => 'Other options', 'multi_select_no_selection' => 'None selected', 'multi_select_all_selected' => 'All selected', + 'multi_select_filter_placeholder' => 'Find..', // repeat frequencies: diff --git a/resources/views/reports/index.twig b/resources/views/reports/index.twig index 16ab2f19dd..85771558c0 100644 --- a/resources/views/reports/index.twig +++ b/resources/views/reports/index.twig @@ -215,8 +215,8 @@ var picker; var nonSelectedText = "{{ 'multi_select_no_selection'|_ }}"; var allSelectedText = "{{ 'multi_select_all_selected'|_ }}"; + var filterPlaceholder = "{{ 'multi_select_filter_placeholder'|_ }}"; </script> <script type="text/javascript" src="js/lib/bootstrap-multiselect.js"></script> - <script type="text/javascript" src="js/ff/reports/index.js"></script> {% endblock %} diff --git a/resources/views/reports/options/budget.twig b/resources/views/reports/options/budget.twig index b2e0e7862e..85d9bac773 100644 --- a/resources/views/reports/options/budget.twig +++ b/resources/views/reports/options/budget.twig @@ -1,8 +1,8 @@ <p> {{ 'select_budget'|_ }} </p> -{% for budget in budgets %} -<label class="checkbox-inline"> - <input type="checkbox" class="budget-checkbox" name="budget[]" value="{{ budget.id }}"> {{ budget.name }} -</label> -{% endfor %} +<select id="inputBudgets" name="budget[]" multiple="multiple" class="form-control"> + {% for budget in budgets %} + <option value="{{ budget.id }}" label="{{ budget.name }}">{{ budget.name }}</option> + {% endfor %} +</select> diff --git a/resources/views/reports/options/category.twig b/resources/views/reports/options/category.twig index 1487c42ed0..126467ca26 100644 --- a/resources/views/reports/options/category.twig +++ b/resources/views/reports/options/category.twig @@ -1,8 +1,8 @@ <p> {{ 'select_category'|_ }} </p> -{% for category in categories %} -<label class="checkbox-inline"> - <input type="checkbox" class="category-checkbox" name="category[]" value="{{ category.id }}"> {{ category.name }} -</label> -{% endfor %} +<select id="inputCategories" name="category[]" multiple="multiple" class="form-control"> + {% for category in categories %} + <option value="{{ category.id }}" label="{{ category.name }}">{{ category.name }}</option> + {% endfor %} +</select> \ No newline at end of file From 7688d7c619ba49b69dce86444277368da9755e15 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Tue, 24 Jan 2017 15:38:41 +0100 Subject: [PATCH 598/709] Include trigger that responds to tags --- app/Rules/Triggers/TagIs.php | 74 +++++++++++++++++++++++++++++++ config/firefly.php | 3 +- public/js/ff/rules/create-edit.js | 10 +++++ resources/lang/en_US/firefly.php | 1 + 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 app/Rules/Triggers/TagIs.php diff --git a/app/Rules/Triggers/TagIs.php b/app/Rules/Triggers/TagIs.php new file mode 100644 index 0000000000..684d806760 --- /dev/null +++ b/app/Rules/Triggers/TagIs.php @@ -0,0 +1,74 @@ +<?php +/** + * TagIs.php + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +declare(strict_types = 1); + +namespace FireflyIII\Rules\Triggers; + +use FireflyIII\Models\Tag; +use FireflyIII\Models\TransactionJournal; +use Log; + +/** + * Class TagIs + * + * @package FireflyIII\Rules\Triggers + */ +final class TagIs extends AbstractTrigger implements TriggerInterface +{ + + /** + * A trigger is said to "match anything", or match any given transaction, + * when the trigger value is very vague or has no restrictions. Easy examples + * are the "AmountMore"-trigger combined with an amount of 0: any given transaction + * has an amount of more than zero! Other examples are all the "Description"-triggers + * which have hard time handling empty trigger values such as "" or "*" (wild cards). + * + * If the user tries to create such a trigger, this method MUST return true so Firefly III + * can stop the storing / updating the trigger. If the trigger is in any way restrictive + * (even if it will still include 99.9% of the users transactions), this method MUST return + * false. + * + * @param null $value + * + * @return bool + */ + public static function willMatchEverything($value = null) + { + if (!is_null($value)) { + return false; + } + Log::error(sprintf('Cannot use %s with a null value.', self::class)); + + return true; + } + + /** + * @param TransactionJournal $journal + * + * @return bool + */ + public function triggered(TransactionJournal $journal): bool + { + $tags = $journal->tags()->get(); + /** @var Tag $tag */ + foreach ($tags as $tag) { + $name = strtolower($tag->tag); + // match on journal: + if ($name === strtolower($this->triggerValue)) { + Log::debug(sprintf('RuleTrigger TagIs for journal #%d: is tagged with "%s", return true.', $journal->id, $name)); + + return true; + } + } + Log::debug(sprintf('RuleTrigger TagIs for journal #%d: is not tagged with "%s", return false.', $journal->id, $this->triggerValue)); + + return false; + } +} diff --git a/config/firefly.php b/config/firefly.php index a68e72bb74..d3b9e7a268 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -166,7 +166,6 @@ return [ 'to_account_ends' => 'FireflyIII\Rules\Triggers\ToAccountEnds', 'to_account_is' => 'FireflyIII\Rules\Triggers\ToAccountIs', 'to_account_contains' => 'FireflyIII\Rules\Triggers\ToAccountContains', - 'transaction_type' => 'FireflyIII\Rules\Triggers\TransactionType', 'amount_less' => 'FireflyIII\Rules\Triggers\AmountLess', 'amount_exactly' => 'FireflyIII\Rules\Triggers\AmountExactly', 'amount_more' => 'FireflyIII\Rules\Triggers\AmountMore', @@ -174,8 +173,10 @@ return [ 'description_ends' => 'FireflyIII\Rules\Triggers\DescriptionEnds', 'description_contains' => 'FireflyIII\Rules\Triggers\DescriptionContains', 'description_is' => 'FireflyIII\Rules\Triggers\DescriptionIs', + 'transaction_type' => 'FireflyIII\Rules\Triggers\TransactionType', 'category_is' => 'FireflyIII\Rules\Triggers\CategoryIs', 'budget_is' => 'FireflyIII\Rules\Triggers\BudgetIs', + 'tag_is' => 'FireflyIII\Rules\Triggers\TagIs', ], 'rule-actions' => [ 'set_category' => 'FireflyIII\Rules\Actions\SetCategory', diff --git a/public/js/ff/rules/create-edit.js b/public/js/ff/rules/create-edit.js index ebee001611..c47ccdf140 100644 --- a/public/js/ff/rules/create-edit.js +++ b/public/js/ff/rules/create-edit.js @@ -268,6 +268,16 @@ function updateTriggerInput(selectList) { case 'to_account_contains': createAutoComplete(input, 'json/all-accounts'); break; + case 'tag_is': + // also make tag thing? + createAutoComplete(input, 'json/tags'); + break; + case 'budget_is': + createAutoComplete(input, 'json/budgets'); + break; + case 'category_is': + createAutoComplete(input, 'json/categories'); + break; case 'transaction_type': createAutoComplete(input, 'json/transaction-types'); break; diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index abee277d95..d54d34d4a6 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -262,6 +262,7 @@ return [ 'rule_trigger_description_is_choice' => 'Description is..', 'rule_trigger_category_is_choice' => 'Category is..', 'rule_trigger_budget_is_choice' => 'Budget is..', + 'rule_trigger_tag_is_choice' => '(A) tag is..', 'rule_trigger_store_journal' => 'When a transaction is created', 'rule_trigger_update_journal' => 'When a transaction is updated', 'rule_action_set_category' => 'Set category to ":action_value"', From c7fc10ac890e2b82d712b312e78369deae8d07d2 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 08:32:16 +0100 Subject: [PATCH 599/709] Remove debug statements. --- public/js/ff/rules/create-edit.js | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/public/js/ff/rules/create-edit.js b/public/js/ff/rules/create-edit.js index c47ccdf140..ded93e61a9 100644 --- a/public/js/ff/rules/create-edit.js +++ b/public/js/ff/rules/create-edit.js @@ -13,20 +13,16 @@ $(function () { "use strict"; if (triggerCount === 0) { - console.log('addNewTrigger() because count is zero'); addNewTrigger(); } if (actionCount === 0) { - console.log('addNewAction() because count is zero'); addNewAction(); } if (triggerCount > 0) { - console.log('onAddNewTrigger() because count is > zero'); onAddNewTrigger(); } if (actionCount > 0) { - console.log('onAddNewAction() because count is > zero'); onAddNewAction(); } @@ -43,15 +39,12 @@ $(function () { function addNewTrigger() { "use strict"; triggerCount++; - console.log('click on add_rule_trigger'); // disable the button $('.add_rule_trigger').attr('disabled', 'disabled'); - console.log('Disabled the button'); // get the HTML for the new trigger $.getJSON('json/trigger', {count: triggerCount}).done(function (data) { - console.log('new trigger html retrieved'); // append it to the other triggers $('tbody.rule-trigger-tbody').append(data.html); @@ -62,7 +55,6 @@ function addNewTrigger() { onAddNewTrigger(); $('.add_rule_trigger').removeAttr('disabled'); - console.log('Enabled the button'); }).fail(function () { @@ -78,12 +70,10 @@ function addNewTrigger() { */ function addNewAction() { "use strict"; - console.log('click on add_rule_action'); actionCount++; // disable the button $('.add_rule_action').attr('disabled', 'disabled'); - console.log('Disabled the button'); $.getJSON('json/action', {count: actionCount}).done(function (data) { @@ -97,13 +87,11 @@ function addNewAction() { onAddNewAction(); $('.add_rule_action').removeAttr('disabled'); - console.log('Enabled the button'); }).fail(function () { alert('Cannot get a new action.'); $('.add_rule_action').removeAttr('disabled'); - console.log('Enabled the button'); }); return false; } @@ -115,7 +103,6 @@ function addNewAction() { */ function removeTrigger(e) { "use strict"; - console.log('click on remove-trigger'); var target = $(e.target); if (target.prop("tagName") == "I") { target = target.parent(); @@ -125,7 +112,6 @@ function removeTrigger(e) { // if now at zero, immediatly add one again: if ($('.rule-trigger-tbody tr').length == 0) { - console.log('Add a new trigger again'); addNewTrigger(); } return false; @@ -138,7 +124,6 @@ function removeTrigger(e) { */ function removeAction(e) { "use strict"; - console.log('click on remove-action'); var target = $(e.target); if (target.prop("tagName") == "I") { target = target.parent(); @@ -148,7 +133,6 @@ function removeAction(e) { // if now at zero, immediatly add one again: if ($('.rule-action-tbody tr').length == 0) { - console.log('Add a new action again'); addNewAction(); } return false; @@ -160,7 +144,6 @@ function removeAction(e) { */ function onAddNewAction() { "use strict"; - console.log('now in onAddNewAction'); // update all "select action type" dropdown buttons so they will respond correctly $('select[name^="rule-action["]').unbind('change').change(function (e) { @@ -169,7 +152,6 @@ function onAddNewAction() { }); $.each($('.rule-action-holder'), function (i, v) { - console.log('Update action input for row ' + i); var holder = $(v); var select = holder.find('select'); updateActionInput(select); @@ -181,7 +163,6 @@ function onAddNewAction() { */ function onAddNewTrigger() { "use strict"; - console.log('now in onAddNewTrigger'); // update all "select trigger type" dropdown buttons so they will respond correctly $('select[name^="rule-trigger["]').unbind('change').change(function (e) { @@ -190,7 +171,6 @@ function onAddNewTrigger() { }); $.each($('.rule-trigger-holder'), function (i, v) { - console.log('Update trigger input for row ' + i); var holder = $(v); var select = holder.find('select'); updateTriggerInput(select); @@ -203,7 +183,6 @@ function onAddNewTrigger() { * @param selectList */ function updateActionInput(selectList) { - console.log('now in updateActionInput'); // the actual row this select list is in: var parent = selectList.parent().parent(); // the text input we're looking for: @@ -212,10 +191,8 @@ function updateActionInput(selectList) { switch (selectList.val()) { default: input.typeahead('destroy'); - console.log('Cannot or will not do stuff to "' + selectList.val() + '"'); break; case 'set_category': - console.log('Create autocomplete for category list'); createAutoComplete(input, 'json/categories'); break; case 'clear_category': @@ -224,7 +201,6 @@ function updateActionInput(selectList) { input.attr('disabled', 'disabled'); break; case 'set_budget': - console.log('Create autocomplete for budget list'); createAutoComplete(input, 'json/budgets'); break; case 'add_tag': @@ -256,7 +232,6 @@ function updateTriggerInput(selectList) { switch (selectList.val()) { default: input.typeahead('destroy'); - console.log('Cannot or will not add autocomplete to "' + selectList.val() + '"'); break; case 'from_account_starts': case 'from_account_ends': @@ -296,18 +271,15 @@ function updateTriggerInput(selectList) { * @param URI */ function createAutoComplete(input, URI) { - console.log('Will create auto-complete for "' + URI + '".'); input.typeahead('destroy'); $.getJSON(URI).done(function (data) { input.typeahead({source: data}); - console.log('Created new auto-complete!'); }); } function testRuleTriggers() { "use strict"; - console.log('click on test_rule_triggers'); // Serialize all trigger data var triggerData = $(".rule-trigger-tbody").find("input[type=text], input[type=checkbox], select").serializeArray(); From e47d6fb3ac073db39b2b844d72968e6c99ae5cae Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 10:33:18 +0100 Subject: [PATCH 600/709] Move default option to bottom. --- public/js/ff/rules/create-edit.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/public/js/ff/rules/create-edit.js b/public/js/ff/rules/create-edit.js index ded93e61a9..9100050012 100644 --- a/public/js/ff/rules/create-edit.js +++ b/public/js/ff/rules/create-edit.js @@ -189,9 +189,6 @@ function updateActionInput(selectList) { var input = parent.find('input[name^="rule-action-value["]'); input.removeAttr('disabled'); switch (selectList.val()) { - default: - input.typeahead('destroy'); - break; case 'set_category': createAutoComplete(input, 'json/categories'); break; @@ -216,6 +213,9 @@ function updateActionInput(selectList) { case 'set_destination_account': createAutoComplete(input, 'json/all-accounts'); break; + default: + input.typeahead('destroy'); + break; } } @@ -230,9 +230,6 @@ function updateTriggerInput(selectList) { // the text input we're looking for: var input = parent.find('input[name^="rule-trigger-value["]'); switch (selectList.val()) { - default: - input.typeahead('destroy'); - break; case 'from_account_starts': case 'from_account_ends': case 'from_account_is': @@ -262,6 +259,9 @@ function updateTriggerInput(selectList) { case 'description_is': createAutoComplete(input, 'json/transaction-journals/all'); break; + default: + input.typeahead('destroy'); + break; } } From 21222eb697e02a8612610d81fd09b098ff96bfd1 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 10:33:44 +0100 Subject: [PATCH 601/709] Mention new version. --- config/firefly.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/firefly.php b/config/firefly.php index d3b9e7a268..7d0627c636 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -24,7 +24,7 @@ return [ ], 'encryption' => (is_null(env('USE_ENCRYPTION')) || env('USE_ENCRYPTION') === true), 'chart' => 'chartjs', - 'version' => '4.3.2', + 'version' => '4.3.3', 'csv_import_enabled' => true, 'maxUploadSize' => 5242880, 'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'], From f64e8d8973f757dd8a5fe8609b5900a375813111 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 11:10:34 +0100 Subject: [PATCH 602/709] Composer update. [skip ci] --- composer.lock | 235 +++++++++++++++++++++++++------------------------- 1 file changed, 118 insertions(+), 117 deletions(-) diff --git a/composer.lock b/composer.lock index cf40011813..ae477e0989 100644 --- a/composer.lock +++ b/composer.lock @@ -378,28 +378,29 @@ }, { "name": "doctrine/collections", - "version": "v1.3.0", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/doctrine/collections.git", - "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a" + "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/6c1e4eef75f310ea1b3e30945e9f06e652128b8a", - "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a", + "url": "https://api.github.com/repos/doctrine/collections/zipball/1a4fb7e902202c33cce8c55989b945612943c2ba", + "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": "^5.6 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "doctrine/coding-standard": "~0.1@dev", + "phpunit/phpunit": "^5.7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "1.3.x-dev" } }, "autoload": { @@ -440,7 +441,7 @@ "collections", "iterator" ], - "time": "2015-04-14T22:21:58+00:00" + "time": "2017-01-03T10:49:41+00:00" }, { "name": "doctrine/common", @@ -517,16 +518,16 @@ }, { "name": "doctrine/dbal", - "version": "v2.5.9", + "version": "v2.5.10", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "0d2f8187d4b4c7b72d8e2acba359e25c36feaf5e" + "reference": "fc376f7a61498e18520cd6fa083752a4ca08072b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/0d2f8187d4b4c7b72d8e2acba359e25c36feaf5e", - "reference": "0d2f8187d4b4c7b72d8e2acba359e25c36feaf5e", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/fc376f7a61498e18520cd6fa083752a4ca08072b", + "reference": "fc376f7a61498e18520cd6fa083752a4ca08072b", "shasum": "" }, "require": { @@ -584,7 +585,7 @@ "persistence", "queryobject" ], - "time": "2017-01-19T13:27:33+00:00" + "time": "2017-01-23T23:17:10+00:00" }, { "name": "doctrine/inflector", @@ -982,16 +983,16 @@ }, { "name": "laravelcollective/html", - "version": "v5.3.0", + "version": "v5.3.1", "source": { "type": "git", "url": "https://github.com/LaravelCollective/html.git", - "reference": "961ce141c16c6b085128f209496c26efd3e681ca" + "reference": "2f7f2e127c6fed47f269ea29ab5efeb8f65e9d35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/LaravelCollective/html/zipball/961ce141c16c6b085128f209496c26efd3e681ca", - "reference": "961ce141c16c6b085128f209496c26efd3e681ca", + "url": "https://api.github.com/repos/LaravelCollective/html/zipball/2f7f2e127c6fed47f269ea29ab5efeb8f65e9d35", + "reference": "2f7f2e127c6fed47f269ea29ab5efeb8f65e9d35", "shasum": "" }, "require": { @@ -1032,7 +1033,7 @@ ], "description": "HTML and Form Builders for the Laravel Framework", "homepage": "http://laravelcollective.com", - "time": "2016-08-27T23:52:43+00:00" + "time": "2016-12-13T14:23:36+00:00" }, { "name": "league/commonmark", @@ -1105,16 +1106,16 @@ }, { "name": "league/csv", - "version": "8.1.2", + "version": "8.2.0", "source": { "type": "git", "url": "https://github.com/thephpleague/csv.git", - "reference": "33447984f7a7038fefaa5a6177e8407b66bc85b4" + "reference": "ef7eef710810c8bd0cf9371582ccd0123ff96d4b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/33447984f7a7038fefaa5a6177e8407b66bc85b4", - "reference": "33447984f7a7038fefaa5a6177e8407b66bc85b4", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/ef7eef710810c8bd0cf9371582ccd0123ff96d4b", + "reference": "ef7eef710810c8bd0cf9371582ccd0123ff96d4b", "shasum": "" }, "require": { @@ -1128,7 +1129,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "8.1-dev" + "dev-master": "8.2-dev" } }, "autoload": { @@ -1158,20 +1159,20 @@ "read", "write" ], - "time": "2016-10-27T11:21:24+00:00" + "time": "2017-01-25T13:32:07+00:00" }, { "name": "league/flysystem", - "version": "1.0.32", + "version": "1.0.33", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "1b5c4a0031697f46e779a9d1b309c2e1b24daeab" + "reference": "5c7f98498b12d47f9de90ec9186a90000125777c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/1b5c4a0031697f46e779a9d1b309c2e1b24daeab", - "reference": "1b5c4a0031697f46e779a9d1b309c2e1b24daeab", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/5c7f98498b12d47f9de90ec9186a90000125777c", + "reference": "5c7f98498b12d47f9de90ec9186a90000125777c", "shasum": "" }, "require": { @@ -1241,7 +1242,7 @@ "sftp", "storage" ], - "time": "2016-10-19T20:38:46+00:00" + "time": "2017-01-23T10:32:09+00:00" }, { "name": "monolog/monolog", @@ -1323,16 +1324,16 @@ }, { "name": "mtdowling/cron-expression", - "version": "v1.1.0", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/mtdowling/cron-expression.git", - "reference": "c9ee7886f5a12902b225a1a12f36bb45f9ab89e5" + "reference": "9504fa9ea681b586028adaaa0877db4aecf32bad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mtdowling/cron-expression/zipball/c9ee7886f5a12902b225a1a12f36bb45f9ab89e5", - "reference": "c9ee7886f5a12902b225a1a12f36bb45f9ab89e5", + "url": "https://api.github.com/repos/mtdowling/cron-expression/zipball/9504fa9ea681b586028adaaa0877db4aecf32bad", + "reference": "9504fa9ea681b586028adaaa0877db4aecf32bad", "shasum": "" }, "require": { @@ -1343,8 +1344,8 @@ }, "type": "library", "autoload": { - "psr-0": { - "Cron": "src/" + "psr-4": { + "Cron\\": "src/Cron/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1363,7 +1364,7 @@ "cron", "schedule" ], - "time": "2016-01-26T21:23:30+00:00" + "time": "2017-01-23T04:29:33+00:00" }, { "name": "nesbot/carbon", @@ -1782,23 +1783,23 @@ }, { "name": "rcrowe/twigbridge", - "version": "v0.9.3", + "version": "v0.9.4", "source": { "type": "git", "url": "https://github.com/rcrowe/TwigBridge.git", - "reference": "6226d33331bbb1cdf64593a786f7efd1670200a7" + "reference": "effda159c436b08eae1a9d9ba3d28bee8f7b0f3f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rcrowe/TwigBridge/zipball/6226d33331bbb1cdf64593a786f7efd1670200a7", - "reference": "6226d33331bbb1cdf64593a786f7efd1670200a7", + "url": "https://api.github.com/repos/rcrowe/TwigBridge/zipball/effda159c436b08eae1a9d9ba3d28bee8f7b0f3f", + "reference": "effda159c436b08eae1a9d9ba3d28bee8f7b0f3f", "shasum": "" }, "require": { - "illuminate/support": "5.0.*|5.1.*|5.2.*|5.3.*", - "illuminate/view": "5.0.*|5.1.*|5.2.*|5.3.*", + "illuminate/support": "5.0.*|5.1.*|5.2.*|5.3.*|5.4.*", + "illuminate/view": "5.0.*|5.1.*|5.2.*|5.3.*|5.4.*", "php": ">=5.4.0", - "twig/twig": "~1.15|~2.0" + "twig/twig": "~1.30" }, "require-dev": { "laravel/framework": "5.0.*", @@ -1842,7 +1843,7 @@ "laravel", "twig" ], - "time": "2016-05-01T16:43:38+00:00" + "time": "2017-01-21T14:33:47+00:00" }, { "name": "rmccue/requests", @@ -1949,7 +1950,7 @@ }, { "name": "symfony/console", - "version": "v3.1.9", + "version": "v3.1.10", "source": { "type": "git", "url": "https://github.com/symfony/console.git", @@ -2010,16 +2011,16 @@ }, { "name": "symfony/debug", - "version": "v3.1.9", + "version": "v3.1.10", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "73f1c337907ba963af8028844fea1af98498dfff" + "reference": "c6661361626b3cf5cf2089df98b3b5006a197e85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/73f1c337907ba963af8028844fea1af98498dfff", - "reference": "73f1c337907ba963af8028844fea1af98498dfff", + "url": "https://api.github.com/repos/symfony/debug/zipball/c6661361626b3cf5cf2089df98b3b5006a197e85", + "reference": "c6661361626b3cf5cf2089df98b3b5006a197e85", "shasum": "" }, "require": { @@ -2063,7 +2064,7 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2017-01-02T20:31:54+00:00" + "time": "2017-01-28T00:04:57+00:00" }, { "name": "symfony/event-dispatcher", @@ -2127,7 +2128,7 @@ }, { "name": "symfony/finder", - "version": "v3.1.9", + "version": "v3.1.10", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", @@ -2176,7 +2177,7 @@ }, { "name": "symfony/http-foundation", - "version": "v3.1.9", + "version": "v3.1.10", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", @@ -2229,16 +2230,16 @@ }, { "name": "symfony/http-kernel", - "version": "v3.1.9", + "version": "v3.1.10", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "d7578a0ed01e689f5b058e1ed37b9ad0718a1ef3" + "reference": "c830387dec1b48c100473d10a6a356c3c3ae2a13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/d7578a0ed01e689f5b058e1ed37b9ad0718a1ef3", - "reference": "d7578a0ed01e689f5b058e1ed37b9ad0718a1ef3", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/c830387dec1b48c100473d10a6a356c3c3ae2a13", + "reference": "c830387dec1b48c100473d10a6a356c3c3ae2a13", "shasum": "" }, "require": { @@ -2307,7 +2308,7 @@ ], "description": "Symfony HttpKernel Component", "homepage": "https://symfony.com", - "time": "2017-01-12T20:43:39+00:00" + "time": "2017-01-28T02:53:17+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -2478,16 +2479,16 @@ }, { "name": "symfony/process", - "version": "v3.1.9", + "version": "v3.1.10", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "b525066a9efe372f0910296e486aa61741b09025" + "reference": "2605753c5f8c531623d24d002825ebb1d6a22248" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/b525066a9efe372f0910296e486aa61741b09025", - "reference": "b525066a9efe372f0910296e486aa61741b09025", + "url": "https://api.github.com/repos/symfony/process/zipball/2605753c5f8c531623d24d002825ebb1d6a22248", + "reference": "2605753c5f8c531623d24d002825ebb1d6a22248", "shasum": "" }, "require": { @@ -2523,20 +2524,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2017-01-02T20:31:54+00:00" + "time": "2017-01-21T17:13:55+00:00" }, { "name": "symfony/routing", - "version": "v3.1.9", + "version": "v3.1.10", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "5cd8d7b88e9f30a7d830fa15876828da272685d3" + "reference": "f25581d4eb0a82962c291917f826166f0dcd8a9a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/5cd8d7b88e9f30a7d830fa15876828da272685d3", - "reference": "5cd8d7b88e9f30a7d830fa15876828da272685d3", + "url": "https://api.github.com/repos/symfony/routing/zipball/f25581d4eb0a82962c291917f826166f0dcd8a9a", + "reference": "f25581d4eb0a82962c291917f826166f0dcd8a9a", "shasum": "" }, "require": { @@ -2598,20 +2599,20 @@ "uri", "url" ], - "time": "2017-01-02T20:31:54+00:00" + "time": "2017-01-28T00:04:57+00:00" }, { "name": "symfony/translation", - "version": "v3.1.9", + "version": "v3.1.10", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "7882149d1e1fd46d960f3e42344c9caf2e535573" + "reference": "d5a20fab5f63f44c233c69b3041c3cb1d4945e45" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/7882149d1e1fd46d960f3e42344c9caf2e535573", - "reference": "7882149d1e1fd46d960f3e42344c9caf2e535573", + "url": "https://api.github.com/repos/symfony/translation/zipball/d5a20fab5f63f44c233c69b3041c3cb1d4945e45", + "reference": "d5a20fab5f63f44c233c69b3041c3cb1d4945e45", "shasum": "" }, "require": { @@ -2662,20 +2663,20 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2017-01-02T20:31:54+00:00" + "time": "2017-01-21T17:01:39+00:00" }, { "name": "symfony/var-dumper", - "version": "v3.1.9", + "version": "v3.1.10", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "d34a930421233f119fe61149ce046bb5b0b412d9" + "reference": "16df11647e5b992d687cb4eeeb9a882d5f5c26b9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/d34a930421233f119fe61149ce046bb5b0b412d9", - "reference": "d34a930421233f119fe61149ce046bb5b0b412d9", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/16df11647e5b992d687cb4eeeb9a882d5f5c26b9", + "reference": "16df11647e5b992d687cb4eeeb9a882d5f5c26b9", "shasum": "" }, "require": { @@ -2725,7 +2726,7 @@ "debug", "dump" ], - "time": "2017-01-02T20:31:54+00:00" + "time": "2017-01-24T13:02:38+00:00" }, { "name": "twig/twig", @@ -2892,16 +2893,16 @@ "packages-dev": [ { "name": "barryvdh/laravel-debugbar", - "version": "v2.3.1", + "version": "v2.3.2", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-debugbar.git", - "reference": "65b0465e38a9524c9d5eb2dfc0389aba23090625" + "reference": "24e4f0261e352d3fd86d0447791b56ae49398674" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/65b0465e38a9524c9d5eb2dfc0389aba23090625", - "reference": "65b0465e38a9524c9d5eb2dfc0389aba23090625", + "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/24e4f0261e352d3fd86d0447791b56ae49398674", + "reference": "24e4f0261e352d3fd86d0447791b56ae49398674", "shasum": "" }, "require": { @@ -2942,27 +2943,27 @@ "profiler", "webprofiler" ], - "time": "2017-01-05T08:53:44+00:00" + "time": "2017-01-19T08:19:49+00:00" }, { "name": "barryvdh/laravel-ide-helper", - "version": "v2.2.2", + "version": "v2.2.3", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-ide-helper.git", - "reference": "105f14a50d0959a0e80004a15b3350fdf78f9623" + "reference": "a7fc2ec489aada6062d3a63ddc915004a21e38af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/105f14a50d0959a0e80004a15b3350fdf78f9623", - "reference": "105f14a50d0959a0e80004a15b3350fdf78f9623", + "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/a7fc2ec489aada6062d3a63ddc915004a21e38af", + "reference": "a7fc2ec489aada6062d3a63ddc915004a21e38af", "shasum": "" }, "require": { "barryvdh/reflection-docblock": "^2.0.4", - "illuminate/console": "^5.0,<5.4", - "illuminate/filesystem": "^5.0,<5.4", - "illuminate/support": "^5.0,<5.4", + "illuminate/console": "^5.0,<5.5", + "illuminate/filesystem": "^5.0,<5.5", + "illuminate/support": "^5.0,<5.5", "php": ">=5.4.0", "symfony/class-loader": "^2.3|^3.0" }, @@ -3008,7 +3009,7 @@ "phpstorm", "sublime" ], - "time": "2016-11-15T08:21:23+00:00" + "time": "2017-01-05T21:20:42+00:00" }, { "name": "barryvdh/reflection-docblock", @@ -3384,16 +3385,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.5.5", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "399c1f9781e222f6eb6cc238796f5200d1b7f108" + "reference": "5a5a9fc8025a08d8919be87d6884d5a92520cefe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/399c1f9781e222f6eb6cc238796f5200d1b7f108", - "reference": "399c1f9781e222f6eb6cc238796f5200d1b7f108", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/5a5a9fc8025a08d8919be87d6884d5a92520cefe", + "reference": "5a5a9fc8025a08d8919be87d6884d5a92520cefe", "shasum": "" }, "require": { @@ -3422,7 +3423,7 @@ "object", "object graph" ], - "time": "2016-10-31T17:19:45+00:00" + "time": "2017-01-26T22:05:40+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -3635,16 +3636,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "4.0.4", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "c14196e64a78570034afd0b7a9f3757ba71c2a0a" + "reference": "c19cfc7cbb0e9338d8c469c7eedecc2a428b0971" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c14196e64a78570034afd0b7a9f3757ba71c2a0a", - "reference": "c14196e64a78570034afd0b7a9f3757ba71c2a0a", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c19cfc7cbb0e9338d8c469c7eedecc2a428b0971", + "reference": "c19cfc7cbb0e9338d8c469c7eedecc2a428b0971", "shasum": "" }, "require": { @@ -3694,7 +3695,7 @@ "testing", "xunit" ], - "time": "2016-12-20T15:22:42+00:00" + "time": "2017-01-20T15:06:43+00:00" }, { "name": "phpunit/php-file-iterator", @@ -3879,16 +3880,16 @@ }, { "name": "phpunit/phpunit", - "version": "5.7.5", + "version": "5.7.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "50fd2be8f3e23e91da825f36f08e5f9633076ffe" + "reference": "69f832b87c731d5cacad7f91948778fe98335fdd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/50fd2be8f3e23e91da825f36f08e5f9633076ffe", - "reference": "50fd2be8f3e23e91da825f36f08e5f9633076ffe", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/69f832b87c731d5cacad7f91948778fe98335fdd", + "reference": "69f832b87c731d5cacad7f91948778fe98335fdd", "shasum": "" }, "require": { @@ -3900,7 +3901,7 @@ "myclabs/deep-copy": "~1.3", "php": "^5.6 || ^7.0", "phpspec/prophecy": "^1.6.2", - "phpunit/php-code-coverage": "^4.0.3", + "phpunit/php-code-coverage": "^4.0.4", "phpunit/php-file-iterator": "~1.4", "phpunit/php-text-template": "~1.2", "phpunit/php-timer": "^1.0.6", @@ -3957,7 +3958,7 @@ "testing", "xunit" ], - "time": "2016-12-28T07:18:51+00:00" + "time": "2017-01-28T06:14:33+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -4065,16 +4066,16 @@ }, { "name": "sebastian/comparator", - "version": "1.2.2", + "version": "1.2.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f" + "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6a1ed12e8b2409076ab22e3897126211ff8b1f7f", - "reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", "shasum": "" }, "require": { @@ -4125,7 +4126,7 @@ "compare", "equality" ], - "time": "2016-11-19T09:18:40+00:00" + "time": "2017-01-29T09:50:25+00:00" }, { "name": "sebastian/diff", @@ -4589,7 +4590,7 @@ }, { "name": "symfony/css-selector", - "version": "v3.1.9", + "version": "v3.1.10", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -4642,16 +4643,16 @@ }, { "name": "symfony/dom-crawler", - "version": "v3.1.9", + "version": "v3.1.10", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "a950260ebc947578fba82a3222e2085d90682376" + "reference": "7eede2a901a19928494194f7d1815a77b9a473a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/a950260ebc947578fba82a3222e2085d90682376", - "reference": "a950260ebc947578fba82a3222e2085d90682376", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/7eede2a901a19928494194f7d1815a77b9a473a0", + "reference": "7eede2a901a19928494194f7d1815a77b9a473a0", "shasum": "" }, "require": { @@ -4694,7 +4695,7 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2017-01-02T20:31:54+00:00" + "time": "2017-01-21T17:13:55+00:00" }, { "name": "symfony/yaml", From 7a40c34cf024d31a75173b7e0c5984505e36f432 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 11:18:02 +0100 Subject: [PATCH 603/709] New change log. --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e278c4994..5dc3f748c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,28 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [4.3.3] - 2017-01-30 + +_The 100th release of Firefly!_ + +### Added +- Add locales to Docker (#534) by @elohmeier. +- Optional database encryption. On by default. +- Datepicker for Firefox and other browsers. +- New instruction block for updating and installing. +- Ability to clone transactions. +- Use multi-select Bootstrap thing instead of massive lists of checkboxes. + +### Removed +- Lots of old Javascript + +### Fixed +- Missing sort broke various charts +- Bug in reports that made amounts behave weird +- Various bug fixes + +### Security +- Tested FF against the naughty string list. ## [4.3.2] - 2017-01-09 From 91c96311de1c934cf446e0588ad5d7882c1b62e1 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 11:41:39 +0100 Subject: [PATCH 604/709] Expand contributing guidelines. [skip ci] --- .github/CONTRIBUTING.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 2d9bcc0192..b8a2038ebb 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -4,4 +4,8 @@ Thank you for taking the time to report an issue or requesting a new feature. Please take note that there are NO rules or regulations when you submit an issue. +However, remember that you are probably not the first one to ask a question. Especially when the installation is concerned. + +So take the time to read the [installation guide FAQ](https://firefly-iii.github.io/installation-guide-faq/) and make sure you search through closed issues for the problems other people have had. Your issue may be among them! + If you are requesting a new feature, please check out the list of [often requested features](https://firefly-iii.github.io/requested-features/). From 2f943c91d2c718a70052eecd9044732bd0e62954 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 14:13:52 +0100 Subject: [PATCH 605/709] New contributing guidelines [skip ci] --- .github/CONTRIBUTING.md | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index b8a2038ebb..f7ef4c3bb3 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,11 +1,19 @@ -## Hi there! +# Welcome to Firefly III on Github! -Thank you for taking the time to report an issue or requesting a new feature. +:+1::tada: Thank you for taking the time to contribute something to Firefly III! -Please take note that there are NO rules or regulations when you submit an issue. - -However, remember that you are probably not the first one to ask a question. Especially when the installation is concerned. - -So take the time to read the [installation guide FAQ](https://firefly-iii.github.io/installation-guide-faq/) and make sure you search through closed issues for the problems other people have had. Your issue may be among them! +## Feature requests If you are requesting a new feature, please check out the list of [often requested features](https://firefly-iii.github.io/requested-features/). + +## Bugs + +If you find a bug, include as many log files and details as you think are necessary. + +## Installation problems + +Take the time to read the [installation guide FAQ](https://firefly-iii.github.io/installation-guide-faq/) and make sure you search through closed issues for the problems other people have had. Your problem may be among them! + +## Pull requests + +I can only accept pull requests against the `develop` branch, never the `master` branch From 29f763d4e4779dd1b3264477c89ffc113705b91e Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 16:20:23 +0100 Subject: [PATCH 606/709] Update composer files. --- composer.json | 4 +- composer.lock | 688 ++++++++++++++++---------------------------------- 2 files changed, 214 insertions(+), 478 deletions(-) diff --git a/composer.json b/composer.json index 6f7bea2597..5c6784f143 100755 --- a/composer.json +++ b/composer.json @@ -48,7 +48,7 @@ "require": { "php": ">=7.0.0", "ext-intl": "*", - "laravel/framework": "5.3.29", + "laravel/framework": "5.4.*", "davejamesmiller/laravel-breadcrumbs": "^3.0", "watson/validating": "3.*", "doctrine/dbal": "^2.5", @@ -56,7 +56,7 @@ "twig/twig": "1.30.0", "rcrowe/twigbridge": "0.9.*", "league/csv": "8.*", - "laravelcollective/html": "^5.3", + "laravelcollective/html": "^5.4", "rmccue/requests": "1.*", "pragmarx/google2fa": "1.*", "bacon/bacon-qr-code": "1.*" diff --git a/composer.lock b/composer.lock index ae477e0989..0d10a81094 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "db26ae145d3656fe05d8a222fc21e263", + "content-hash": "3baa1b043873285dc937f9dad0083506", "packages": [ { "name": "bacon/bacon-qr-code", @@ -102,60 +102,6 @@ ], "time": "2016-05-05T11:49:03+00:00" }, - { - "name": "classpreloader/classpreloader", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/ClassPreloader/ClassPreloader.git", - "reference": "bc7206aa892b5a33f4680421b69b191efd32b096" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ClassPreloader/ClassPreloader/zipball/bc7206aa892b5a33f4680421b69b191efd32b096", - "reference": "bc7206aa892b5a33f4680421b69b191efd32b096", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^1.0|^2.0|^3.0", - "php": ">=5.5.9" - }, - "require-dev": { - "phpunit/phpunit": "^4.8|^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "psr-4": { - "ClassPreloader\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com" - }, - { - "name": "Graham Campbell", - "email": "graham@alt-three.com" - } - ], - "description": "Helps class loading performance by generating a single PHP file containing all of the autoloaded files for a specific use case", - "keywords": [ - "autoload", - "class", - "preload" - ], - "time": "2016-09-16T12:50:15+00:00" - }, { "name": "davejamesmiller/laravel-breadcrumbs", "version": "3.0.1", @@ -205,39 +151,6 @@ ], "time": "2016-08-28T16:57:03+00:00" }, - { - "name": "dnoegel/php-xdg-base-dir", - "version": "0.1", - "source": { - "type": "git", - "url": "https://github.com/dnoegel/php-xdg-base-dir.git", - "reference": "265b8593498b997dc2d31e75b89f053b5cc9621a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/265b8593498b997dc2d31e75b89f053b5cc9621a", - "reference": "265b8593498b997dc2d31e75b89f053b5cc9621a", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "require-dev": { - "phpunit/phpunit": "@stable" - }, - "type": "project", - "autoload": { - "psr-4": { - "XdgBaseDir\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "implementation of xdg base directory specification for php", - "time": "2014-10-24T07:27:01+00:00" - }, { "name": "doctrine/annotations", "version": "v1.3.1", @@ -709,77 +622,26 @@ "time": "2014-09-09T13:34:57+00:00" }, { - "name": "jakub-onderka/php-console-color", - "version": "0.1", + "name": "erusev/parsedown", + "version": "1.6.1", "source": { "type": "git", - "url": "https://github.com/JakubOnderka/PHP-Console-Color.git", - "reference": "e0b393dacf7703fc36a4efc3df1435485197e6c1" + "url": "https://github.com/erusev/parsedown.git", + "reference": "20ff8bbb57205368b4b42d094642a3e52dac85fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/JakubOnderka/PHP-Console-Color/zipball/e0b393dacf7703fc36a4efc3df1435485197e6c1", - "reference": "e0b393dacf7703fc36a4efc3df1435485197e6c1", + "url": "https://api.github.com/repos/erusev/parsedown/zipball/20ff8bbb57205368b4b42d094642a3e52dac85fb", + "reference": "20ff8bbb57205368b4b42d094642a3e52dac85fb", "shasum": "" }, "require": { - "php": ">=5.3.2" - }, - "require-dev": { - "jakub-onderka/php-code-style": "1.0", - "jakub-onderka/php-parallel-lint": "0.*", - "jakub-onderka/php-var-dump-check": "0.*", - "phpunit/phpunit": "3.7.*", - "squizlabs/php_codesniffer": "1.*" - }, - "type": "library", - "autoload": { - "psr-0": { - "JakubOnderka\\PhpConsoleColor": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-2-Clause" - ], - "authors": [ - { - "name": "Jakub Onderka", - "email": "jakub.onderka@gmail.com", - "homepage": "http://www.acci.cz" - } - ], - "time": "2014-04-08T15:00:19+00:00" - }, - { - "name": "jakub-onderka/php-console-highlighter", - "version": "v0.3.2", - "source": { - "type": "git", - "url": "https://github.com/JakubOnderka/PHP-Console-Highlighter.git", - "reference": "7daa75df45242c8d5b75a22c00a201e7954e4fb5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/JakubOnderka/PHP-Console-Highlighter/zipball/7daa75df45242c8d5b75a22c00a201e7954e4fb5", - "reference": "7daa75df45242c8d5b75a22c00a201e7954e4fb5", - "shasum": "" - }, - "require": { - "jakub-onderka/php-console-color": "~0.1", "php": ">=5.3.0" }, - "require-dev": { - "jakub-onderka/php-code-style": "~1.0", - "jakub-onderka/php-parallel-lint": "~0.5", - "jakub-onderka/php-var-dump-check": "~0.1", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~1.5" - }, "type": "library", "autoload": { "psr-0": { - "JakubOnderka\\PhpConsoleHighlighter": "src/" + "Parsedown": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -788,109 +650,55 @@ ], "authors": [ { - "name": "Jakub Onderka", - "email": "acci@acci.cz", - "homepage": "http://www.acci.cz/" + "name": "Emanuil Rusev", + "email": "hello@erusev.com", + "homepage": "http://erusev.com" } ], - "time": "2015-04-20T18:58:01+00:00" - }, - { - "name": "jeremeamia/SuperClosure", - "version": "2.3.0", - "source": { - "type": "git", - "url": "https://github.com/jeremeamia/super_closure.git", - "reference": "443c3df3207f176a1b41576ee2a66968a507b3db" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/jeremeamia/super_closure/zipball/443c3df3207f176a1b41576ee2a66968a507b3db", - "reference": "443c3df3207f176a1b41576ee2a66968a507b3db", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^1.2|^2.0|^3.0", - "php": ">=5.4", - "symfony/polyfill-php56": "^1.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0|^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3-dev" - } - }, - "autoload": { - "psr-4": { - "SuperClosure\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jeremy Lindblom", - "email": "jeremeamia@gmail.com", - "homepage": "https://github.com/jeremeamia", - "role": "Developer" - } - ], - "description": "Serialize Closure objects, including their context and binding", - "homepage": "https://github.com/jeremeamia/super_closure", + "description": "Parser for Markdown.", + "homepage": "http://parsedown.org", "keywords": [ - "closure", - "function", - "lambda", - "parser", - "serializable", - "serialize", - "tokenizer" + "markdown", + "parser" ], - "time": "2016-12-07T09:37:55+00:00" + "time": "2016-11-02T15:56:58+00:00" }, { "name": "laravel/framework", - "version": "v5.3.29", + "version": "v5.4.6", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "6fd76dec90466dc3f703d8df72e38130f2ee6a32" + "reference": "5913b960656d7f978605ff9c1513ffbfd190c173" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/6fd76dec90466dc3f703d8df72e38130f2ee6a32", - "reference": "6fd76dec90466dc3f703d8df72e38130f2ee6a32", + "url": "https://api.github.com/repos/laravel/framework/zipball/5913b960656d7f978605ff9c1513ffbfd190c173", + "reference": "5913b960656d7f978605ff9c1513ffbfd190c173", "shasum": "" }, "require": { - "classpreloader/classpreloader": "~3.0", "doctrine/inflector": "~1.0", + "erusev/parsedown": "~1.6", "ext-mbstring": "*", "ext-openssl": "*", - "jeremeamia/superclosure": "~2.2", "league/flysystem": "~1.0", "monolog/monolog": "~1.11", "mtdowling/cron-expression": "~1.0", "nesbot/carbon": "~1.20", "paragonie/random_compat": "~1.4|~2.0", "php": ">=5.6.4", - "psy/psysh": "0.7.*|0.8.*", "ramsey/uuid": "~3.0", "swiftmailer/swiftmailer": "~5.4", - "symfony/console": "3.1.*", - "symfony/debug": "3.1.*", - "symfony/finder": "3.1.*", - "symfony/http-foundation": "3.1.*", - "symfony/http-kernel": "3.1.*", - "symfony/process": "3.1.*", - "symfony/routing": "3.1.*", - "symfony/translation": "3.1.*", - "symfony/var-dumper": "3.1.*", + "symfony/console": "~3.2", + "symfony/debug": "~3.2", + "symfony/finder": "~3.2", + "symfony/http-foundation": "~3.2", + "symfony/http-kernel": "~3.2", + "symfony/process": "~3.2", + "symfony/routing": "~3.2", + "symfony/var-dumper": "~3.2", + "tijsverkoyen/css-to-inline-styles": "~2.2", "vlucas/phpdotenv": "~2.2" }, "replace": { @@ -927,31 +735,34 @@ }, "require-dev": { "aws/aws-sdk-php": "~3.0", + "doctrine/dbal": "~2.5", "mockery/mockery": "~0.9.4", "pda/pheanstalk": "~3.0", - "phpunit/phpunit": "~5.4", + "phpunit/phpunit": "~5.7", "predis/predis": "~1.0", - "symfony/css-selector": "3.1.*", - "symfony/dom-crawler": "3.1.*" + "symfony/css-selector": "~3.2", + "symfony/dom-crawler": "~3.2" }, "suggest": { "aws/aws-sdk-php": "Required to use the SQS queue driver and SES mail driver (~3.0).", - "doctrine/dbal": "Required to rename columns and drop SQLite columns (~2.4).", + "doctrine/dbal": "Required to rename columns and drop SQLite columns (~2.5).", "fzaninotto/faker": "Required to use the eloquent factory builder (~1.4).", - "guzzlehttp/guzzle": "Required to use the Mailgun and Mandrill mail drivers and the ping methods on schedules (~5.3|~6.0).", + "guzzlehttp/guzzle": "Required to use the Mailgun and Mandrill mail drivers and the ping methods on schedules (~6.0).", + "laravel/tinker": "Required to use the tinker console command (~1.0).", "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (~1.0).", "league/flysystem-rackspace": "Required to use the Flysystem Rackspace driver (~1.0).", + "nexmo/client": "Required to use the Nexmo transport (~1.0).", "pda/pheanstalk": "Required to use the beanstalk queue driver (~3.0).", "predis/predis": "Required to use the redis cache and queue drivers (~1.0).", "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (~2.0).", - "symfony/css-selector": "Required to use some of the crawler integration testing tools (3.1.*).", - "symfony/dom-crawler": "Required to use most of the crawler integration testing tools (3.1.*).", - "symfony/psr-http-message-bridge": "Required to use psr7 bridging features (0.2.*)." + "symfony/css-selector": "Required to use some of the crawler integration testing tools (~3.2).", + "symfony/dom-crawler": "Required to use most of the crawler integration testing tools (~3.2).", + "symfony/psr-http-message-bridge": "Required to psr7 bridging features (0.2.*)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.3-dev" + "dev-master": "5.4-dev" } }, "autoload": { @@ -979,32 +790,32 @@ "framework", "laravel" ], - "time": "2017-01-06T14:33:56+00:00" + "time": "2017-01-27T19:27:15+00:00" }, { "name": "laravelcollective/html", - "version": "v5.3.1", + "version": "v5.4.1", "source": { "type": "git", "url": "https://github.com/LaravelCollective/html.git", - "reference": "2f7f2e127c6fed47f269ea29ab5efeb8f65e9d35" + "reference": "7570f25d58a00fd6909c0563808590f9cdb14d47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/LaravelCollective/html/zipball/2f7f2e127c6fed47f269ea29ab5efeb8f65e9d35", - "reference": "2f7f2e127c6fed47f269ea29ab5efeb8f65e9d35", + "url": "https://api.github.com/repos/LaravelCollective/html/zipball/7570f25d58a00fd6909c0563808590f9cdb14d47", + "reference": "7570f25d58a00fd6909c0563808590f9cdb14d47", "shasum": "" }, "require": { - "illuminate/http": "5.3.*", - "illuminate/routing": "5.3.*", - "illuminate/session": "5.3.*", - "illuminate/support": "5.3.*", - "illuminate/view": "5.3.*", + "illuminate/http": "5.4.*", + "illuminate/routing": "5.4.*", + "illuminate/session": "5.4.*", + "illuminate/support": "5.4.*", + "illuminate/view": "5.4.*", "php": ">=5.6.4" }, "require-dev": { - "illuminate/database": "5.3.*", + "illuminate/database": "5.4.*", "mockery/mockery": "~0.9.4", "phpunit/phpunit": "~5.4" }, @@ -1033,7 +844,7 @@ ], "description": "HTML and Form Builders for the Laravel Framework", "homepage": "http://laravelcollective.com", - "time": "2016-12-13T14:23:36+00:00" + "time": "2017-01-26T19:27:05+00:00" }, { "name": "league/commonmark", @@ -1419,57 +1230,6 @@ ], "time": "2017-01-16T07:55:07+00:00" }, - { - "name": "nikic/php-parser", - "version": "v3.0.2", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "adf44419c0fc014a0f191db6f89d3e55d4211744" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/adf44419c0fc014a0f191db6f89d3e55d4211744", - "reference": "adf44419c0fc014a0f191db6f89d3e55d4211744", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "~4.0|~5.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "time": "2016-12-06T11:30:35+00:00" - }, { "name": "paragonie/random_compat", "version": "v2.0.4", @@ -1626,79 +1386,6 @@ ], "time": "2016-10-10T12:19:37+00:00" }, - { - "name": "psy/psysh", - "version": "v0.8.1", - "source": { - "type": "git", - "url": "https://github.com/bobthecow/psysh.git", - "reference": "701e8a1cc426ee170f1296f5d9f6b8a26ad25c4a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/701e8a1cc426ee170f1296f5d9f6b8a26ad25c4a", - "reference": "701e8a1cc426ee170f1296f5d9f6b8a26ad25c4a", - "shasum": "" - }, - "require": { - "dnoegel/php-xdg-base-dir": "0.1", - "jakub-onderka/php-console-highlighter": "0.3.*", - "nikic/php-parser": "~1.3|~2.0|~3.0", - "php": ">=5.3.9", - "symfony/console": "~2.3.10|^2.4.2|~3.0", - "symfony/var-dumper": "~2.7|~3.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "~1.11", - "hoa/console": "~3.16|~1.14", - "phpunit/phpunit": "~4.4|~5.0", - "symfony/finder": "~2.1|~3.0" - }, - "suggest": { - "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", - "ext-pdo-sqlite": "The doc command requires SQLite to work.", - "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well.", - "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history.", - "hoa/console": "A pure PHP readline implementation. You'll want this if your PHP install doesn't already support readline or libedit." - }, - "bin": [ - "bin/psysh" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-develop": "0.9.x-dev" - } - }, - "autoload": { - "files": [ - "src/Psy/functions.php" - ], - "psr-4": { - "Psy\\": "src/Psy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Justin Hileman", - "email": "justin@justinhileman.info", - "homepage": "http://justinhileman.com" - } - ], - "description": "An interactive shell for modern PHP.", - "homepage": "http://psysh.org", - "keywords": [ - "REPL", - "console", - "interactive", - "shell" - ], - "time": "2017-01-15T17:54:13+00:00" - }, { "name": "ramsey/uuid", "version": "3.5.2", @@ -1950,16 +1637,16 @@ }, { "name": "symfony/console", - "version": "v3.1.10", + "version": "v3.2.2", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "047f16485d68c083bd5d9b73ff16f9cb9c1a9f52" + "reference": "4f9e449e76996adf310498a8ca955c6deebe29dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/047f16485d68c083bd5d9b73ff16f9cb9c1a9f52", - "reference": "047f16485d68c083bd5d9b73ff16f9cb9c1a9f52", + "url": "https://api.github.com/repos/symfony/console/zipball/4f9e449e76996adf310498a8ca955c6deebe29dd", + "reference": "4f9e449e76996adf310498a8ca955c6deebe29dd", "shasum": "" }, "require": { @@ -1970,17 +1657,19 @@ "require-dev": { "psr/log": "~1.0", "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/filesystem": "~2.8|~3.0", "symfony/process": "~2.8|~3.0" }, "suggest": { "psr/log": "For using the console logger", "symfony/event-dispatcher": "", + "symfony/filesystem": "", "symfony/process": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -2007,20 +1696,73 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2017-01-08T20:43:43+00:00" + "time": "2017-01-08T20:47:33+00:00" }, { - "name": "symfony/debug", + "name": "symfony/css-selector", "version": "v3.1.10", "source": { "type": "git", - "url": "https://github.com/symfony/debug.git", - "reference": "c6661361626b3cf5cf2089df98b3b5006a197e85" + "url": "https://github.com/symfony/css-selector.git", + "reference": "722a87478a72d95dc2a3bcf41dc9c2d13fd4cb2d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/c6661361626b3cf5cf2089df98b3b5006a197e85", - "reference": "c6661361626b3cf5cf2089df98b3b5006a197e85", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/722a87478a72d95dc2a3bcf41dc9c2d13fd4cb2d", + "reference": "722a87478a72d95dc2a3bcf41dc9c2d13fd4cb2d", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony CssSelector Component", + "homepage": "https://symfony.com", + "time": "2017-01-02T20:31:54+00:00" + }, + { + "name": "symfony/debug", + "version": "v3.2.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "810ba5c1c5352a4ddb15d4719e8936751dff0b05" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/810ba5c1c5352a4ddb15d4719e8936751dff0b05", + "reference": "810ba5c1c5352a4ddb15d4719e8936751dff0b05", "shasum": "" }, "require": { @@ -2037,7 +1779,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -2064,7 +1806,7 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2017-01-28T00:04:57+00:00" + "time": "2017-01-02T20:32:22+00:00" }, { "name": "symfony/event-dispatcher", @@ -2128,16 +1870,16 @@ }, { "name": "symfony/finder", - "version": "v3.1.10", + "version": "v3.2.2", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "59687a255d1562f2c17b012418273862083d85f7" + "reference": "8c71141cae8e2957946b403cc71a67213c0380d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/59687a255d1562f2c17b012418273862083d85f7", - "reference": "59687a255d1562f2c17b012418273862083d85f7", + "url": "https://api.github.com/repos/symfony/finder/zipball/8c71141cae8e2957946b403cc71a67213c0380d6", + "reference": "8c71141cae8e2957946b403cc71a67213c0380d6", "shasum": "" }, "require": { @@ -2146,7 +1888,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -2173,20 +1915,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2017-01-02T20:31:54+00:00" + "time": "2017-01-02T20:32:22+00:00" }, { "name": "symfony/http-foundation", - "version": "v3.1.10", + "version": "v3.2.2", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "cef0ad49a2e90455cfc649522025b5a2929648c0" + "reference": "33eb76bf1d833c705433e5361a646c164696394b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/cef0ad49a2e90455cfc649522025b5a2929648c0", - "reference": "cef0ad49a2e90455cfc649522025b5a2929648c0", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/33eb76bf1d833c705433e5361a646c164696394b", + "reference": "33eb76bf1d833c705433e5361a646c164696394b", "shasum": "" }, "require": { @@ -2199,7 +1941,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -2226,20 +1968,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2017-01-08T20:43:43+00:00" + "time": "2017-01-08T20:47:33+00:00" }, { "name": "symfony/http-kernel", - "version": "v3.1.10", + "version": "v3.2.2", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "c830387dec1b48c100473d10a6a356c3c3ae2a13" + "reference": "8a898e340a89022246645b1288d295f49c9381e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/c830387dec1b48c100473d10a6a356c3c3ae2a13", - "reference": "c830387dec1b48c100473d10a6a356c3c3ae2a13", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/8a898e340a89022246645b1288d295f49c9381e4", + "reference": "8a898e340a89022246645b1288d295f49c9381e4", "shasum": "" }, "require": { @@ -2267,7 +2009,7 @@ "symfony/stopwatch": "~2.8|~3.0", "symfony/templating": "~2.8|~3.0", "symfony/translation": "~2.8|~3.0", - "symfony/var-dumper": "~2.8|~3.0" + "symfony/var-dumper": "~3.2" }, "suggest": { "symfony/browser-kit": "", @@ -2281,7 +2023,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -2308,7 +2050,7 @@ ], "description": "Symfony HttpKernel Component", "homepage": "https://symfony.com", - "time": "2017-01-28T02:53:17+00:00" + "time": "2017-01-12T21:36:33+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -2479,16 +2221,16 @@ }, { "name": "symfony/process", - "version": "v3.1.10", + "version": "v3.2.2", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "2605753c5f8c531623d24d002825ebb1d6a22248" + "reference": "350e810019fc52dd06ae844b6a6d382f8a0e8893" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/2605753c5f8c531623d24d002825ebb1d6a22248", - "reference": "2605753c5f8c531623d24d002825ebb1d6a22248", + "url": "https://api.github.com/repos/symfony/process/zipball/350e810019fc52dd06ae844b6a6d382f8a0e8893", + "reference": "350e810019fc52dd06ae844b6a6d382f8a0e8893", "shasum": "" }, "require": { @@ -2497,7 +2239,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -2524,20 +2266,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2017-01-21T17:13:55+00:00" + "time": "2017-01-02T20:32:22+00:00" }, { "name": "symfony/routing", - "version": "v3.1.10", + "version": "v3.2.2", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "f25581d4eb0a82962c291917f826166f0dcd8a9a" + "reference": "fda2c67d47ec801726ca888c95d701d31b27b444" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/f25581d4eb0a82962c291917f826166f0dcd8a9a", - "reference": "f25581d4eb0a82962c291917f826166f0dcd8a9a", + "url": "https://api.github.com/repos/symfony/routing/zipball/fda2c67d47ec801726ca888c95d701d31b27b444", + "reference": "fda2c67d47ec801726ca888c95d701d31b27b444", "shasum": "" }, "require": { @@ -2566,7 +2308,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -2599,20 +2341,20 @@ "uri", "url" ], - "time": "2017-01-28T00:04:57+00:00" + "time": "2017-01-02T20:32:22+00:00" }, { "name": "symfony/translation", - "version": "v3.1.10", + "version": "v3.2.2", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "d5a20fab5f63f44c233c69b3041c3cb1d4945e45" + "reference": "6520f3d4cce604d9dd1e86cac7af954984dd9bda" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/d5a20fab5f63f44c233c69b3041c3cb1d4945e45", - "reference": "d5a20fab5f63f44c233c69b3041c3cb1d4945e45", + "url": "https://api.github.com/repos/symfony/translation/zipball/6520f3d4cce604d9dd1e86cac7af954984dd9bda", + "reference": "6520f3d4cce604d9dd1e86cac7af954984dd9bda", "shasum": "" }, "require": { @@ -2636,7 +2378,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -2663,20 +2405,20 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2017-01-21T17:01:39+00:00" + "time": "2017-01-02T20:32:22+00:00" }, { "name": "symfony/var-dumper", - "version": "v3.1.10", + "version": "v3.2.2", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "16df11647e5b992d687cb4eeeb9a882d5f5c26b9" + "reference": "b54b23f9a19b465e76fdaac0f6732410467c83b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/16df11647e5b992d687cb4eeeb9a882d5f5c26b9", - "reference": "16df11647e5b992d687cb4eeeb9a882d5f5c26b9", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/b54b23f9a19b465e76fdaac0f6732410467c83b2", + "reference": "b54b23f9a19b465e76fdaac0f6732410467c83b2", "shasum": "" }, "require": { @@ -2692,7 +2434,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -2726,7 +2468,54 @@ "debug", "dump" ], - "time": "2017-01-24T13:02:38+00:00" + "time": "2017-01-03T08:53:57+00:00" + }, + { + "name": "tijsverkoyen/css-to-inline-styles", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", + "reference": "ab03919dfd85a74ae0372f8baf9f3c7d5c03b04b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/ab03919dfd85a74ae0372f8baf9f3c7d5c03b04b", + "reference": "ab03919dfd85a74ae0372f8baf9f3c7d5c03b04b", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7", + "symfony/css-selector": "^2.7|~3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|5.1.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "TijsVerkoyen\\CssToInlineStyles\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Tijs Verkoyen", + "email": "css_to_inline_styles@verkoyen.eu", + "role": "Developer" + } + ], + "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.", + "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", + "time": "2016-09-20T12:50:39+00:00" }, { "name": "twig/twig", @@ -4588,59 +4377,6 @@ "homepage": "https://symfony.com", "time": "2017-01-10T14:14:38+00:00" }, - { - "name": "symfony/css-selector", - "version": "v3.1.10", - "source": { - "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "722a87478a72d95dc2a3bcf41dc9c2d13fd4cb2d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/722a87478a72d95dc2a3bcf41dc9c2d13fd4cb2d", - "reference": "722a87478a72d95dc2a3bcf41dc9c2d13fd4cb2d", - "shasum": "" - }, - "require": { - "php": ">=5.5.9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\CssSelector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony CssSelector Component", - "homepage": "https://symfony.com", - "time": "2017-01-02T20:31:54+00:00" - }, { "name": "symfony/dom-crawler", "version": "v3.1.10", From 395aaad9c6b5668f8e2d0259ab6c9ac4e4a55ef9 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 16:21:01 +0100 Subject: [PATCH 607/709] New class name. --- app/Console/Kernel.php | 2 +- app/Http/Kernel.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 8ea0f88014..f7c14fc8f3 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -39,7 +39,7 @@ class Kernel extends ConsoleKernel */ protected $bootstrappers = [ - 'Illuminate\Foundation\Bootstrap\DetectEnvironment', + 'Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables', 'Illuminate\Foundation\Bootstrap\LoadConfiguration', 'FireflyIII\Bootstrap\ConfigureLogging', 'Illuminate\Foundation\Bootstrap\HandleExceptions', diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index e8dc229028..394ad35826 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -48,7 +48,7 @@ class Kernel extends HttpKernel */ protected $bootstrappers = [ - 'Illuminate\Foundation\Bootstrap\DetectEnvironment', + 'Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables', 'Illuminate\Foundation\Bootstrap\LoadConfiguration', 'FireflyIII\Bootstrap\ConfigureLogging', 'Illuminate\Foundation\Bootstrap\HandleExceptions', From b79dcd7f2398e31782069ae9f4595b24dd28986b Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 16:27:33 +0100 Subject: [PATCH 608/709] Disable ConfigureLogging class. --- app/Console/Kernel.php | 2 +- app/Http/Kernel.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index f7c14fc8f3..f4a1e32559 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -41,7 +41,7 @@ class Kernel extends ConsoleKernel = [ 'Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables', 'Illuminate\Foundation\Bootstrap\LoadConfiguration', - 'FireflyIII\Bootstrap\ConfigureLogging', + //'FireflyIII\Bootstrap\ConfigureLogging', 'Illuminate\Foundation\Bootstrap\HandleExceptions', 'Illuminate\Foundation\Bootstrap\RegisterFacades', 'Illuminate\Foundation\Bootstrap\SetRequestForConsole', diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 394ad35826..c552a552ee 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -50,7 +50,7 @@ class Kernel extends HttpKernel = [ 'Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables', 'Illuminate\Foundation\Bootstrap\LoadConfiguration', - 'FireflyIII\Bootstrap\ConfigureLogging', + //'FireflyIII\Bootstrap\ConfigureLogging', 'Illuminate\Foundation\Bootstrap\HandleExceptions', 'Illuminate\Foundation\Bootstrap\RegisterFacades', 'Illuminate\Foundation\Bootstrap\RegisterProviders', From 14971cf2498ae3d2b5d36e4a2a24ff9d25d5b131 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 16:30:35 +0100 Subject: [PATCH 609/709] Overrule logging. --- app/Providers/LogServiceProvider.php | 53 ++++++++++++++++++++++++++++ config/app.php | 10 +++--- 2 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 app/Providers/LogServiceProvider.php diff --git a/app/Providers/LogServiceProvider.php b/app/Providers/LogServiceProvider.php new file mode 100644 index 0000000000..86bf30481f --- /dev/null +++ b/app/Providers/LogServiceProvider.php @@ -0,0 +1,53 @@ +<?php +/** + * LogServiceProvider.php + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +declare(strict_types = 1); + +namespace FireflyIII\Providers; + +use Illuminate\Log\LogServiceProvider as LaravelLogServiceProvider; +use Illuminate\Log\Writer; + +/** + * Class LogServiceProvider + * + * @package FireflyIII\Providers + */ +class LogServiceProvider extends LaravelLogServiceProvider +{ + /** + * Configure the Monolog handlers for the application. + * + * @param \Illuminate\Log\Writer $log + * + * @return void + */ + protected function configureDailyHandler(Writer $log) + { + $log->useDailyFiles( + $this->app->storagePath() . '/logs/firefly-iii.log', $this->maxFiles(), + $this->logLevel() + ); + } + + /** + * Configure the Monolog handlers for the application. + * + * @param \Illuminate\Log\Writer $log + * + * @return void + */ + protected function configureSingleHandler(Writer $log) + { + $log->useFiles( + $this->app->storagePath() . '/logs/firefly-iii.log', + $this->logLevel() + ); + } +} \ No newline at end of file diff --git a/config/app.php b/config/app.php index 4933ad1c8c..125ec35093 100755 --- a/config/app.php +++ b/config/app.php @@ -26,8 +26,8 @@ return [ 'providers' => [ /* - * Laravel Framework Service Providers... - */ + * Laravel Framework Service Providers... + */ Illuminate\Auth\AuthServiceProvider::class, Illuminate\Broadcasting\BroadcastServiceProvider::class, Illuminate\Bus\BusServiceProvider::class, @@ -50,12 +50,11 @@ return [ Illuminate\Translation\TranslationServiceProvider::class, Illuminate\Validation\ValidationServiceProvider::class, Illuminate\View\ViewServiceProvider::class, - Collective\Html\HtmlServiceProvider::class, - /* * Application Service Providers... */ + FireflyIII\Providers\LogServiceProvider::class, FireflyIII\Providers\AppServiceProvider::class, FireflyIII\Providers\AuthServiceProvider::class, // FireflyIII\Providers\BroadcastServiceProvider::class, @@ -92,11 +91,12 @@ return [ ], 'aliases' => [ - 'App' => Illuminate\Support\Facades\App::class, 'Artisan' => Illuminate\Support\Facades\Artisan::class, 'Auth' => Illuminate\Support\Facades\Auth::class, 'Blade' => Illuminate\Support\Facades\Blade::class, + 'Broadcast' => Illuminate\Support\Facades\Broadcast::class, + 'Bus' => Illuminate\Support\Facades\Bus::class, 'Cache' => Illuminate\Support\Facades\Cache::class, 'Config' => Illuminate\Support\Facades\Config::class, 'Cookie' => Illuminate\Support\Facades\Cookie::class, From e80298f81554b67ca60cd1907113ec6378cb91f3 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 16:31:41 +0100 Subject: [PATCH 610/709] Include laravel bread crumbs and custom repository. --- composer.json | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 5c6784f143..4c1764d3c0 100755 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ "financials", "financial", "budgets", - "administration", + "administration", "tool", "tooling", "help", @@ -49,7 +49,7 @@ "php": ">=7.0.0", "ext-intl": "*", "laravel/framework": "5.4.*", - "davejamesmiller/laravel-breadcrumbs": "^3.0", + "davejamesmiller/laravel-breadcrumbs": "3.1", "watson/validating": "3.*", "doctrine/dbal": "^2.5", "league/commonmark": "0.15.*", @@ -106,5 +106,11 @@ }, "config": { "preferred-install": "dist" - } + }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/firefly-iii/laravel-breadcrumbs.git" + } + ] } From 4bc3af717641173cc28d2a3c6551688d9b1d7e6f Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 16:35:31 +0100 Subject: [PATCH 611/709] Updated composer.lock file for change to bread crumbs. --- composer.lock | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/composer.lock b/composer.lock index 0d10a81094..46a59ecc21 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "3baa1b043873285dc937f9dad0083506", + "content-hash": "485d633a8ac40b245a597a33fae7abf4", "packages": [ { "name": "bacon/bacon-qr-code", @@ -104,16 +104,16 @@ }, { "name": "davejamesmiller/laravel-breadcrumbs", - "version": "3.0.1", + "version": "3.1", "source": { "type": "git", - "url": "https://github.com/davejamesmiller/laravel-breadcrumbs.git", - "reference": "460bf79e83ff9e3db1e3f1c40169d8893893f8ff" + "url": "https://github.com/firefly-iii/laravel-breadcrumbs.git", + "reference": "afebafc321432188b10dafc7b4c072501687f3d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/davejamesmiller/laravel-breadcrumbs/zipball/460bf79e83ff9e3db1e3f1c40169d8893893f8ff", - "reference": "460bf79e83ff9e3db1e3f1c40169d8893893f8ff", + "url": "https://api.github.com/repos/firefly-iii/laravel-breadcrumbs/zipball/afebafc321432188b10dafc7b4c072501687f3d0", + "reference": "afebafc321432188b10dafc7b4c072501687f3d0", "shasum": "" }, "require": { @@ -133,7 +133,6 @@ "DaveJamesMiller\\Breadcrumbs\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT License" ], @@ -141,15 +140,18 @@ { "name": "Dave James Miller", "email": "dave@davejamesmiller.com", - "homepage": "http://davejamesmiller.com/" + "homepage": "https://davejamesmiller.com/" } ], "description": "A simple Laravel-style way to create breadcrumbs in Laravel 4+.", - "homepage": "http://laravel-breadcrumbs.davejamesmiller.com", + "homepage": "https://laravel-breadcrumbs.readthedocs.io/", "keywords": [ "laravel" ], - "time": "2016-08-28T16:57:03+00:00" + "support": { + "source": "https://github.com/firefly-iii/laravel-breadcrumbs/tree/3.1" + }, + "time": "2017-01-30T07:52:50+00:00" }, { "name": "doctrine/annotations", From 4cad2eb0c4bde7c6ff24ced0393e8631f6ce0ea3 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 16:35:41 +0100 Subject: [PATCH 612/709] Upgraded validator --- app/Validation/FireflyValidator.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index 61590816b4..f38a164993 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -27,9 +27,9 @@ use FireflyIII\Rules\Triggers\TriggerInterface; use FireflyIII\User; use Google2FA; use Illuminate\Contracts\Encryption\DecryptException; +use Illuminate\Contracts\Translation\Translator; use Illuminate\Validation\Validator; use Session; -use Symfony\Component\Translation\TranslatorInterface; /** * Class FireflyValidator @@ -40,14 +40,14 @@ class FireflyValidator extends Validator { /** - * @param TranslatorInterface $translator - * @param array $data - * @param array $rules - * @param array $messages - * @param array $customAttributes + * @param Translator $translator + * @param array $data + * @param array $rules + * @param array $messages + * @param array $customAttributes * */ - public function __construct(TranslatorInterface $translator, array $data, array $rules, array $messages = [], array $customAttributes = []) + public function __construct(Translator $translator, array $data, array $rules, array $messages = [], array $customAttributes = []) { parent::__construct($translator, $data, $rules, $messages, $customAttributes); } @@ -158,6 +158,7 @@ class FireflyValidator extends Validator public function validateMore($attribute, $value, $parameters): bool { $compare = $parameters[0] ?? '0'; + return bccomp($value, $compare) > 0; } From 84e380e4d06f8056f09cc549dcbff125d5565153 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 16:40:49 +0100 Subject: [PATCH 613/709] Give all repositories a new setUser function. --- app/Repositories/Account/AccountRepositoryInterface.php | 6 ++++++ app/Repositories/Account/AccountTaskerInterface.php | 5 +++++ .../Attachment/AttachmentRepositoryInterface.php | 6 ++++++ app/Repositories/Bill/BillRepositoryInterface.php | 5 +++++ app/Repositories/Budget/BudgetRepositoryInterface.php | 5 +++++ app/Repositories/Category/CategoryRepositoryInterface.php | 6 ++++++ app/Repositories/Currency/CurrencyRepositoryInterface.php | 6 ++++++ app/Repositories/ExportJob/ExportJobRepositoryInterface.php | 6 ++++++ app/Repositories/ImportJob/ImportJobRepositoryInterface.php | 6 ++++++ app/Repositories/Journal/JournalRepositoryInterface.php | 6 +++++- app/Repositories/Journal/JournalTaskerInterface.php | 5 +++++ app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php | 5 +++++ app/Repositories/Rule/RuleRepositoryInterface.php | 6 +++++- app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php | 6 +++++- app/Repositories/Tag/TagRepositoryInterface.php | 6 ++++++ app/Repositories/User/UserRepositoryInterface.php | 1 + 16 files changed, 83 insertions(+), 3 deletions(-) diff --git a/app/Repositories/Account/AccountRepositoryInterface.php b/app/Repositories/Account/AccountRepositoryInterface.php index 428e48144d..d19ba94d52 100644 --- a/app/Repositories/Account/AccountRepositoryInterface.php +++ b/app/Repositories/Account/AccountRepositoryInterface.php @@ -16,6 +16,7 @@ namespace FireflyIII\Repositories\Account; use Carbon\Carbon; use FireflyIII\Models\Account; use FireflyIII\Models\TransactionJournal; +use FireflyIII\User; use Illuminate\Support\Collection; /** @@ -26,6 +27,11 @@ use Illuminate\Support\Collection; interface AccountRepositoryInterface { + /** + * @param User $user + */ + public function setUser(User $user); + /** * Moved here from account CRUD. * diff --git a/app/Repositories/Account/AccountTaskerInterface.php b/app/Repositories/Account/AccountTaskerInterface.php index 57fbebaa79..81ef432b9e 100644 --- a/app/Repositories/Account/AccountTaskerInterface.php +++ b/app/Repositories/Account/AccountTaskerInterface.php @@ -14,6 +14,7 @@ declare(strict_types = 1); namespace FireflyIII\Repositories\Account; use Carbon\Carbon; +use FireflyIII\User; use Illuminate\Support\Collection; /** @@ -23,6 +24,10 @@ use Illuminate\Support\Collection; */ interface AccountTaskerInterface { + /** + * @param User $user + */ + public function setUser(User $user); /** * @param Collection $accounts diff --git a/app/Repositories/Attachment/AttachmentRepositoryInterface.php b/app/Repositories/Attachment/AttachmentRepositoryInterface.php index 9e12092c70..25dc72ff38 100644 --- a/app/Repositories/Attachment/AttachmentRepositoryInterface.php +++ b/app/Repositories/Attachment/AttachmentRepositoryInterface.php @@ -15,6 +15,7 @@ namespace FireflyIII\Repositories\Attachment; use Carbon\Carbon; use FireflyIII\Models\Attachment; +use FireflyIII\User; use Illuminate\Support\Collection; /** @@ -25,6 +26,11 @@ use Illuminate\Support\Collection; interface AttachmentRepositoryInterface { + /** + * @param User $user + */ + public function setUser(User $user); + /** * @param Attachment $attachment * diff --git a/app/Repositories/Bill/BillRepositoryInterface.php b/app/Repositories/Bill/BillRepositoryInterface.php index c2c6cd223d..af6dd36dab 100644 --- a/app/Repositories/Bill/BillRepositoryInterface.php +++ b/app/Repositories/Bill/BillRepositoryInterface.php @@ -16,6 +16,7 @@ namespace FireflyIII\Repositories\Bill; use Carbon\Carbon; use FireflyIII\Models\Bill; use FireflyIII\Models\TransactionJournal; +use FireflyIII\User; use Illuminate\Support\Collection; /** @@ -25,6 +26,10 @@ use Illuminate\Support\Collection; */ interface BillRepositoryInterface { + /** + * @param User $user + */ + public function setUser(User $user); /** * @param Bill $bill diff --git a/app/Repositories/Budget/BudgetRepositoryInterface.php b/app/Repositories/Budget/BudgetRepositoryInterface.php index ee9eba1641..3eefa19b01 100644 --- a/app/Repositories/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetRepositoryInterface.php @@ -17,6 +17,7 @@ use Carbon\Carbon; use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\TransactionCurrency; +use FireflyIII\User; use Illuminate\Support\Collection; /** @@ -26,6 +27,10 @@ use Illuminate\Support\Collection; */ interface BudgetRepositoryInterface { + /** + * @param User $user + */ + public function setUser(User $user); /** * @return bool diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index 1194fbbf74..80b42d6852 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -15,6 +15,7 @@ namespace FireflyIII\Repositories\Category; use Carbon\Carbon; use FireflyIII\Models\Category; +use FireflyIII\User; use Illuminate\Support\Collection; /** @@ -24,6 +25,11 @@ use Illuminate\Support\Collection; */ interface CategoryRepositoryInterface { + /** + * @param User $user + */ + public function setUser(User $user); + /** * @param Category $category * diff --git a/app/Repositories/Currency/CurrencyRepositoryInterface.php b/app/Repositories/Currency/CurrencyRepositoryInterface.php index 4ccebe40b0..9c63c1fbfc 100644 --- a/app/Repositories/Currency/CurrencyRepositoryInterface.php +++ b/app/Repositories/Currency/CurrencyRepositoryInterface.php @@ -16,6 +16,7 @@ namespace FireflyIII\Repositories\Currency; use FireflyIII\Models\Preference; use FireflyIII\Models\TransactionCurrency; +use FireflyIII\User; use Illuminate\Support\Collection; /** @@ -25,6 +26,11 @@ use Illuminate\Support\Collection; */ interface CurrencyRepositoryInterface { + /** + * @param User $user + */ + public function setUser(User $user); + /** * @param TransactionCurrency $currency * diff --git a/app/Repositories/ExportJob/ExportJobRepositoryInterface.php b/app/Repositories/ExportJob/ExportJobRepositoryInterface.php index 6f3c7a30b1..484e8496b5 100644 --- a/app/Repositories/ExportJob/ExportJobRepositoryInterface.php +++ b/app/Repositories/ExportJob/ExportJobRepositoryInterface.php @@ -14,6 +14,7 @@ declare(strict_types = 1); namespace FireflyIII\Repositories\ExportJob; use FireflyIII\Models\ExportJob; +use FireflyIII\User; /** * Interface ExportJobRepositoryInterface @@ -22,6 +23,11 @@ use FireflyIII\Models\ExportJob; */ interface ExportJobRepositoryInterface { + /** + * @param User $user + */ + public function setUser(User $user); + /** * @param ExportJob $job * @param string $status diff --git a/app/Repositories/ImportJob/ImportJobRepositoryInterface.php b/app/Repositories/ImportJob/ImportJobRepositoryInterface.php index a17d9ca62c..b58468f1af 100644 --- a/app/Repositories/ImportJob/ImportJobRepositoryInterface.php +++ b/app/Repositories/ImportJob/ImportJobRepositoryInterface.php @@ -14,6 +14,7 @@ declare(strict_types = 1); namespace FireflyIII\Repositories\ImportJob; use FireflyIII\Models\ImportJob; +use FireflyIII\User; /** * Interface ImportJobRepositoryInterface @@ -22,6 +23,11 @@ use FireflyIII\Models\ImportJob; */ interface ImportJobRepositoryInterface { + /** + * @param User $user + */ + public function setUser(User $user); + /** * @param string $fileType * diff --git a/app/Repositories/Journal/JournalRepositoryInterface.php b/app/Repositories/Journal/JournalRepositoryInterface.php index 0b62a5729a..446d21c184 100644 --- a/app/Repositories/Journal/JournalRepositoryInterface.php +++ b/app/Repositories/Journal/JournalRepositoryInterface.php @@ -16,6 +16,7 @@ namespace FireflyIII\Repositories\Journal; use FireflyIII\Models\Account; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; +use FireflyIII\User; use Illuminate\Support\Collection; use Illuminate\Support\MessageBag; @@ -26,7 +27,10 @@ use Illuminate\Support\MessageBag; */ interface JournalRepositoryInterface { - + /** + * @param User $user + */ + public function setUser(User $user); /** * @param TransactionJournal $journal * @param TransactionType $type diff --git a/app/Repositories/Journal/JournalTaskerInterface.php b/app/Repositories/Journal/JournalTaskerInterface.php index 058bc63733..e5c007c1e7 100644 --- a/app/Repositories/Journal/JournalTaskerInterface.php +++ b/app/Repositories/Journal/JournalTaskerInterface.php @@ -15,6 +15,7 @@ namespace FireflyIII\Repositories\Journal; use FireflyIII\Models\TransactionJournal; +use FireflyIII\User; use Illuminate\Support\Collection; /** @@ -24,6 +25,10 @@ use Illuminate\Support\Collection; */ interface JournalTaskerInterface { + /** + * @param User $user + */ + public function setUser(User $user); /** * @param TransactionJournal $journal diff --git a/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php b/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php index aae01702d7..a2da326d86 100644 --- a/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php +++ b/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php @@ -15,6 +15,7 @@ namespace FireflyIII\Repositories\PiggyBank; use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBankEvent; +use FireflyIII\User; use Illuminate\Support\Collection; /** @@ -98,6 +99,10 @@ interface PiggyBankRepositoryInterface */ public function setOrder(int $piggyBankId, int $order): bool; + /** + * @param User $user + */ + public function setUser(User $user); /** * Store new piggy bank. diff --git a/app/Repositories/Rule/RuleRepositoryInterface.php b/app/Repositories/Rule/RuleRepositoryInterface.php index 518417d94a..887bb20d79 100644 --- a/app/Repositories/Rule/RuleRepositoryInterface.php +++ b/app/Repositories/Rule/RuleRepositoryInterface.php @@ -17,6 +17,7 @@ use FireflyIII\Models\Rule; use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleGroup; use FireflyIII\Models\RuleTrigger; +use FireflyIII\User; /** * Interface RuleRepositoryInterface @@ -25,7 +26,10 @@ use FireflyIII\Models\RuleTrigger; */ interface RuleRepositoryInterface { - + /** + * @param User $user + */ + public function setUser(User $user); /** * @return int */ diff --git a/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php b/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php index 985a121b92..c365fca704 100644 --- a/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php +++ b/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php @@ -26,7 +26,6 @@ use Illuminate\Support\Collection; interface RuleGroupRepositoryInterface { - /** * * @@ -92,6 +91,11 @@ interface RuleGroupRepositoryInterface */ public function resetRulesInGroupOrder(RuleGroup $ruleGroup): bool; + /** + * @param User $user + */ + public function setUser(User $user); + /** * @param array $data * diff --git a/app/Repositories/Tag/TagRepositoryInterface.php b/app/Repositories/Tag/TagRepositoryInterface.php index 64bf47c44e..c813a238da 100644 --- a/app/Repositories/Tag/TagRepositoryInterface.php +++ b/app/Repositories/Tag/TagRepositoryInterface.php @@ -16,6 +16,7 @@ namespace FireflyIII\Repositories\Tag; use Carbon\Carbon; use FireflyIII\Models\Tag; use FireflyIII\Models\TransactionJournal; +use FireflyIII\User; use Illuminate\Support\Collection; @@ -26,6 +27,11 @@ use Illuminate\Support\Collection; */ interface TagRepositoryInterface { + /** + * @param User $user + */ + public function setUser(User $user); + /** * This method will connect a journal with a tag. * diff --git a/app/Repositories/User/UserRepositoryInterface.php b/app/Repositories/User/UserRepositoryInterface.php index 93e34a7a14..9e2bb096b3 100644 --- a/app/Repositories/User/UserRepositoryInterface.php +++ b/app/Repositories/User/UserRepositoryInterface.php @@ -24,6 +24,7 @@ use Illuminate\Support\Collection; */ interface UserRepositoryInterface { + /** * Returns a collection of all users. * From 311c1a3c844f2d3ea9bb06d0c74a217fbd015f54 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 16:42:58 +0100 Subject: [PATCH 614/709] Implement all setUser methods. --- app/Repositories/Account/AccountRepository.php | 8 ++++++++ .../Account/AccountRepositoryInterface.php | 10 +++++----- app/Repositories/Account/AccountTasker.php | 12 ++++++++++-- app/Repositories/Account/AccountTaskerInterface.php | 10 +++++----- app/Repositories/Attachment/AttachmentRepository.php | 8 +++++++- app/Repositories/Bill/BillRepository.php | 8 +++++++- app/Repositories/Budget/BudgetRepository.php | 8 +++++++- app/Repositories/Category/CategoryRepository.php | 8 +++++++- app/Repositories/Currency/CurrencyRepository.php | 8 +++++++- app/Repositories/ExportJob/ExportJobRepository.php | 8 +++++++- app/Repositories/ImportJob/ImportJobRepository.php | 8 +++++++- app/Repositories/Journal/JournalRepository.php | 8 +++++++- app/Repositories/Journal/JournalTasker.php | 8 ++++++++ app/Repositories/PiggyBank/PiggyBankRepository.php | 8 +++++++- app/Repositories/Rule/RuleRepository.php | 8 +++++++- app/Repositories/RuleGroup/RuleGroupRepository.php | 8 +++++++- app/Repositories/Tag/TagRepository.php | 8 +++++++- 17 files changed, 120 insertions(+), 24 deletions(-) diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index d740291f68..631157d62c 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -323,6 +323,14 @@ class AccountRepository implements AccountRepositoryInterface return $journal->date; } + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } + /** * @param array $data * diff --git a/app/Repositories/Account/AccountRepositoryInterface.php b/app/Repositories/Account/AccountRepositoryInterface.php index d19ba94d52..b0ab9b738b 100644 --- a/app/Repositories/Account/AccountRepositoryInterface.php +++ b/app/Repositories/Account/AccountRepositoryInterface.php @@ -27,11 +27,6 @@ use Illuminate\Support\Collection; interface AccountRepositoryInterface { - /** - * @param User $user - */ - public function setUser(User $user); - /** * Moved here from account CRUD. * @@ -130,6 +125,11 @@ interface AccountRepositoryInterface */ public function oldestJournalDate(Account $account): Carbon; + /** + * @param User $user + */ + public function setUser(User $user); + /** * @param array $data * diff --git a/app/Repositories/Account/AccountTasker.php b/app/Repositories/Account/AccountTasker.php index 126d084e8d..874ceb7a30 100644 --- a/app/Repositories/Account/AccountTasker.php +++ b/app/Repositories/Account/AccountTasker.php @@ -108,8 +108,8 @@ class AccountTasker implements AccountTaskerInterface */ public function getAccountReport(Collection $accounts, Carbon $start, Carbon $end): array { - $ids = $accounts->pluck('id')->toArray(); - $yesterday = clone $start; + $ids = $accounts->pluck('id')->toArray(); + $yesterday = clone $start; $yesterday->subDay(); $startSet = Steam::balancesById($ids, $yesterday); $endSet = Steam::balancesById($ids, $end); @@ -155,6 +155,14 @@ class AccountTasker implements AccountTaskerInterface return $return; } + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } + /** * Will return how much money has been going out (ie. spent) by the given account(s). * Alternatively, will return how much money has been coming in (ie. earned) by the given accounts. diff --git a/app/Repositories/Account/AccountTaskerInterface.php b/app/Repositories/Account/AccountTaskerInterface.php index 81ef432b9e..4ea5be74ef 100644 --- a/app/Repositories/Account/AccountTaskerInterface.php +++ b/app/Repositories/Account/AccountTaskerInterface.php @@ -24,11 +24,6 @@ use Illuminate\Support\Collection; */ interface AccountTaskerInterface { - /** - * @param User $user - */ - public function setUser(User $user); - /** * @param Collection $accounts * @param Collection $excluded @@ -62,4 +57,9 @@ interface AccountTaskerInterface */ public function getAccountReport(Collection $accounts, Carbon $start, Carbon $end): array; + /** + * @param User $user + */ + public function setUser(User $user); + } diff --git a/app/Repositories/Attachment/AttachmentRepository.php b/app/Repositories/Attachment/AttachmentRepository.php index 1bc9401624..4ee2b458c6 100644 --- a/app/Repositories/Attachment/AttachmentRepository.php +++ b/app/Repositories/Attachment/AttachmentRepository.php @@ -30,7 +30,13 @@ class AttachmentRepository implements AttachmentRepositoryInterface { /** @var User */ private $user; - + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } /** * AttachmentRepository constructor. * diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index d3bfa01572..4955145877 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -36,7 +36,13 @@ class BillRepository implements BillRepositoryInterface /** @var User */ private $user; - + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } /** * BillRepository constructor. * diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 7c8d2f6391..8d6cde13a8 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -47,7 +47,13 @@ class BudgetRepository implements BudgetRepositoryInterface { $this->user = $user; } - + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } /** * @return bool */ diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 32c2eb2135..7d88329dbe 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -43,7 +43,13 @@ class CategoryRepository implements CategoryRepositoryInterface { $this->user = $user; } - + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } /** * @param Category $category * diff --git a/app/Repositories/Currency/CurrencyRepository.php b/app/Repositories/Currency/CurrencyRepository.php index d2eaed1c30..d299bbf4f2 100644 --- a/app/Repositories/Currency/CurrencyRepository.php +++ b/app/Repositories/Currency/CurrencyRepository.php @@ -39,7 +39,13 @@ class CurrencyRepository implements CurrencyRepositoryInterface { $this->user = $user; } - + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } /** * @param TransactionCurrency $currency * diff --git a/app/Repositories/ExportJob/ExportJobRepository.php b/app/Repositories/ExportJob/ExportJobRepository.php index b83818284a..afbdab9248 100644 --- a/app/Repositories/ExportJob/ExportJobRepository.php +++ b/app/Repositories/ExportJob/ExportJobRepository.php @@ -38,7 +38,13 @@ class ExportJobRepository implements ExportJobRepositoryInterface { $this->user = $user; } - + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } /** * @param ExportJob $job * @param string $status diff --git a/app/Repositories/ImportJob/ImportJobRepository.php b/app/Repositories/ImportJob/ImportJobRepository.php index d7f4ac30cf..88d54f4a81 100644 --- a/app/Repositories/ImportJob/ImportJobRepository.php +++ b/app/Repositories/ImportJob/ImportJobRepository.php @@ -37,7 +37,13 @@ class ImportJobRepository implements ImportJobRepositoryInterface { $this->user = $user; } - + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } /** * @param string $fileType * diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php index 9ee336cddf..aa28551d4c 100644 --- a/app/Repositories/Journal/JournalRepository.php +++ b/app/Repositories/Journal/JournalRepository.php @@ -52,7 +52,13 @@ class JournalRepository implements JournalRepositoryInterface { $this->user = $user; } - + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } /** * @param TransactionJournal $journal * @param TransactionType $type diff --git a/app/Repositories/Journal/JournalTasker.php b/app/Repositories/Journal/JournalTasker.php index eb77c77e6f..5f9f610ca1 100644 --- a/app/Repositories/Journal/JournalTasker.php +++ b/app/Repositories/Journal/JournalTasker.php @@ -152,6 +152,14 @@ class JournalTasker implements JournalTaskerInterface return $transactions; } + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } + /** * Collect the balance of an account before the given transaction has hit. This is tricky, because * the balance does not depend on the transaction itself but the journal it's part of. And of course diff --git a/app/Repositories/PiggyBank/PiggyBankRepository.php b/app/Repositories/PiggyBank/PiggyBankRepository.php index 5a9f07704c..fad4b0f4fa 100644 --- a/app/Repositories/PiggyBank/PiggyBankRepository.php +++ b/app/Repositories/PiggyBank/PiggyBankRepository.php @@ -41,7 +41,13 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface { $this->user = $user; } - + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } /** * @param PiggyBank $piggyBank * @param string $amount diff --git a/app/Repositories/Rule/RuleRepository.php b/app/Repositories/Rule/RuleRepository.php index 4b7ce00433..05849acd5d 100644 --- a/app/Repositories/Rule/RuleRepository.php +++ b/app/Repositories/Rule/RuleRepository.php @@ -39,7 +39,13 @@ class RuleRepository implements RuleRepositoryInterface { $this->user = $user; } - + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } /** * @return int */ diff --git a/app/Repositories/RuleGroup/RuleGroupRepository.php b/app/Repositories/RuleGroup/RuleGroupRepository.php index b294e19937..7a6afa6f0e 100644 --- a/app/Repositories/RuleGroup/RuleGroupRepository.php +++ b/app/Repositories/RuleGroup/RuleGroupRepository.php @@ -39,7 +39,13 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface { $this->user = $user; } - + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } /** * @return int */ diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index 8145be1151..a344da412a 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -43,7 +43,13 @@ class TagRepository implements TagRepositoryInterface { $this->user = $user; } - + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } /** * * @param TransactionJournal $journal From 1e947870a613dfc2903eebbbcca94c04a51a40a0 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 16:46:30 +0100 Subject: [PATCH 615/709] Remove all constructors. --- .../Account/AccountRepository.php | 10 ------- app/Repositories/Account/AccountTasker.php | 10 ------- .../Attachment/AttachmentRepository.php | 26 +++++++------------ .../AttachmentRepositoryInterface.php | 10 +++---- app/Repositories/Bill/BillRepository.php | 24 ++++++----------- .../Bill/BillRepositoryInterface.php | 10 +++---- app/Repositories/Budget/BudgetRepository.php | 24 ++++++----------- .../Budget/BudgetRepositoryInterface.php | 10 +++---- .../Category/CategoryRepository.php | 24 ++++++----------- .../Category/CategoryRepositoryInterface.php | 10 +++---- .../Currency/CurrencyRepository.php | 10 +------ .../Currency/CurrencyRepositoryInterface.php | 10 +++---- .../ExportJob/ExportJobRepository.php | 24 ++++++----------- .../ExportJobRepositoryInterface.php | 10 +++---- .../ImportJob/ImportJobRepository.php | 24 ++++++----------- .../ImportJobRepositoryInterface.php | 10 +++---- .../Journal/JournalRepository.php | 10 +------ .../Journal/JournalRepositoryInterface.php | 18 ++++++------- app/Repositories/Journal/JournalTasker.php | 9 ------- .../Journal/JournalTaskerInterface.php | 10 +++---- .../PiggyBank/PiggyBankRepository.php | 24 ++++++----------- app/Repositories/Rule/RuleRepository.php | 24 ++++++----------- .../Rule/RuleRepositoryInterface.php | 9 ++++--- .../RuleGroup/RuleGroupRepository.php | 10 +------ app/Repositories/Tag/TagRepository.php | 10 +------ .../Tag/TagRepositoryInterface.php | 10 +++---- 26 files changed, 128 insertions(+), 252 deletions(-) diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index 631157d62c..26ba26b6ea 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -43,16 +43,6 @@ class AccountRepository implements AccountRepositoryInterface /** @var array */ private $validFields = ['accountRole', 'ccMonthlyPaymentDate', 'ccType', 'accountNumber', 'currency_id', 'BIC']; - /** - * AttachmentRepository constructor. - * - * @param User $user - */ - public function __construct(User $user) - { - $this->user = $user; - } - /** * Moved here from account CRUD * diff --git a/app/Repositories/Account/AccountTasker.php b/app/Repositories/Account/AccountTasker.php index 874ceb7a30..d43902e094 100644 --- a/app/Repositories/Account/AccountTasker.php +++ b/app/Repositories/Account/AccountTasker.php @@ -31,16 +31,6 @@ class AccountTasker implements AccountTaskerInterface /** @var User */ private $user; - /** - * AttachmentRepository constructor. - * - * @param User $user - */ - public function __construct(User $user) - { - $this->user = $user; - } - /** * @see self::amountInPeriod * diff --git a/app/Repositories/Attachment/AttachmentRepository.php b/app/Repositories/Attachment/AttachmentRepository.php index 4ee2b458c6..79a481e4ab 100644 --- a/app/Repositories/Attachment/AttachmentRepository.php +++ b/app/Repositories/Attachment/AttachmentRepository.php @@ -14,12 +14,12 @@ declare(strict_types = 1); namespace FireflyIII\Repositories\Attachment; use Carbon\Carbon; +use Crypt; use FireflyIII\Helpers\Attachments\AttachmentHelperInterface; use FireflyIII\Models\Attachment; use FireflyIII\User; use Illuminate\Support\Collection; use Storage; -use Crypt; /** * Class AttachmentRepository @@ -30,22 +30,6 @@ class AttachmentRepository implements AttachmentRepositoryInterface { /** @var User */ private $user; - /** - * @param User $user - */ - public function setUser(User $user) - { - $this->user = $user; - } - /** - * AttachmentRepository constructor. - * - * @param User $user - */ - public function __construct(User $user) - { - $this->user = $user; - } /** * @param Attachment $attachment @@ -123,6 +107,14 @@ class AttachmentRepository implements AttachmentRepositoryInterface return ''; } + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } + /** * @param Attachment $attachment * @param array $data diff --git a/app/Repositories/Attachment/AttachmentRepositoryInterface.php b/app/Repositories/Attachment/AttachmentRepositoryInterface.php index 25dc72ff38..cc7cb887f8 100644 --- a/app/Repositories/Attachment/AttachmentRepositoryInterface.php +++ b/app/Repositories/Attachment/AttachmentRepositoryInterface.php @@ -26,11 +26,6 @@ use Illuminate\Support\Collection; interface AttachmentRepositoryInterface { - /** - * @param User $user - */ - public function setUser(User $user); - /** * @param Attachment $attachment * @@ -65,6 +60,11 @@ interface AttachmentRepositoryInterface */ public function getContent(Attachment $attachment): string; + /** + * @param User $user + */ + public function setUser(User $user); + /** * @param Attachment $attachment * @param array $attachmentData diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index 4955145877..545a29fb96 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -36,22 +36,6 @@ class BillRepository implements BillRepositoryInterface /** @var User */ private $user; - /** - * @param User $user - */ - public function setUser(User $user) - { - $this->user = $user; - } - /** - * BillRepository constructor. - * - * @param User $user - */ - public function __construct(User $user) - { - $this->user = $user; - } /** * @param Bill $bill @@ -526,6 +510,14 @@ class BillRepository implements BillRepositoryInterface } + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } + /** * @param array $data * diff --git a/app/Repositories/Bill/BillRepositoryInterface.php b/app/Repositories/Bill/BillRepositoryInterface.php index af6dd36dab..2e07d12d93 100644 --- a/app/Repositories/Bill/BillRepositoryInterface.php +++ b/app/Repositories/Bill/BillRepositoryInterface.php @@ -26,11 +26,6 @@ use Illuminate\Support\Collection; */ interface BillRepositoryInterface { - /** - * @param User $user - */ - public function setUser(User $user); - /** * @param Bill $bill * @@ -164,6 +159,11 @@ interface BillRepositoryInterface */ public function scan(Bill $bill, TransactionJournal $journal): bool; + /** + * @param User $user + */ + public function setUser(User $user); + /** * @param array $data * diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 8d6cde13a8..8f0588dc1a 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -38,22 +38,6 @@ class BudgetRepository implements BudgetRepositoryInterface /** @var User */ private $user; - /** - * BudgetRepository constructor. - * - * @param User $user - */ - public function __construct(User $user) - { - $this->user = $user; - } - /** - * @param User $user - */ - public function setUser(User $user) - { - $this->user = $user; - } /** * @return bool */ @@ -439,6 +423,14 @@ class BudgetRepository implements BudgetRepositoryInterface return true; } + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } + /** * @param Collection $budgets * @param Collection $accounts diff --git a/app/Repositories/Budget/BudgetRepositoryInterface.php b/app/Repositories/Budget/BudgetRepositoryInterface.php index 3eefa19b01..906a4827ae 100644 --- a/app/Repositories/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetRepositoryInterface.php @@ -27,11 +27,6 @@ use Illuminate\Support\Collection; */ interface BudgetRepositoryInterface { - /** - * @param User $user - */ - public function setUser(User $user); - /** * @return bool */ @@ -154,6 +149,11 @@ interface BudgetRepositoryInterface */ public function setAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end, string $amount): bool; + /** + * @param User $user + */ + public function setUser(User $user); + /** * @param Collection $budgets * @param Collection $accounts diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 7d88329dbe..fb366c0812 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -34,22 +34,6 @@ class CategoryRepository implements CategoryRepositoryInterface /** @var User */ private $user; - /** - * CategoryRepository constructor. - * - * @param User $user - */ - public function __construct(User $user) - { - $this->user = $user; - } - /** - * @param User $user - */ - public function setUser(User $user) - { - $this->user = $user; - } /** * @param Category $category * @@ -390,6 +374,14 @@ class CategoryRepository implements CategoryRepositoryInterface return $result; } + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } + /** * @param Collection $categories * @param Collection $accounts diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index 80b42d6852..9e89a78f50 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -25,11 +25,6 @@ use Illuminate\Support\Collection; */ interface CategoryRepositoryInterface { - /** - * @param User $user - */ - public function setUser(User $user); - /** * @param Category $category * @@ -127,6 +122,11 @@ interface CategoryRepositoryInterface */ public function periodIncomeNoCategory(Collection $accounts, Carbon $start, Carbon $end): array; + /** + * @param User $user + */ + public function setUser(User $user); + /** * @param Collection $categories * @param Collection $accounts diff --git a/app/Repositories/Currency/CurrencyRepository.php b/app/Repositories/Currency/CurrencyRepository.php index d299bbf4f2..bb1444d73c 100644 --- a/app/Repositories/Currency/CurrencyRepository.php +++ b/app/Repositories/Currency/CurrencyRepository.php @@ -30,15 +30,6 @@ class CurrencyRepository implements CurrencyRepositoryInterface /** @var User */ private $user; - /** - * CategoryRepository constructor. - * - * @param User $user - */ - public function __construct(User $user) - { - $this->user = $user; - } /** * @param User $user */ @@ -46,6 +37,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface { $this->user = $user; } + /** * @param TransactionCurrency $currency * diff --git a/app/Repositories/Currency/CurrencyRepositoryInterface.php b/app/Repositories/Currency/CurrencyRepositoryInterface.php index 9c63c1fbfc..63eb9bf0b5 100644 --- a/app/Repositories/Currency/CurrencyRepositoryInterface.php +++ b/app/Repositories/Currency/CurrencyRepositoryInterface.php @@ -26,11 +26,6 @@ use Illuminate\Support\Collection; */ interface CurrencyRepositoryInterface { - /** - * @param User $user - */ - public function setUser(User $user); - /** * @param TransactionCurrency $currency * @@ -100,6 +95,11 @@ interface CurrencyRepositoryInterface */ public function getCurrencyByPreference(Preference $preference): TransactionCurrency; + /** + * @param User $user + */ + public function setUser(User $user); + /** * @param array $data * diff --git a/app/Repositories/ExportJob/ExportJobRepository.php b/app/Repositories/ExportJob/ExportJobRepository.php index afbdab9248..aa956f901e 100644 --- a/app/Repositories/ExportJob/ExportJobRepository.php +++ b/app/Repositories/ExportJob/ExportJobRepository.php @@ -29,22 +29,6 @@ class ExportJobRepository implements ExportJobRepositoryInterface /** @var User */ private $user; - /** - * ExportJobRepository constructor. - * - * @param User $user - */ - public function __construct(User $user) - { - $this->user = $user; - } - /** - * @param User $user - */ - public function setUser(User $user) - { - $this->user = $user; - } /** * @param ExportJob $job * @param string $status @@ -155,4 +139,12 @@ class ExportJobRepository implements ExportJobRepositoryInterface return $content; } + + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } } diff --git a/app/Repositories/ExportJob/ExportJobRepositoryInterface.php b/app/Repositories/ExportJob/ExportJobRepositoryInterface.php index 484e8496b5..7e93663ea5 100644 --- a/app/Repositories/ExportJob/ExportJobRepositoryInterface.php +++ b/app/Repositories/ExportJob/ExportJobRepositoryInterface.php @@ -23,11 +23,6 @@ use FireflyIII\User; */ interface ExportJobRepositoryInterface { - /** - * @param User $user - */ - public function setUser(User $user); - /** * @param ExportJob $job * @param string $status @@ -67,4 +62,9 @@ interface ExportJobRepositoryInterface */ public function getContent(ExportJob $job): string; + /** + * @param User $user + */ + public function setUser(User $user); + } diff --git a/app/Repositories/ImportJob/ImportJobRepository.php b/app/Repositories/ImportJob/ImportJobRepository.php index 88d54f4a81..0eec759e5b 100644 --- a/app/Repositories/ImportJob/ImportJobRepository.php +++ b/app/Repositories/ImportJob/ImportJobRepository.php @@ -28,22 +28,6 @@ class ImportJobRepository implements ImportJobRepositoryInterface /** @var User */ private $user; - /** - * ExportJobRepository constructor. - * - * @param User $user - */ - public function __construct(User $user) - { - $this->user = $user; - } - /** - * @param User $user - */ - public function setUser(User $user) - { - $this->user = $user; - } /** * @param string $fileType * @@ -101,4 +85,12 @@ class ImportJobRepository implements ImportJobRepositoryInterface return $result; } + + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } } diff --git a/app/Repositories/ImportJob/ImportJobRepositoryInterface.php b/app/Repositories/ImportJob/ImportJobRepositoryInterface.php index b58468f1af..5bb2149fbe 100644 --- a/app/Repositories/ImportJob/ImportJobRepositoryInterface.php +++ b/app/Repositories/ImportJob/ImportJobRepositoryInterface.php @@ -23,11 +23,6 @@ use FireflyIII\User; */ interface ImportJobRepositoryInterface { - /** - * @param User $user - */ - public function setUser(User $user); - /** * @param string $fileType * @@ -41,4 +36,9 @@ interface ImportJobRepositoryInterface * @return ImportJob */ public function findByKey(string $key): ImportJob; + + /** + * @param User $user + */ + public function setUser(User $user); } diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php index aa28551d4c..12ce898ff1 100644 --- a/app/Repositories/Journal/JournalRepository.php +++ b/app/Repositories/Journal/JournalRepository.php @@ -43,15 +43,6 @@ class JournalRepository implements JournalRepositoryInterface /** @var array */ private $validMetaFields = ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date', 'internal_reference', 'notes']; - /** - * JournalRepository constructor. - * - * @param User $user - */ - public function __construct(User $user) - { - $this->user = $user; - } /** * @param User $user */ @@ -59,6 +50,7 @@ class JournalRepository implements JournalRepositoryInterface { $this->user = $user; } + /** * @param TransactionJournal $journal * @param TransactionType $type diff --git a/app/Repositories/Journal/JournalRepositoryInterface.php b/app/Repositories/Journal/JournalRepositoryInterface.php index 446d21c184..f1f5438ad4 100644 --- a/app/Repositories/Journal/JournalRepositoryInterface.php +++ b/app/Repositories/Journal/JournalRepositoryInterface.php @@ -27,10 +27,6 @@ use Illuminate\Support\MessageBag; */ interface JournalRepositoryInterface { - /** - * @param User $user - */ - public function setUser(User $user); /** * @param TransactionJournal $journal * @param TransactionType $type @@ -41,11 +37,6 @@ interface JournalRepositoryInterface */ public function convert(TransactionJournal $journal, TransactionType $type, Account $source, Account $destination): MessageBag; - /** - * @return Collection - */ - public function getTransactionTypes(): Collection; - /** * Deletes a journal. * @@ -71,6 +62,15 @@ interface JournalRepositoryInterface */ public function first(): TransactionJournal; + /** + * @return Collection + */ + public function getTransactionTypes(): Collection; + + /** + * @param User $user + */ + public function setUser(User $user); /** * @param array $data diff --git a/app/Repositories/Journal/JournalTasker.php b/app/Repositories/Journal/JournalTasker.php index 5f9f610ca1..c0424242c8 100644 --- a/app/Repositories/Journal/JournalTasker.php +++ b/app/Repositories/Journal/JournalTasker.php @@ -35,15 +35,6 @@ class JournalTasker implements JournalTaskerInterface /** @var User */ private $user; - /** - * JournalRepository constructor. - * - * @param User $user - */ - public function __construct(User $user) - { - $this->user = $user; - } /** * @param TransactionJournal $journal diff --git a/app/Repositories/Journal/JournalTaskerInterface.php b/app/Repositories/Journal/JournalTaskerInterface.php index e5c007c1e7..1273f69c3a 100644 --- a/app/Repositories/Journal/JournalTaskerInterface.php +++ b/app/Repositories/Journal/JournalTaskerInterface.php @@ -25,11 +25,6 @@ use Illuminate\Support\Collection; */ interface JournalTaskerInterface { - /** - * @param User $user - */ - public function setUser(User $user); - /** * @param TransactionJournal $journal * @@ -46,4 +41,9 @@ interface JournalTaskerInterface * @return array */ public function getTransactionsOverview(TransactionJournal $journal): array; + + /** + * @param User $user + */ + public function setUser(User $user); } diff --git a/app/Repositories/PiggyBank/PiggyBankRepository.php b/app/Repositories/PiggyBank/PiggyBankRepository.php index fad4b0f4fa..1f14467a5b 100644 --- a/app/Repositories/PiggyBank/PiggyBankRepository.php +++ b/app/Repositories/PiggyBank/PiggyBankRepository.php @@ -32,22 +32,6 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface /** @var User */ private $user; - /** - * PiggyBankRepository constructor. - * - * @param User $user - */ - public function __construct(User $user) - { - $this->user = $user; - } - /** - * @param User $user - */ - public function setUser(User $user) - { - $this->user = $user; - } /** * @param PiggyBank $piggyBank * @param string $amount @@ -173,6 +157,14 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface return true; } + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } + /** * @param array $data * diff --git a/app/Repositories/Rule/RuleRepository.php b/app/Repositories/Rule/RuleRepository.php index 05849acd5d..7e0487be63 100644 --- a/app/Repositories/Rule/RuleRepository.php +++ b/app/Repositories/Rule/RuleRepository.php @@ -30,22 +30,6 @@ class RuleRepository implements RuleRepositoryInterface /** @var User */ private $user; - /** - * BillRepository constructor. - * - * @param User $user - */ - public function __construct(User $user) - { - $this->user = $user; - } - /** - * @param User $user - */ - public function setUser(User $user) - { - $this->user = $user; - } /** * @return int */ @@ -224,6 +208,14 @@ class RuleRepository implements RuleRepositoryInterface } + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } + /** * @param array $data * diff --git a/app/Repositories/Rule/RuleRepositoryInterface.php b/app/Repositories/Rule/RuleRepositoryInterface.php index 887bb20d79..6ca1456214 100644 --- a/app/Repositories/Rule/RuleRepositoryInterface.php +++ b/app/Repositories/Rule/RuleRepositoryInterface.php @@ -26,10 +26,6 @@ use FireflyIII\User; */ interface RuleRepositoryInterface { - /** - * @param User $user - */ - public function setUser(User $user); /** * @return int */ @@ -98,6 +94,11 @@ interface RuleRepositoryInterface */ public function resetRulesInGroupOrder(RuleGroup $ruleGroup): bool; + /** + * @param User $user + */ + public function setUser(User $user); + /** * @param array $data * diff --git a/app/Repositories/RuleGroup/RuleGroupRepository.php b/app/Repositories/RuleGroup/RuleGroupRepository.php index 7a6afa6f0e..e413987334 100644 --- a/app/Repositories/RuleGroup/RuleGroupRepository.php +++ b/app/Repositories/RuleGroup/RuleGroupRepository.php @@ -30,15 +30,6 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface /** @var User */ private $user; - /** - * BillRepository constructor. - * - * @param User $user - */ - public function __construct(User $user) - { - $this->user = $user; - } /** * @param User $user */ @@ -46,6 +37,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface { $this->user = $user; } + /** * @return int */ diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index a344da412a..1b0950467f 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -34,15 +34,6 @@ class TagRepository implements TagRepositoryInterface /** @var User */ private $user; - /** - * TagRepository constructor. - * - * @param User $user - */ - public function __construct(User $user) - { - $this->user = $user; - } /** * @param User $user */ @@ -50,6 +41,7 @@ class TagRepository implements TagRepositoryInterface { $this->user = $user; } + /** * * @param TransactionJournal $journal diff --git a/app/Repositories/Tag/TagRepositoryInterface.php b/app/Repositories/Tag/TagRepositoryInterface.php index c813a238da..b360e7d436 100644 --- a/app/Repositories/Tag/TagRepositoryInterface.php +++ b/app/Repositories/Tag/TagRepositoryInterface.php @@ -27,11 +27,6 @@ use Illuminate\Support\Collection; */ interface TagRepositoryInterface { - /** - * @param User $user - */ - public function setUser(User $user); - /** * This method will connect a journal with a tag. * @@ -95,6 +90,11 @@ interface TagRepositoryInterface */ public function lastUseDate(Tag $tag): Carbon; + /** + * @param User $user + */ + public function setUser(User $user); + /** * @param Tag $tag * @param Carbon $start From c7341c9194def617df53f35aca61c6d1fbd1512c Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 16:57:00 +0100 Subject: [PATCH 616/709] Also extend collector. --- app/Helpers/Collector/JournalCollector.php | 15 ++++++++++----- .../Collector/JournalCollectorInterface.php | 4 +++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index c198da7764..f706508183 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -98,12 +98,9 @@ class JournalCollector implements JournalCollectorInterface /** * JournalCollector constructor. - * - * @param User $user */ - public function __construct(User $user) + public function __construct() { - $this->user = $user; $this->query = $this->startQuery(); } @@ -168,7 +165,7 @@ class JournalCollector implements JournalCollectorInterface { $this->run = true; /** @var Collection $set */ - $set = $this->query->get(array_values($this->fields)); + $set = $this->query->get(array_values($this->fields)); Log::debug(sprintf('Count of set is %d', $set->count())); $set = $this->filterTransfers($set); Log::debug(sprintf('Count of set after filterTransfers() is %d', $set->count())); @@ -456,6 +453,14 @@ class JournalCollector implements JournalCollectorInterface return $this; } + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } + /** * @return JournalCollectorInterface */ diff --git a/app/Helpers/Collector/JournalCollectorInterface.php b/app/Helpers/Collector/JournalCollectorInterface.php index b3307ebac0..5b19faeaa3 100644 --- a/app/Helpers/Collector/JournalCollectorInterface.php +++ b/app/Helpers/Collector/JournalCollectorInterface.php @@ -17,6 +17,7 @@ use Carbon\Carbon; use FireflyIII\Models\Budget; use FireflyIII\Models\Category; use FireflyIII\Models\Tag; +use FireflyIII\User; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; @@ -84,7 +85,6 @@ interface JournalCollectorInterface */ public function setBudget(Budget $budget): JournalCollectorInterface; - /** * @param Collection $budgets * @@ -149,6 +149,8 @@ interface JournalCollectorInterface */ public function setTypes(array $types): JournalCollectorInterface; + public function setUser(User $user); + /** * @return JournalCollectorInterface */ From 01468c2663cdf3ebb7d5c77bb5dca4036d22389d Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 16:58:10 +0100 Subject: [PATCH 617/709] Rewrote journal service provider --- app/Providers/JournalServiceProvider.php | 55 +++++++++++++----------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/app/Providers/JournalServiceProvider.php b/app/Providers/JournalServiceProvider.php index 2ac7f61f64..6f22587325 100644 --- a/app/Providers/JournalServiceProvider.php +++ b/app/Providers/JournalServiceProvider.php @@ -14,7 +14,12 @@ declare(strict_types = 1); namespace FireflyIII\Providers; -use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Helpers\Collector\JournalCollector; +use FireflyIII\Helpers\Collector\JournalCollectorInterface; +use FireflyIII\Repositories\Journal\JournalRepository; +use FireflyIII\Repositories\Journal\JournalRepositoryInterface; +use FireflyIII\Repositories\Journal\JournalTasker; +use FireflyIII\Repositories\Journal\JournalTaskerInterface; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -50,16 +55,16 @@ class JournalServiceProvider extends ServiceProvider private function registerCollector() { $this->app->bind( - 'FireflyIII\Helpers\Collector\JournalCollectorInterface', - function (Application $app, array $arguments) { - if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Helpers\Collector\JournalCollector', [auth()->user()]); - } - if (!isset($arguments[0]) && !$app->auth->check()) { - throw new FireflyException('There is no user present.'); + JournalCollectorInterface::class, + function (Application $app) { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollector::class); + + if ($app->auth->check()) { + $collector->setUser(auth()->user()); } - return app('FireflyIII\Helpers\Collector\JournalCollector', $arguments); + return $collector; } ); } @@ -67,16 +72,16 @@ class JournalServiceProvider extends ServiceProvider private function registerRepository() { $this->app->bind( - 'FireflyIII\Repositories\Journal\JournalRepositoryInterface', - function (Application $app, array $arguments) { - if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\Journal\JournalRepository', [auth()->user()]); - } - if (!isset($arguments[0]) && !$app->auth->check()) { - throw new FireflyException('There is no user present.'); + JournalRepositoryInterface::class, + function (Application $app) { + /** @var JournalRepositoryInterface $repository */ + $repository = app(JournalRepository::class); + if ($app->auth->check()) { + + $repository->setUser(auth()->user()); } - return app('FireflyIII\Repositories\Journal\JournalRepository', $arguments); + return $repository; } ); } @@ -84,16 +89,16 @@ class JournalServiceProvider extends ServiceProvider private function registerTasker() { $this->app->bind( - 'FireflyIII\Repositories\Journal\JournalTaskerInterface', - function (Application $app, array $arguments) { - if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\Journal\JournalTasker', [auth()->user()]); - } - if (!isset($arguments[0]) && !$app->auth->check()) { - throw new FireflyException('There is no user present.'); + JournalTaskerInterface::class, + function (Application $app) { + /** @var JournalTaskerInterface $tasker */ + $tasker = app(JournalTasker::class); + + if ($app->auth->check()) { + $tasker->setUser(auth()->user()); } - return app('FireflyIII\Repositories\Journal\JournalTasker', $arguments); + return $tasker; } ); } From 355baa7fef65a8e52803dc393f2743aeaefa0242 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 16:59:55 +0100 Subject: [PATCH 618/709] Rewrote account service provider. --- app/Providers/AccountServiceProvider.php | 39 ++++++++++++------------ 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/app/Providers/AccountServiceProvider.php b/app/Providers/AccountServiceProvider.php index 0ce5a90329..3035731adb 100644 --- a/app/Providers/AccountServiceProvider.php +++ b/app/Providers/AccountServiceProvider.php @@ -14,7 +14,10 @@ declare(strict_types = 1); namespace FireflyIII\Providers; -use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Repositories\Account\AccountRepository; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Account\AccountTasker; +use FireflyIII\Repositories\Account\AccountTaskerInterface; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -44,8 +47,6 @@ class AccountServiceProvider extends ServiceProvider { $this->registerRepository(); $this->registerTasker(); - - } /** @@ -54,16 +55,16 @@ class AccountServiceProvider extends ServiceProvider private function registerRepository() { $this->app->bind( - 'FireflyIII\Repositories\Account\AccountRepositoryInterface', - function (Application $app, array $arguments) { - if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\Account\AccountRepository', [auth()->user()]); - } - if (!isset($arguments[0]) && !$app->auth->check()) { - throw new FireflyException('There is no user present.'); + AccountRepositoryInterface::class, + function (Application $app) { + /** @var AccountRepositoryInterface $repository */ + $repository = app(AccountRepository::class); + + if ($app->auth->check()) { + $repository->setUser(auth()->user()); } - return app('FireflyIII\Repositories\Account\AccountRepository', $arguments); + return $repository; } ); } @@ -74,16 +75,16 @@ class AccountServiceProvider extends ServiceProvider private function registerTasker() { $this->app->bind( - 'FireflyIII\Repositories\Account\AccountTaskerInterface', - function (Application $app, array $arguments) { - if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\Account\AccountTasker', [auth()->user()]); - } - if (!isset($arguments[0]) && !$app->auth->check()) { - throw new FireflyException('There is no user present.'); + AccountTaskerInterface::class, + function (Application $app) { + /** @var AccountTaskerInterface $tasker */ + $tasker = app(AccountTasker::class); + + if ($app->auth->check()) { + $tasker->setUser(auth()->user()); } - return app('FireflyIII\Repositories\Account\AccountTasker', $arguments); + return $tasker; } ); } From 2c786e6a3831d3333d2130f5fadd302b379c7aae Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 17:09:44 +0100 Subject: [PATCH 619/709] Small fixes to collector because constructor is gone. --- app/Helpers/Collector/JournalCollector.php | 54 ++++++++----------- .../Collector/JournalCollectorInterface.php | 6 ++- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index f706508183..7952ed2e2f 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -96,14 +96,6 @@ class JournalCollector implements JournalCollectorInterface /** @var User */ private $user; - /** - * JournalCollector constructor. - */ - public function __construct() - { - $this->query = $this->startQuery(); - } - /** * @return int * @throws FireflyException @@ -461,6 +453,29 @@ class JournalCollector implements JournalCollectorInterface $this->user = $user; } + /** + * + */ + public function startQuery() + { + /** @var EloquentBuilder $query */ + $query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transaction_journals.transaction_currency_id') + ->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id') + ->leftJoin('bills', 'bills.id', 'transaction_journals.bill_id') + ->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id') + ->leftJoin('account_types', 'accounts.account_type_id', 'account_types.id') + ->whereNull('transactions.deleted_at') + ->whereNull('transaction_journals.deleted_at') + ->where('transaction_journals.user_id', $this->user->id) + ->orderBy('transaction_journals.date', 'DESC') + ->orderBy('transaction_journals.order', 'ASC') + ->orderBy('transaction_journals.id', 'DESC'); + + $this->query = $query; + + } + /** * @return JournalCollectorInterface */ @@ -734,27 +749,4 @@ class JournalCollector implements JournalCollectorInterface $this->query->leftJoin('tag_transaction_journal', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'); } } - - /** - * @return EloquentBuilder - */ - private function startQuery(): EloquentBuilder - { - /** @var EloquentBuilder $query */ - $query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transaction_journals.transaction_currency_id') - ->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id') - ->leftJoin('bills', 'bills.id', 'transaction_journals.bill_id') - ->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id') - ->leftJoin('account_types', 'accounts.account_type_id', 'account_types.id') - ->whereNull('transactions.deleted_at') - ->whereNull('transaction_journals.deleted_at') - ->where('transaction_journals.user_id', $this->user->id) - ->orderBy('transaction_journals.date', 'DESC') - ->orderBy('transaction_journals.order', 'ASC') - ->orderBy('transaction_journals.id', 'DESC'); - - return $query; - - } } diff --git a/app/Helpers/Collector/JournalCollectorInterface.php b/app/Helpers/Collector/JournalCollectorInterface.php index 5b19faeaa3..80e41eb781 100644 --- a/app/Helpers/Collector/JournalCollectorInterface.php +++ b/app/Helpers/Collector/JournalCollectorInterface.php @@ -28,7 +28,6 @@ use Illuminate\Support\Collection; */ interface JournalCollectorInterface { - /** * @return int */ @@ -151,6 +150,11 @@ interface JournalCollectorInterface public function setUser(User $user); + /** + * + */ + public function startQuery(); + /** * @return JournalCollectorInterface */ From 3aaf356054456c8d4fd7e82461f8295154ae4772 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 17:10:08 +0100 Subject: [PATCH 620/709] Rewrote bill service provider --- app/Providers/BillServiceProvider.php | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/app/Providers/BillServiceProvider.php b/app/Providers/BillServiceProvider.php index 7275348a8d..a07e8e37ff 100644 --- a/app/Providers/BillServiceProvider.php +++ b/app/Providers/BillServiceProvider.php @@ -14,7 +14,8 @@ declare(strict_types = 1); namespace FireflyIII\Providers; -use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Repositories\Bill\BillRepository; +use FireflyIII\Repositories\Bill\BillRepositoryInterface; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -43,16 +44,16 @@ class BillServiceProvider extends ServiceProvider public function register() { $this->app->bind( - 'FireflyIII\Repositories\Bill\BillRepositoryInterface', - function (Application $app, array $arguments) { - if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\Bill\BillRepository', [auth()->user()]); - } - if (!isset($arguments[0]) && !$app->auth->check()) { - throw new FireflyException('There is no user present.'); + BillRepositoryInterface::class, + function (Application $app) { + /** @var BillRepositoryInterface $repository */ + $repository = app(BillRepository::class); + + if ($app->auth->check()) { + $repository->setUser(auth()->user()); } - return app('FireflyIII\Repositories\Bill\BillRepository', $arguments); + return $repository; } ); } From ac54032f558da08a777771dc68cc1426ff2a0734 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 17:10:23 +0100 Subject: [PATCH 621/709] Need to call everything from collector to work. --- app/Providers/JournalServiceProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Providers/JournalServiceProvider.php b/app/Providers/JournalServiceProvider.php index 6f22587325..b44c11b760 100644 --- a/app/Providers/JournalServiceProvider.php +++ b/app/Providers/JournalServiceProvider.php @@ -59,10 +59,10 @@ class JournalServiceProvider extends ServiceProvider function (Application $app) { /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollector::class); - if ($app->auth->check()) { $collector->setUser(auth()->user()); } + $collector->startQuery(); return $collector; } From 646ed0d4dd309578e540ff92cb4bc71d0cf351d9 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 17:19:51 +0100 Subject: [PATCH 622/709] Rewrote attachment, budget and category service providers. --- app/Providers/AttachmentServiceProvider.php | 18 +++++++++--------- app/Providers/BudgetServiceProvider.php | 18 +++++++++--------- app/Providers/CategoryServiceProvider.php | 18 +++++++++--------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/app/Providers/AttachmentServiceProvider.php b/app/Providers/AttachmentServiceProvider.php index 77debb7354..c9f3a9d48d 100644 --- a/app/Providers/AttachmentServiceProvider.php +++ b/app/Providers/AttachmentServiceProvider.php @@ -14,7 +14,8 @@ declare(strict_types = 1); namespace FireflyIII\Providers; -use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Repositories\Attachment\AttachmentRepository; +use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -43,16 +44,15 @@ class AttachmentServiceProvider extends ServiceProvider public function register() { $this->app->bind( - 'FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface', - function (Application $app, array $arguments) { - if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\Attachment\AttachmentRepository', [auth()->user()]); - } - if (!isset($arguments[0]) && !$app->auth->check()) { - throw new FireflyException('There is no user present.'); + AttachmentRepositoryInterface::class, + function (Application $app) { + /** @var AttachmentRepositoryInterface $repository */ + $repository = app(AttachmentRepository::class); + if ($app->auth->check()) { + $repository->setUser(auth()->user()); } - return app('FireflyIII\Repositories\Attachment\AttachmentRepository', $arguments); + return $repository; } ); } diff --git a/app/Providers/BudgetServiceProvider.php b/app/Providers/BudgetServiceProvider.php index 51408c5098..df87739ea6 100644 --- a/app/Providers/BudgetServiceProvider.php +++ b/app/Providers/BudgetServiceProvider.php @@ -14,7 +14,8 @@ declare(strict_types = 1); namespace FireflyIII\Providers; -use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Repositories\Budget\BudgetRepository; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -43,16 +44,15 @@ class BudgetServiceProvider extends ServiceProvider public function register() { $this->app->bind( - 'FireflyIII\Repositories\Budget\BudgetRepositoryInterface', - function (Application $app, array $arguments) { - if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\Budget\BudgetRepository', [auth()->user()]); - } - if (!isset($arguments[0]) && !$app->auth->check()) { - throw new FireflyException('There is no user present.'); + BudgetRepositoryInterface::class, + function (Application $app) { + /** @var BudgetRepositoryInterface $repository */ + $repository = app(BudgetRepository::class); + if ($app->auth->check()) { + $repository->setUser(auth()->user()); } - return app('FireflyIII\Repositories\Budget\BudgetRepository', $arguments); + return $repository; } ); } diff --git a/app/Providers/CategoryServiceProvider.php b/app/Providers/CategoryServiceProvider.php index a3deb55f30..bdac9c91f1 100644 --- a/app/Providers/CategoryServiceProvider.php +++ b/app/Providers/CategoryServiceProvider.php @@ -14,7 +14,8 @@ declare(strict_types = 1); namespace FireflyIII\Providers; -use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Repositories\Category\CategoryRepository; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -43,16 +44,15 @@ class CategoryServiceProvider extends ServiceProvider public function register() { $this->app->bind( - 'FireflyIII\Repositories\Category\CategoryRepositoryInterface', - function (Application $app, array $arguments) { - if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\Category\CategoryRepository', [auth()->user()]); - } - if (!isset($arguments[0]) && !$app->auth->check()) { - throw new FireflyException('There is no user present.'); + CategoryRepositoryInterface::class, + function (Application $app) { + /** @var CategoryRepository $repository */ + $repository = app(CategoryRepository::class); + if ($app->auth->check()) { + $repository->setUser(auth()->user()); } - return app('FireflyIII\Repositories\Category\CategoryRepository', $arguments); + return $repository; } ); From eeae4d215dc256417e498180903441050cf39e21 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 17:20:00 +0100 Subject: [PATCH 623/709] Removed old service provider. --- app/Providers/CrudServiceProvider.php | 64 --------------------------- 1 file changed, 64 deletions(-) delete mode 100644 app/Providers/CrudServiceProvider.php diff --git a/app/Providers/CrudServiceProvider.php b/app/Providers/CrudServiceProvider.php deleted file mode 100644 index 93acfa1412..0000000000 --- a/app/Providers/CrudServiceProvider.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php -/** - * CrudServiceProvider.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -declare(strict_types = 1); - -namespace FireflyIII\Providers; - -use FireflyIII\Exceptions\FireflyException; -use Illuminate\Foundation\Application; -use Illuminate\Support\ServiceProvider; - -/** - * Class CrudServiceProvider - * - * @package FireflyIII\Providers - */ -class CrudServiceProvider extends ServiceProvider -{ - /** - * Bootstrap the application services. - * - * @return void - */ - public function boot() - { - // - } - - /** - * Register the application services. - * - * @return void - */ - public function register() - { - $this->registerJournal(); - } - - private function registerJournal() - { - $this->app->bind( - 'FireflyIII\Crud\Split\JournalInterface', - function (Application $app, array $arguments) { - if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Crud\Split\Journal', [auth()->user()]); - } - if (!isset($arguments[0]) && !$app->auth->check()) { - throw new FireflyException('There is no user present.'); - } - - return app('FireflyIII\Crud\Split\Journal', $arguments); - } - ); - - } -} From 8263fa41dd7b95f0e115ea29ee61ca35c18b37f8 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 17:29:05 +0100 Subject: [PATCH 624/709] Rewrote currency and export/import job service provider --- app/Providers/CurrencyServiceProvider.php | 18 ++++++++-------- app/Providers/ExportJobServiceProvider.php | 24 +++++++++++----------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/app/Providers/CurrencyServiceProvider.php b/app/Providers/CurrencyServiceProvider.php index d95532e072..bfdf535c70 100644 --- a/app/Providers/CurrencyServiceProvider.php +++ b/app/Providers/CurrencyServiceProvider.php @@ -14,7 +14,8 @@ declare(strict_types = 1); namespace FireflyIII\Providers; -use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Repositories\Currency\CurrencyRepository; +use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -43,16 +44,15 @@ class CurrencyServiceProvider extends ServiceProvider public function register() { $this->app->bind( - 'FireflyIII\Repositories\Currency\CurrencyRepositoryInterface', - function (Application $app, array $arguments) { - if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\Currency\CurrencyRepository', [auth()->user()]); - } - if (!isset($arguments[0]) && !$app->auth->check()) { - throw new FireflyException('There is no user present.'); + CurrencyRepositoryInterface::class, + function (Application $app) { + /** @var CurrencyRepository $repository */ + $repository = app(CurrencyRepository::class); + if ($app->auth->check()) { + $repository->setUser(auth()->user()); } - return app('FireflyIII\Repositories\Currency\CurrencyRepository', $arguments); + return $repository; } ); } diff --git a/app/Providers/ExportJobServiceProvider.php b/app/Providers/ExportJobServiceProvider.php index 220a678623..7c12925df1 100644 --- a/app/Providers/ExportJobServiceProvider.php +++ b/app/Providers/ExportJobServiceProvider.php @@ -15,6 +15,8 @@ declare(strict_types = 1); namespace FireflyIII\Providers; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Repositories\ExportJob\ExportJobRepository; +use FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -33,8 +35,7 @@ class ExportJobServiceProvider extends ServiceProvider */ public function boot() { - $this->exportJob(); - $this->importJob(); + } @@ -45,7 +46,8 @@ class ExportJobServiceProvider extends ServiceProvider */ public function register() { - // + $this->exportJob(); + $this->importJob(); } /** @@ -53,18 +55,16 @@ class ExportJobServiceProvider extends ServiceProvider */ private function exportJob() { - $this->app->bind( - 'FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface', - function (Application $app, array $arguments) { - if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\ExportJob\ExportJobRepository', [auth()->user()]); - } - if (!isset($arguments[0]) && !$app->auth->check()) { - throw new FireflyException('There is no user present.'); + ExportJobRepositoryInterface::class, + function (Application $app) { + /** @var ExportJobRepository $repository */ + $repository = app(ExportJobRepository::class); + if ($app->auth->check()) { + $repository->setUser(auth()->user()); } - return app('FireflyIII\Repositories\ExportJob\ExportJobRepository', $arguments); + return $repository; } ); } From e057c4d79ccd226fb3759c34ed58e399d3c90036 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Mon, 30 Jan 2017 17:29:18 +0100 Subject: [PATCH 625/709] Removed reference to crud service provider. --- config/app.php | 1 - 1 file changed, 1 deletion(-) diff --git a/config/app.php b/config/app.php index 125ec35093..1bbf9d2452 100755 --- a/config/app.php +++ b/config/app.php @@ -73,7 +73,6 @@ return [ /* * More service providers. */ - FireflyIII\Providers\CrudServiceProvider::class, FireflyIII\Providers\AccountServiceProvider::class, FireflyIII\Providers\AttachmentServiceProvider::class, FireflyIII\Providers\BillServiceProvider::class, From 615d90c8f46ac271b1fff2eb497e577caa3553ee Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 2 Feb 2017 07:35:53 +0100 Subject: [PATCH 626/709] Update various service providers. --- app/Providers/FireflyServiceProvider.php | 44 ++++++++++++++++------ app/Providers/PiggyBankServiceProvider.php | 18 ++++----- app/Providers/RuleGroupServiceProvider.php | 16 ++++---- 3 files changed, 50 insertions(+), 28 deletions(-) diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php index b798f531e0..2ef97e48d2 100644 --- a/app/Providers/FireflyServiceProvider.php +++ b/app/Providers/FireflyServiceProvider.php @@ -13,6 +13,28 @@ declare(strict_types = 1); namespace FireflyIII\Providers; +use FireflyIII\Export\Processor; +use FireflyIII\Export\ProcessorInterface; +use FireflyIII\Generator\Chart\Basic\ChartJsGenerator; +use FireflyIII\Generator\Chart\Basic\GeneratorInterface; +use FireflyIII\Helpers\Attachments\AttachmentHelper; +use FireflyIII\Helpers\Attachments\AttachmentHelperInterface; +use FireflyIII\Helpers\Chart\MetaPieChart; +use FireflyIII\Helpers\Chart\MetaPieChartInterface; +use FireflyIII\Helpers\FiscalHelper; +use FireflyIII\Helpers\FiscalHelperInterface; +use FireflyIII\Helpers\Help\Help; +use FireflyIII\Helpers\Help\HelpInterface; +use FireflyIII\Helpers\Report\BalanceReportHelper; +use FireflyIII\Helpers\Report\BalanceReportHelperInterface; +use FireflyIII\Helpers\Report\BudgetReportHelper; +use FireflyIII\Helpers\Report\BudgetReportHelperInterface; +use FireflyIII\Helpers\Report\ReportHelper; +use FireflyIII\Helpers\Report\ReportHelperInterface; +use FireflyIII\Import\ImportProcedure; +use FireflyIII\Import\ImportProcedureInterface; +use FireflyIII\Repositories\User\UserRepository; +use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Support\Amount; use FireflyIII\Support\ExpandedForm; use FireflyIII\Support\FireflyConfig; @@ -94,22 +116,22 @@ class FireflyServiceProvider extends ServiceProvider ); // chart generator: - $this->app->bind('FireflyIII\Generator\Chart\Basic\GeneratorInterface', 'FireflyIII\Generator\Chart\Basic\ChartJsGenerator'); + $this->app->bind(GeneratorInterface::class, ChartJsGenerator::class); // chart builder - $this->app->bind('FireflyIII\Helpers\Chart\MetaPieChartInterface', 'FireflyIII\Helpers\Chart\MetaPieChart'); + $this->app->bind(MetaPieChartInterface::class, MetaPieChart::class); // other generators - $this->app->bind('FireflyIII\Export\ProcessorInterface', 'FireflyIII\Export\Processor'); - $this->app->bind('FireflyIII\Import\ImportProcedureInterface', 'FireflyIII\Import\ImportProcedure'); - $this->app->bind('FireflyIII\Repositories\User\UserRepositoryInterface', 'FireflyIII\Repositories\User\UserRepository'); - $this->app->bind('FireflyIII\Helpers\Attachments\AttachmentHelperInterface', 'FireflyIII\Helpers\Attachments\AttachmentHelper'); + $this->app->bind(ProcessorInterface::class,Processor::class); + $this->app->bind(ImportProcedureInterface::class,ImportProcedure::class); + $this->app->bind(UserRepositoryInterface::class, UserRepository::class); + $this->app->bind(AttachmentHelperInterface::class, AttachmentHelper::class); - $this->app->bind('FireflyIII\Helpers\Help\HelpInterface', 'FireflyIII\Helpers\Help\Help'); - $this->app->bind('FireflyIII\Helpers\Report\ReportHelperInterface', 'FireflyIII\Helpers\Report\ReportHelper'); - $this->app->bind('FireflyIII\Helpers\FiscalHelperInterface', 'FireflyIII\Helpers\FiscalHelper'); - $this->app->bind('FireflyIII\Helpers\Report\BalanceReportHelperInterface', 'FireflyIII\Helpers\Report\BalanceReportHelper'); - $this->app->bind('FireflyIII\Helpers\Report\BudgetReportHelperInterface', 'FireflyIII\Helpers\Report\BudgetReportHelper'); + $this->app->bind(HelpInterface::class, Help::class); + $this->app->bind(ReportHelperInterface::class, ReportHelper::class); + $this->app->bind(FiscalHelperInterface::class,FiscalHelper::class); + $this->app->bind(BalanceReportHelperInterface::class, BalanceReportHelper::class); + $this->app->bind(BudgetReportHelperInterface::class, BudgetReportHelper::class); } } diff --git a/app/Providers/PiggyBankServiceProvider.php b/app/Providers/PiggyBankServiceProvider.php index 11ba540e3c..fb1aaf7d5f 100644 --- a/app/Providers/PiggyBankServiceProvider.php +++ b/app/Providers/PiggyBankServiceProvider.php @@ -15,6 +15,8 @@ declare(strict_types = 1); namespace FireflyIII\Providers; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Repositories\PiggyBank\PiggyBankRepository; +use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -44,16 +46,14 @@ class PiggyBankServiceProvider extends ServiceProvider public function register() { $this->app->bind( - 'FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface', - function (Application $app, array $arguments) { - if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\PiggyBank\PiggyBankRepository', [auth()->user()]); + PiggyBankRepositoryInterface::class, + function (Application $app) { + /** @var PiggyBankRepository $repository */ + $repository = app(PiggyBankRepository::class); + if ($app->auth->check()) { + $repository->setUser(auth()->user()); } - if (!isset($arguments[0]) && !$app->auth->check()) { - throw new FireflyException('There is no user present.'); - } - - return app('FireflyIII\Repositories\PiggyBank\PiggyBankRepository', $arguments); + return $repository; } ); } diff --git a/app/Providers/RuleGroupServiceProvider.php b/app/Providers/RuleGroupServiceProvider.php index bddd6a8480..c6b86c1604 100644 --- a/app/Providers/RuleGroupServiceProvider.php +++ b/app/Providers/RuleGroupServiceProvider.php @@ -14,7 +14,8 @@ declare(strict_types = 1); namespace FireflyIII\Providers; -use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Repositories\RuleGroup\RuleGroupRepository; +use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -44,16 +45,15 @@ class RuleGroupServiceProvider extends ServiceProvider public function register() { $this->app->bind( - 'FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface', + RuleGroupRepositoryInterface::class, function (Application $app, array $arguments) { - if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\RuleGroup\RuleGroupRepository', [auth()->user()]); - } - if (!isset($arguments[0]) && !$app->auth->check()) { - throw new FireflyException('There is no user present.'); + /** @var RuleGroupRepository $repository */ + $repository = app(RuleGroupRepository::class); + if ($app->auth->check()) { + $repository->setUser(auth()->user()); } - return app('FireflyIII\Repositories\RuleGroup\RuleGroupRepository', $arguments); + return $repository; } ); } From a337d9a599c8d48d3ed1c50ebcac3c01d6befe9e Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 2 Feb 2017 07:36:57 +0100 Subject: [PATCH 627/709] New route for JS file, may fix #550 --- resources/views/layout/default.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/layout/default.twig b/resources/views/layout/default.twig index 16a8c2f4e9..43c919f7b4 100644 --- a/resources/views/layout/default.twig +++ b/resources/views/layout/default.twig @@ -177,7 +177,7 @@ <script src="lib/adminlte/js/app.min.js" type="text/javascript"></script> <script type="text/javascript" src="js/lib/accounting.min.js"></script> <script src="js/lib/bootstrap-tour.min.js" type="text/javascript"></script> -<script src="javascript/variables.js" type="text/javascript"></script> +<script src="{{ route('javascript.variables') }}" type="text/javascript"></script> <script type="text/javascript" src="js/ff/firefly.js"></script> <script type="text/javascript" src="js/ff/help.js"></script> {% block scripts %}{% endblock %} From b0d93621a8538867ab1cd500ce76361c5396b56b Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 2 Feb 2017 20:36:32 +0100 Subject: [PATCH 628/709] This fixes #551 --- app/Http/Requests/AccountFormRequest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Requests/AccountFormRequest.php b/app/Http/Requests/AccountFormRequest.php index 91b05abcb0..cbd07f46ee 100644 --- a/app/Http/Requests/AccountFormRequest.php +++ b/app/Http/Requests/AccountFormRequest.php @@ -63,7 +63,7 @@ class AccountFormRequest extends Request { /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); - $accountRoles = join(',', array_keys(config('firefly.accountRoles'))); + $accountRoles = join(',', config('firefly.accountRoles')); $types = join(',', array_keys(config('firefly.subTitlesByIdentifier'))); $ccPaymentTypes = join(',', array_keys(config('firefly.ccTypes'))); From 9aa53c11e066acd8a429d1dc6fa892b3316d850d Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 2 Feb 2017 20:37:39 +0100 Subject: [PATCH 629/709] This fixes #550 --- resources/views/layout/default.twig | 2 +- routes/web.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/views/layout/default.twig b/resources/views/layout/default.twig index 43c919f7b4..fc86a04dce 100644 --- a/resources/views/layout/default.twig +++ b/resources/views/layout/default.twig @@ -177,7 +177,7 @@ <script src="lib/adminlte/js/app.min.js" type="text/javascript"></script> <script type="text/javascript" src="js/lib/accounting.min.js"></script> <script src="js/lib/bootstrap-tour.min.js" type="text/javascript"></script> -<script src="{{ route('javascript.variables') }}" type="text/javascript"></script> +<script src="{{ route('javascript.variables') }}?ext=.js" type="text/javascript"></script> <script type="text/javascript" src="js/ff/firefly.js"></script> <script type="text/javascript" src="js/ff/help.js"></script> {% block scripts %}{% endblock %} diff --git a/routes/web.php b/routes/web.php index bd438f5f59..8a7b9c8eb7 100755 --- a/routes/web.php +++ b/routes/web.php @@ -370,7 +370,7 @@ Route::group( */ Route::group( ['middleware' => 'user-full-auth', 'prefix' => 'javascript', 'as' => 'javascript.'], function () { - Route::get('variables.js', ['uses' => 'JavascriptController@variables', 'as' => 'variables']); + Route::get('variables', ['uses' => 'JavascriptController@variables', 'as' => 'variables']); } ); From c3b4849fa01cbdb3a17db1e42650ac83d5ac3fb1 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Thu, 2 Feb 2017 20:55:01 +0100 Subject: [PATCH 630/709] Various updates for upcoming release 4.3.4 --- CHANGELOG.md | 6 ++++++ composer.json | 1 - composer.lock | 40 ++++++++++++++++++++-------------------- config/firefly.php | 2 +- 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dc3f748c8..91120fe9b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). + +## [4.3.3] - 2017-02-02 +### Fixed +- Fixed bug #550, reported by @worldworm! +- Fixed bug #551, reported by @t-me! + ## [4.3.3] - 2017-01-30 _The 100th release of Firefly!_ diff --git a/composer.json b/composer.json index 6f7bea2597..659144ebc2 100755 --- a/composer.json +++ b/composer.json @@ -53,7 +53,6 @@ "watson/validating": "3.*", "doctrine/dbal": "^2.5", "league/commonmark": "0.15.*", - "twig/twig": "1.30.0", "rcrowe/twigbridge": "0.9.*", "league/csv": "8.*", "laravelcollective/html": "^5.3", diff --git a/composer.lock b/composer.lock index ae477e0989..b816654cd5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "db26ae145d3656fe05d8a222fc21e263", + "content-hash": "8a447ee60bb0509881af5070e5980f18", "packages": [ { "name": "bacon/bacon-qr-code", @@ -158,16 +158,16 @@ }, { "name": "davejamesmiller/laravel-breadcrumbs", - "version": "3.0.1", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/davejamesmiller/laravel-breadcrumbs.git", - "reference": "460bf79e83ff9e3db1e3f1c40169d8893893f8ff" + "reference": "6ca5a600003ecb52a5b5af14dad82033058604e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/davejamesmiller/laravel-breadcrumbs/zipball/460bf79e83ff9e3db1e3f1c40169d8893893f8ff", - "reference": "460bf79e83ff9e3db1e3f1c40169d8893893f8ff", + "url": "https://api.github.com/repos/davejamesmiller/laravel-breadcrumbs/zipball/6ca5a600003ecb52a5b5af14dad82033058604e1", + "reference": "6ca5a600003ecb52a5b5af14dad82033058604e1", "shasum": "" }, "require": { @@ -195,15 +195,15 @@ { "name": "Dave James Miller", "email": "dave@davejamesmiller.com", - "homepage": "http://davejamesmiller.com/" + "homepage": "https://davejamesmiller.com/" } ], "description": "A simple Laravel-style way to create breadcrumbs in Laravel 4+.", - "homepage": "http://laravel-breadcrumbs.davejamesmiller.com", + "homepage": "https://laravel-breadcrumbs.readthedocs.io/", "keywords": [ "laravel" ], - "time": "2016-08-28T16:57:03+00:00" + "time": "2017-01-30T21:16:53+00:00" }, { "name": "dnoegel/php-xdg-base-dir", @@ -1163,16 +1163,16 @@ }, { "name": "league/flysystem", - "version": "1.0.33", + "version": "1.0.34", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "5c7f98498b12d47f9de90ec9186a90000125777c" + "reference": "469ad53c13ea19a0e54e3e5d70f61227ddcc0299" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/5c7f98498b12d47f9de90ec9186a90000125777c", - "reference": "5c7f98498b12d47f9de90ec9186a90000125777c", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/469ad53c13ea19a0e54e3e5d70f61227ddcc0299", + "reference": "469ad53c13ea19a0e54e3e5d70f61227ddcc0299", "shasum": "" }, "require": { @@ -1242,7 +1242,7 @@ "sftp", "storage" ], - "time": "2017-01-23T10:32:09+00:00" + "time": "2017-01-30T17:41:17+00:00" }, { "name": "monolog/monolog", @@ -2730,16 +2730,16 @@ }, { "name": "twig/twig", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "c6ff71094fde15d12398eaba029434b013dc5e59" + "reference": "ddc9e3e20ee9c0b6908f401ac8353635b750eca7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/c6ff71094fde15d12398eaba029434b013dc5e59", - "reference": "c6ff71094fde15d12398eaba029434b013dc5e59", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/ddc9e3e20ee9c0b6908f401ac8353635b750eca7", + "reference": "ddc9e3e20ee9c0b6908f401ac8353635b750eca7", "shasum": "" }, "require": { @@ -2747,12 +2747,12 @@ }, "require-dev": { "symfony/debug": "~2.7", - "symfony/phpunit-bridge": "~3.2@dev" + "symfony/phpunit-bridge": "~3.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.30-dev" + "dev-master": "1.31-dev" } }, "autoload": { @@ -2787,7 +2787,7 @@ "keywords": [ "templating" ], - "time": "2016-12-23T11:06:22+00:00" + "time": "2017-01-11T19:36:15+00:00" }, { "name": "vlucas/phpdotenv", diff --git a/config/firefly.php b/config/firefly.php index 7d0627c636..adae9e8c07 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -24,7 +24,7 @@ return [ ], 'encryption' => (is_null(env('USE_ENCRYPTION')) || env('USE_ENCRYPTION') === true), 'chart' => 'chartjs', - 'version' => '4.3.3', + 'version' => '4.3.4', 'csv_import_enabled' => true, 'maxUploadSize' => 5242880, 'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf'], From e3599c002b4fa39fd87fef29b883d88ed0121330 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 4 Feb 2017 02:19:47 +0100 Subject: [PATCH 631/709] Updated contribution instructions [skip ci] --- .github/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index f7ef4c3bb3..3ebecdd310 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -8,7 +8,7 @@ If you are requesting a new feature, please check out the list of [often request ## Bugs -If you find a bug, include as many log files and details as you think are necessary. +If you find a bug, please take the time and see if the [demo site](https://firefly-iii.nder.be/) is also suffering from this bug. Include as many log files and details as you think are necessary. ## Installation problems From a7c198048e8a7aac49d7294aa25dbf7c9721fe36 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 4 Feb 2017 02:20:17 +0100 Subject: [PATCH 632/709] Add app log method. [skip ci] --- .env.example | 1 + 1 file changed, 1 insertion(+) diff --git a/.env.example b/.env.example index 04528da7fc..eaa69b5d82 100755 --- a/.env.example +++ b/.env.example @@ -3,6 +3,7 @@ APP_DEBUG=false APP_FORCE_SSL=false APP_FORCE_ROOT= APP_KEY=SomeRandomStringOf32CharsExactly +APP_LOG=daily APP_LOG_LEVEL=warning APP_URL=http://localhost From df1da32745801c3eb773a6fd35292d218ca58813 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 4 Feb 2017 02:26:16 +0100 Subject: [PATCH 633/709] Update composer [skip ci] --- composer.json | 1 + composer.lock | 15 ++++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 659144ebc2..f89c9630e8 100755 --- a/composer.json +++ b/composer.json @@ -48,6 +48,7 @@ "require": { "php": ">=7.0.0", "ext-intl": "*", + "ext-bcmath": "*", "laravel/framework": "5.3.29", "davejamesmiller/laravel-breadcrumbs": "^3.0", "watson/validating": "3.*", diff --git a/composer.lock b/composer.lock index b816654cd5..26f2d42877 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "8a447ee60bb0509881af5070e5980f18", + "content-hash": "5f1001d0b797b78e769d42adf358620d", "packages": [ { "name": "bacon/bacon-qr-code", @@ -1421,16 +1421,16 @@ }, { "name": "nikic/php-parser", - "version": "v3.0.2", + "version": "v3.0.3", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "adf44419c0fc014a0f191db6f89d3e55d4211744" + "reference": "5b8182cc0abb4b0ff290ba9df6c0e1323286013a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/adf44419c0fc014a0f191db6f89d3e55d4211744", - "reference": "adf44419c0fc014a0f191db6f89d3e55d4211744", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/5b8182cc0abb4b0ff290ba9df6c0e1323286013a", + "reference": "5b8182cc0abb4b0ff290ba9df6c0e1323286013a", "shasum": "" }, "require": { @@ -1468,7 +1468,7 @@ "parser", "php" ], - "time": "2016-12-06T11:30:35+00:00" + "time": "2017-02-03T21:57:31+00:00" }, { "name": "paragonie/random_compat", @@ -4810,7 +4810,8 @@ "prefer-lowest": false, "platform": { "php": ">=7.0.0", - "ext-intl": "*" + "ext-intl": "*", + "ext-bcmath": "*" }, "platform-dev": [] } From 96b5d174d1ce8ba28ab3113e0ca20731dbd7dd5d Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 4 Feb 2017 03:04:55 +0100 Subject: [PATCH 634/709] Initial code base for Sandstorm.IO support. Very beta. --- .env.sandstorm | 55 ++ .sandstorm/.gitattributes | 5 + .sandstorm/.gitignore | 5 + .sandstorm/Vagrantfile | 103 +++ .sandstorm/build.sh | 21 + .sandstorm/global-setup.sh | 44 + .sandstorm/launcher.sh | 66 ++ .sandstorm/sandstorm-files.list | 1113 ++++++++++++++++++++++++ .sandstorm/sandstorm-pkgdef.capnp | 247 ++++++ .sandstorm/service-config/mime.types | 89 ++ .sandstorm/service-config/nginx.conf | 87 ++ .sandstorm/setup.sh | 61 ++ .sandstorm/stack | 1 + public/images/logo/firefly-iii-128.png | Bin 0 -> 14217 bytes public/images/logo/firefly-iii-150.png | Bin 0 -> 15229 bytes public/images/logo/firefly-iii-24.png | Bin 0 -> 2560 bytes public/images/logo/firefly-iii-48.png | Bin 0 -> 4823 bytes 17 files changed, 1897 insertions(+) create mode 100755 .env.sandstorm create mode 100644 .sandstorm/.gitattributes create mode 100644 .sandstorm/.gitignore create mode 100644 .sandstorm/Vagrantfile create mode 100755 .sandstorm/build.sh create mode 100755 .sandstorm/global-setup.sh create mode 100755 .sandstorm/launcher.sh create mode 100644 .sandstorm/sandstorm-files.list create mode 100644 .sandstorm/sandstorm-pkgdef.capnp create mode 100644 .sandstorm/service-config/mime.types create mode 100644 .sandstorm/service-config/nginx.conf create mode 100755 .sandstorm/setup.sh create mode 100644 .sandstorm/stack create mode 100644 public/images/logo/firefly-iii-128.png create mode 100644 public/images/logo/firefly-iii-150.png create mode 100644 public/images/logo/firefly-iii-24.png create mode 100644 public/images/logo/firefly-iii-48.png diff --git a/.env.sandstorm b/.env.sandstorm new file mode 100755 index 0000000000..ed8d9d511b --- /dev/null +++ b/.env.sandstorm @@ -0,0 +1,55 @@ +APP_ENV=production +APP_DEBUG=true +APP_FORCE_SSL=false +APP_FORCE_ROOT= +APP_KEY=SomeRandomStringOf32CharsExactly +APP_LOG=syslog +APP_LOG_LEVEL=debug +APP_URL=http://localhost + +DB_CONNECTION=mysql +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE=firefly +DB_USERNAME=firefly +DB_PASSWORD=firefly + +BROADCAST_DRIVER=log +CACHE_DRIVER=file +SESSION_DRIVER=file +QUEUE_DRIVER=sync + +COOKIE_PATH="/" +COOKIE_DOMAIN= +COOKIE_SECURE=false + +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_DRIVER=smtp +MAIL_HOST=mailtrap.io +MAIL_PORT=2525 +MAIL_FROM=changeme@example.com +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null + +SEND_REGISTRATION_MAIL=true +SEND_ERROR_MESSAGE=true +SHOW_INCOMPLETE_TRANSLATIONS=false + +CACHE_PREFIX=firefly + +GOOGLE_MAPS_API_KEY= +ANALYTICS_ID= +SITE_OWNER=mail@example.com +USE_ENCRYPTION=true + +PUSHER_KEY= +PUSHER_SECRET= +PUSHER_APP_ID= + +DEMO_USERNAME= +DEMO_PASSWORD= + diff --git a/.sandstorm/.gitattributes b/.sandstorm/.gitattributes new file mode 100644 index 0000000000..5a533b9f62 --- /dev/null +++ b/.sandstorm/.gitattributes @@ -0,0 +1,5 @@ + + +# vagrant-spk creates shell scripts, which must end in \n, even on a \r\n system. +*.sh text eol=lf + diff --git a/.sandstorm/.gitignore b/.sandstorm/.gitignore new file mode 100644 index 0000000000..d70e1e39e4 --- /dev/null +++ b/.sandstorm/.gitignore @@ -0,0 +1,5 @@ + + +# This file stores a list of sub-paths of .sandstorm/ that should be ignored by git. +.vagrant + diff --git a/.sandstorm/Vagrantfile b/.sandstorm/Vagrantfile new file mode 100644 index 0000000000..20c01b674b --- /dev/null +++ b/.sandstorm/Vagrantfile @@ -0,0 +1,103 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# Guess at a reasonable name for the VM based on the folder vagrant-spk is +# run from. The timestamp is there to avoid conflicts if you have multiple +# folders with the same name. +VM_NAME = File.basename(File.dirname(File.dirname(__FILE__))) + "_sandstorm_#{Time.now.utc.to_i}" + +# Vagrantfile API/syntax version. Don't touch unless you know what you're doing! +VAGRANTFILE_API_VERSION = "2" + +Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| + # Base on the Sandstorm snapshots of the official Debian 8 (jessie) box. + config.vm.box = "sandstorm/debian-jessie64" + + if Vagrant.has_plugin?("vagrant-vbguest") then + # vagrant-vbguest is a Vagrant plugin that upgrades + # the version of VirtualBox Guest Additions within each + # guest. If you have the vagrant-vbguest plugin, then it + # needs to know how to compile kernel modules, etc., and so + # we give it this hint about operating system type. + config.vm.guest = "debian" + end + + # We forward port 6080, the Sandstorm web port, so that developers can + # visit their sandstorm app from their browser as local.sandstorm.io:6080 + # (aka 127.0.0.1:6080). + config.vm.network :forwarded_port, guest: 6080, host: 6080 + + # Use a shell script to "provision" the box. This installs Sandstorm using + # the bundled installer. + config.vm.provision "shell", inline: "sudo bash /opt/app/.sandstorm/global-setup.sh", keep_color: true + # Then, do stack-specific and app-specific setup. + config.vm.provision "shell", inline: "sudo bash /opt/app/.sandstorm/setup.sh", keep_color: true + + # Shared folders are configured per-provider since vboxsf can't handle >4096 open files, + # NFS requires privilege escalation every time you bring a VM up, + # and 9p is only available on libvirt. + + # Calculate the number of CPUs and the amount of RAM the system has, + # in a platform-dependent way; further logic below. + cpus = nil + total_kB_ram = nil + + host = RbConfig::CONFIG['host_os'] + if host =~ /darwin/ + cpus = `sysctl -n hw.ncpu`.to_i + total_kB_ram = `sysctl -n hw.memsize`.to_i / 1024 + elsif host =~ /linux/ + cpus = `nproc`.to_i + total_kB_ram = `grep MemTotal /proc/meminfo | awk '{print $2}'`.to_i + elsif host =~ /mingw/ + # powershell may not be available on Windows XP and Vista, so wrap this in a rescue block + begin + cpus = `powershell -Command "(Get-WmiObject Win32_Processor -Property NumberOfLogicalProcessors | Select-Object -Property NumberOfLogicalProcessors | Measure-Object NumberOfLogicalProcessors -Sum).Sum"`.to_i + total_kB_ram = `powershell -Command "Get-CimInstance -class cim_physicalmemory | % $_.Capacity}"`.to_i / 1024 + rescue + end + end + # Use the same number of CPUs within Vagrant as the system, with 1 + # as a default. + # + # Use at least 512MB of RAM, and if the system has more than 2GB of + # RAM, use 1/4 of the system RAM. This seems a reasonable compromise + # between having the Vagrant guest operating system not run out of + # RAM entirely (which it basically would if we went much lower than + # 512MB) and also allowing it to use up a healthily large amount of + # RAM so it can run faster on systems that can afford it. + if cpus.nil? or cpus.zero? + cpus = 1 + end + if total_kB_ram.nil? or total_kB_ram < 2048000 + assign_ram_mb = 512 + else + assign_ram_mb = (total_kB_ram / 1024 / 4) + end + # Actually apply these CPU/memory values to the providers. + config.vm.provider :virtualbox do |vb, override| + vb.cpus = cpus + vb.memory = assign_ram_mb + vb.name = VM_NAME + vb.customize ["modifyvm", :id, "--nictype1", "Am79C973"] + + # /opt/app and /host-dot-sandstorm are used by vagrant-spk + override.vm.synced_folder "..", "/opt/app" + override.vm.synced_folder ENV["HOME"] + "/.sandstorm", "/host-dot-sandstorm" + # /vagrant is not used by vagrant-spk; we need this line so it gets disabled; if we removed the + # line, vagrant would automatically insert a synced folder in /vagrant, which is not what we want. + override.vm.synced_folder "..", "/vagrant", disabled: true + end + config.vm.provider :libvirt do |libvirt, override| + libvirt.cpus = cpus + libvirt.memory = assign_ram_mb + libvirt.default_prefix = VM_NAME + + # /opt/app and /host-dot-sandstorm are used by vagrant-spk + override.vm.synced_folder "..", "/opt/app", type: "9p", accessmode: "passthrough" + override.vm.synced_folder ENV["HOME"] + "/.sandstorm", "/host-dot-sandstorm", type: "9p", accessmode: "passthrough" + # /vagrant is not used by vagrant-spk; we need this line so it gets disabled; if we removed the + # line, vagrant would automatically insert a synced folder in /vagrant, which is not what we want. + override.vm.synced_folder "..", "/vagrant", type: "9p", accessmode: "passthrough", disabled: true + end +end diff --git a/.sandstorm/build.sh b/.sandstorm/build.sh new file mode 100755 index 0000000000..a72ac8cb24 --- /dev/null +++ b/.sandstorm/build.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# Checks if there's a composer.json, and if so, installs/runs composer. +# Only runs when we connect the app to sandstorm (so once). +set -euo pipefail + + + +cd /opt/app + +cp .env.sandstorm .env + +if [ -f /opt/app/composer.json ] ; then + if [ ! -f composer.phar ] ; then + curl -sS https://getcomposer.org/installer | php + fi + php composer.phar install --no-dev --no-suggest +fi + +# link storage folder +rm -rf /opt/app/storage +ln -s /var/storage /opt/app \ No newline at end of file diff --git a/.sandstorm/global-setup.sh b/.sandstorm/global-setup.sh new file mode 100755 index 0000000000..219c770f63 --- /dev/null +++ b/.sandstorm/global-setup.sh @@ -0,0 +1,44 @@ +#!/bin/bash +set -euo pipefail + +# Set options for curl. Since we only want to show errors from these curl commands, we also use +# 'cat' to buffer the output; for more information: +# https://github.com/sandstorm-io/vagrant-spk/issues/158 + +CURL_OPTS="--silent --show-error" +echo localhost > /etc/hostname +hostname localhost + +# The following line copies stderr through stderr to cat without accidentally leaving it in the +# output file. Be careful when changing. See: https://github.com/sandstorm-io/vagrant-spk/pull/159 +curl $CURL_OPTS https://install.sandstorm.io/ 2>&1 > /host-dot-sandstorm/caches/install.sh | cat + +SANDSTORM_CURRENT_VERSION=$(curl $CURL_OPTS -f "https://install.sandstorm.io/dev?from=0&type=install") +SANDSTORM_PACKAGE="sandstorm-$SANDSTORM_CURRENT_VERSION.tar.xz" +if [[ ! -f /host-dot-sandstorm/caches/$SANDSTORM_PACKAGE ]] ; then + echo -n "Downloading Sandstorm version ${SANDSTORM_CURRENT_VERSION}..." + curl $CURL_OPTS --output "/host-dot-sandstorm/caches/$SANDSTORM_PACKAGE.partial" "https://dl.sandstorm.io/$SANDSTORM_PACKAGE" 2>&1 | cat + mv "/host-dot-sandstorm/caches/$SANDSTORM_PACKAGE.partial" "/host-dot-sandstorm/caches/$SANDSTORM_PACKAGE" + echo "...done." +fi +if [ ! -e /opt/sandstorm/latest/sandstorm ] ; then + echo -n "Installing Sandstorm version ${SANDSTORM_CURRENT_VERSION}..." + bash /host-dot-sandstorm/caches/install.sh -d -e "/host-dot-sandstorm/caches/$SANDSTORM_PACKAGE" >/dev/null + echo "...done." +fi +modprobe ip_tables +# Make the vagrant user part of the sandstorm group so that commands like +# `spk dev` work. +usermod -a -G 'sandstorm' 'vagrant' +# Bind to all addresses, so the vagrant port-forward works. +sudo sed --in-place='' \ + --expression='s/^BIND_IP=.*/BIND_IP=0.0.0.0/' \ + /opt/sandstorm/sandstorm.conf +sudo service sandstorm restart +# Enable apt-cacher-ng proxy to make things faster if one appears to be running on the gateway IP +GATEWAY_IP=$(ip route | grep ^default | cut -d ' ' -f 3) +if nc -z "$GATEWAY_IP" 3142 ; then + echo "Acquire::http::Proxy \"http://$GATEWAY_IP:3142\";" > /etc/apt/apt.conf.d/80httpproxy +fi +# Configure apt to retry fetching things that fail to download. +echo "APT::Acquire::Retries \"10\";" > /etc/apt/apt.conf.d/80sandstorm-retry diff --git a/.sandstorm/launcher.sh b/.sandstorm/launcher.sh new file mode 100755 index 0000000000..dba084dfa2 --- /dev/null +++ b/.sandstorm/launcher.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +# Runs every time we create a new grain! + +# Create a bunch of folders under the clean /var that php, nginx, and mysql expect to exist +mkdir -p /var/lib/mysql +mkdir -p /var/lib/nginx +mkdir -p /var/lib/php/sessions/ +mkdir -p /var/log +mkdir -p /var/log/mysql +mkdir -p /var/log/nginx +# Wipe /var/run, since pidfiles and socket files from previous launches should go away +# TODO someday: I'd prefer a tmpfs for these. +rm -rf /var/run +mkdir -p /var/run +rm -rf /var/tmp +mkdir -p /var/tmp +mkdir -p /var/run/mysqld + +# make storage directories +rm -rf /var/storage +mkdir -p /var/storage/app/public +mkdir -p /var/storage/build +mkdir -p /var/storage/database +mkdir -p /var/storage/debugbar +mkdir -p /var/storage/export +mkdir -p /var/storage/framework/cache +mkdir -p /var/storage/framework/sessions +mkdir -p /var/storage/framework/views +mkdir -p /var/storage/logs +mkdir -p /var/storage/upload + + +# Ensure mysql tables created +HOME=/etc/mysql /usr/bin/mysql_install_db --force + +# Spawn mysqld, php +HOME=/etc/mysql /usr/sbin/mysqld & + +/usr/sbin/php-fpm7.0 --nodaemonize --fpm-config /etc/php/7.0/fpm/php-fpm.conf & + +# Wait until mysql and php have bound their sockets, indicating readiness +while [ ! -e /var/run/mysqld/mysqld.sock ] ; do + echo "waiting for mysql to be available at /var/run/mysqld/mysqld.sock" + sleep .5 +done +while [ ! -e /var/run/php7.0-fpm.sock ] ; do + echo "waiting for php7.0-fpm to be available at /var/run/php7.0-fpm.sock" + sleep .5 +done + +echo "Installing database.." +# Install database for Firefly III +echo "CREATE DATABASE IF NOT EXISTS firefly; GRANT ALL on firefly.* TO 'firefly'@'localhost' IDENTIFIED BY 'firefly';" | mysql -uroot +echo "Done!" + +#echo "Generate key..." +#php /opt/app/artisan key:generate +#echo "Done!" + +echo "Migrating..." +php /opt/app/artisan migrate:refresh --seed --force +echo "Done!" + +# Start nginx. +/usr/sbin/nginx -c /opt/app/.sandstorm/service-config/nginx.conf -g "daemon off;" diff --git a/.sandstorm/sandstorm-files.list b/.sandstorm/sandstorm-files.list new file mode 100644 index 0000000000..db769f8f6b --- /dev/null +++ b/.sandstorm/sandstorm-files.list @@ -0,0 +1,1113 @@ +# *** WARNING: GENERATED FILE *** +# This file is automatically updated and rewritten in sorted order every time +# the app runs in dev mode. You may manually add or remove files, but don't +# expect comments or ordering to be retained. +bin/bash +bin/cat +bin/chmod +bin/cp +bin/dash +bin/grep +bin/hostname +bin/ln +bin/ls +bin/mkdir +bin/rm +bin/sed +bin/sh +bin/sleep +bin/stty +etc/alternatives/php +etc/bash.bashrc +etc/bindresvport.blacklist +etc/default/nss +etc/hosts.allow +etc/hosts.deny +etc/inputrc +etc/ld.so.cache +etc/localtime +etc/mysql/conf.d +etc/mysql/conf.d/mysqld_safe_syslog.cnf +etc/mysql/conf.d/sandstorm.cnf +etc/mysql/my.cnf +etc/php/7.0/cli/conf.d +etc/php/7.0/cli/conf.d/10-mysqlnd.ini +etc/php/7.0/cli/conf.d/10-opcache.ini +etc/php/7.0/cli/conf.d/10-pdo.ini +etc/php/7.0/cli/conf.d/15-xml.ini +etc/php/7.0/cli/conf.d/20-bcmath.ini +etc/php/7.0/cli/conf.d/20-calendar.ini +etc/php/7.0/cli/conf.d/20-ctype.ini +etc/php/7.0/cli/conf.d/20-curl.ini +etc/php/7.0/cli/conf.d/20-dom.ini +etc/php/7.0/cli/conf.d/20-exif.ini +etc/php/7.0/cli/conf.d/20-fileinfo.ini +etc/php/7.0/cli/conf.d/20-ftp.ini +etc/php/7.0/cli/conf.d/20-gettext.ini +etc/php/7.0/cli/conf.d/20-iconv.ini +etc/php/7.0/cli/conf.d/20-intl.ini +etc/php/7.0/cli/conf.d/20-json.ini +etc/php/7.0/cli/conf.d/20-mbstring.ini +etc/php/7.0/cli/conf.d/20-mysqli.ini +etc/php/7.0/cli/conf.d/20-pdo_mysql.ini +etc/php/7.0/cli/conf.d/20-phar.ini +etc/php/7.0/cli/conf.d/20-posix.ini +etc/php/7.0/cli/conf.d/20-readline.ini +etc/php/7.0/cli/conf.d/20-shmop.ini +etc/php/7.0/cli/conf.d/20-simplexml.ini +etc/php/7.0/cli/conf.d/20-sockets.ini +etc/php/7.0/cli/conf.d/20-sysvmsg.ini +etc/php/7.0/cli/conf.d/20-sysvsem.ini +etc/php/7.0/cli/conf.d/20-sysvshm.ini +etc/php/7.0/cli/conf.d/20-tokenizer.ini +etc/php/7.0/cli/conf.d/20-wddx.ini +etc/php/7.0/cli/conf.d/20-xmlreader.ini +etc/php/7.0/cli/conf.d/20-xmlwriter.ini +etc/php/7.0/cli/conf.d/20-xsl.ini +etc/php/7.0/cli/php.ini +etc/php/7.0/fpm/conf.d +etc/php/7.0/fpm/conf.d/10-mysqlnd.ini +etc/php/7.0/fpm/conf.d/10-opcache.ini +etc/php/7.0/fpm/conf.d/10-pdo.ini +etc/php/7.0/fpm/conf.d/15-xml.ini +etc/php/7.0/fpm/conf.d/20-bcmath.ini +etc/php/7.0/fpm/conf.d/20-calendar.ini +etc/php/7.0/fpm/conf.d/20-ctype.ini +etc/php/7.0/fpm/conf.d/20-curl.ini +etc/php/7.0/fpm/conf.d/20-dom.ini +etc/php/7.0/fpm/conf.d/20-exif.ini +etc/php/7.0/fpm/conf.d/20-fileinfo.ini +etc/php/7.0/fpm/conf.d/20-ftp.ini +etc/php/7.0/fpm/conf.d/20-gettext.ini +etc/php/7.0/fpm/conf.d/20-iconv.ini +etc/php/7.0/fpm/conf.d/20-intl.ini +etc/php/7.0/fpm/conf.d/20-json.ini +etc/php/7.0/fpm/conf.d/20-mbstring.ini +etc/php/7.0/fpm/conf.d/20-mysqli.ini +etc/php/7.0/fpm/conf.d/20-pdo_mysql.ini +etc/php/7.0/fpm/conf.d/20-phar.ini +etc/php/7.0/fpm/conf.d/20-posix.ini +etc/php/7.0/fpm/conf.d/20-readline.ini +etc/php/7.0/fpm/conf.d/20-shmop.ini +etc/php/7.0/fpm/conf.d/20-simplexml.ini +etc/php/7.0/fpm/conf.d/20-sockets.ini +etc/php/7.0/fpm/conf.d/20-sysvmsg.ini +etc/php/7.0/fpm/conf.d/20-sysvsem.ini +etc/php/7.0/fpm/conf.d/20-sysvshm.ini +etc/php/7.0/fpm/conf.d/20-tokenizer.ini +etc/php/7.0/fpm/conf.d/20-wddx.ini +etc/php/7.0/fpm/conf.d/20-xmlreader.ini +etc/php/7.0/fpm/conf.d/20-xmlwriter.ini +etc/php/7.0/fpm/conf.d/20-xsl.ini +etc/php/7.0/fpm/php-fpm.conf +etc/php/7.0/fpm/php.ini +etc/php/7.0/fpm/pool.d +etc/php/7.0/fpm/pool.d/www.conf +etc/php/7.0/mods-available/bcmath.ini +etc/php/7.0/mods-available/calendar.ini +etc/php/7.0/mods-available/ctype.ini +etc/php/7.0/mods-available/curl.ini +etc/php/7.0/mods-available/dom.ini +etc/php/7.0/mods-available/exif.ini +etc/php/7.0/mods-available/fileinfo.ini +etc/php/7.0/mods-available/ftp.ini +etc/php/7.0/mods-available/gettext.ini +etc/php/7.0/mods-available/iconv.ini +etc/php/7.0/mods-available/intl.ini +etc/php/7.0/mods-available/json.ini +etc/php/7.0/mods-available/mbstring.ini +etc/php/7.0/mods-available/mysqli.ini +etc/php/7.0/mods-available/mysqlnd.ini +etc/php/7.0/mods-available/opcache.ini +etc/php/7.0/mods-available/pdo.ini +etc/php/7.0/mods-available/pdo_mysql.ini +etc/php/7.0/mods-available/phar.ini +etc/php/7.0/mods-available/posix.ini +etc/php/7.0/mods-available/readline.ini +etc/php/7.0/mods-available/shmop.ini +etc/php/7.0/mods-available/simplexml.ini +etc/php/7.0/mods-available/sockets.ini +etc/php/7.0/mods-available/sysvmsg.ini +etc/php/7.0/mods-available/sysvsem.ini +etc/php/7.0/mods-available/sysvshm.ini +etc/php/7.0/mods-available/tokenizer.ini +etc/php/7.0/mods-available/wddx.ini +etc/php/7.0/mods-available/xml.ini +etc/php/7.0/mods-available/xmlreader.ini +etc/php/7.0/mods-available/xmlwriter.ini +etc/php/7.0/mods-available/xsl.ini +etc/services +etc/ssl/openssl.cnf +lib/terminfo/d/dumb +lib/x86_64-linux-gnu/ld-2.19.so +lib/x86_64-linux-gnu/libacl.so.1 +lib/x86_64-linux-gnu/libacl.so.1.1.0 +lib/x86_64-linux-gnu/libaio.so.1 +lib/x86_64-linux-gnu/libaio.so.1.0.1 +lib/x86_64-linux-gnu/libattr.so.1 +lib/x86_64-linux-gnu/libattr.so.1.1.0 +lib/x86_64-linux-gnu/libaudit.so.1 +lib/x86_64-linux-gnu/libaudit.so.1.0.0 +lib/x86_64-linux-gnu/libbsd.so.0 +lib/x86_64-linux-gnu/libbsd.so.0.7.0 +lib/x86_64-linux-gnu/libbz2.so.1.0 +lib/x86_64-linux-gnu/libbz2.so.1.0.4 +lib/x86_64-linux-gnu/libc-2.19.so +lib/x86_64-linux-gnu/libc.so.6 +lib/x86_64-linux-gnu/libcom_err.so.2 +lib/x86_64-linux-gnu/libcom_err.so.2.1 +lib/x86_64-linux-gnu/libcrypt-2.19.so +lib/x86_64-linux-gnu/libcrypt.so.1 +lib/x86_64-linux-gnu/libdl-2.19.so +lib/x86_64-linux-gnu/libdl.so.2 +lib/x86_64-linux-gnu/libexpat.so.1 +lib/x86_64-linux-gnu/libexpat.so.1.6.0 +lib/x86_64-linux-gnu/libgcc_s.so.1 +lib/x86_64-linux-gnu/libgcrypt.so.20 +lib/x86_64-linux-gnu/libgcrypt.so.20.0.3 +lib/x86_64-linux-gnu/libgpg-error.so.0 +lib/x86_64-linux-gnu/libgpg-error.so.0.13.0 +lib/x86_64-linux-gnu/libjson-c.so.2 +lib/x86_64-linux-gnu/libjson-c.so.2.0.0 +lib/x86_64-linux-gnu/libkeyutils.so.1 +lib/x86_64-linux-gnu/libkeyutils.so.1.5 +lib/x86_64-linux-gnu/liblzma.so.5 +lib/x86_64-linux-gnu/liblzma.so.5.0.0 +lib/x86_64-linux-gnu/libm-2.19.so +lib/x86_64-linux-gnu/libm.so.6 +lib/x86_64-linux-gnu/libncurses.so.5 +lib/x86_64-linux-gnu/libncurses.so.5.9 +lib/x86_64-linux-gnu/libnsl-2.19.so +lib/x86_64-linux-gnu/libnsl.so.1 +lib/x86_64-linux-gnu/libnss_compat-2.19.so +lib/x86_64-linux-gnu/libnss_compat.so.2 +lib/x86_64-linux-gnu/libnss_dns-2.19.so +lib/x86_64-linux-gnu/libnss_dns.so.2 +lib/x86_64-linux-gnu/libnss_files-2.19.so +lib/x86_64-linux-gnu/libnss_files.so.2 +lib/x86_64-linux-gnu/libnss_nis-2.19.so +lib/x86_64-linux-gnu/libnss_nis.so.2 +lib/x86_64-linux-gnu/libpam.so.0 +lib/x86_64-linux-gnu/libpam.so.0.83.1 +lib/x86_64-linux-gnu/libpcre.so.3 +lib/x86_64-linux-gnu/libpcre.so.3.13.3 +lib/x86_64-linux-gnu/libpng12.so.0 +lib/x86_64-linux-gnu/libpng12.so.0.50.0 +lib/x86_64-linux-gnu/libpthread-2.19.so +lib/x86_64-linux-gnu/libpthread.so.0 +lib/x86_64-linux-gnu/libreadline.so.6 +lib/x86_64-linux-gnu/libreadline.so.6.3 +lib/x86_64-linux-gnu/libresolv-2.19.so +lib/x86_64-linux-gnu/libresolv.so.2 +lib/x86_64-linux-gnu/librt-2.19.so +lib/x86_64-linux-gnu/librt.so.1 +lib/x86_64-linux-gnu/libselinux.so.1 +lib/x86_64-linux-gnu/libsystemd.so.0 +lib/x86_64-linux-gnu/libsystemd.so.0.3.1 +lib/x86_64-linux-gnu/libtinfo.so.5 +lib/x86_64-linux-gnu/libtinfo.so.5.9 +lib/x86_64-linux-gnu/libutil-2.19.so +lib/x86_64-linux-gnu/libutil.so.1 +lib/x86_64-linux-gnu/libwrap.so.0 +lib/x86_64-linux-gnu/libwrap.so.0.7.6 +lib/x86_64-linux-gnu/libz.so.1 +lib/x86_64-linux-gnu/libz.so.1.2.8 +lib64/ld-linux-x86-64.so.2 +opt/app +opt/app/.env +opt/app/.sandstorm/launcher.sh +opt/app/.sandstorm/service-config/mime.types +opt/app/.sandstorm/service-config/nginx.conf +opt/app/app/Bootstrap/ConfigureLogging.php +opt/app/app/Console/Commands/CreateImport.php +opt/app/app/Console/Commands/EncryptFile.php +opt/app/app/Console/Commands/Import.php +opt/app/app/Console/Commands/ScanAttachments.php +opt/app/app/Console/Commands/UpgradeDatabase.php +opt/app/app/Console/Commands/UpgradeFireflyInstructions.php +opt/app/app/Console/Commands/UseEncryption.php +opt/app/app/Console/Commands/VerifyDatabase.php +opt/app/app/Console/Kernel.php +opt/app/app/Exceptions/Handler.php +opt/app/app/Http/Controllers/Controller.php +opt/app/app/Http/Controllers/HomeController.php +opt/app/app/Http/Kernel.php +opt/app/app/Http/breadcrumbs.php +opt/app/app/Jobs/Job.php +opt/app/app/Jobs/MailError.php +opt/app/app/Models/Account.php +opt/app/app/Models/AccountType.php +opt/app/app/Models/Configuration.php +opt/app/app/Models/PiggyBank.php +opt/app/app/Models/Role.php +opt/app/app/Models/TransactionCurrency.php +opt/app/app/Models/TransactionJournal.php +opt/app/app/Models/TransactionType.php +opt/app/app/Providers/AccountServiceProvider.php +opt/app/app/Providers/AppServiceProvider.php +opt/app/app/Providers/AttachmentServiceProvider.php +opt/app/app/Providers/AuthServiceProvider.php +opt/app/app/Providers/BillServiceProvider.php +opt/app/app/Providers/BudgetServiceProvider.php +opt/app/app/Providers/CategoryServiceProvider.php +opt/app/app/Providers/CrudServiceProvider.php +opt/app/app/Providers/CurrencyServiceProvider.php +opt/app/app/Providers/EventServiceProvider.php +opt/app/app/Providers/ExportJobServiceProvider.php +opt/app/app/Providers/FireflyServiceProvider.php +opt/app/app/Providers/JournalServiceProvider.php +opt/app/app/Providers/PiggyBankServiceProvider.php +opt/app/app/Providers/RouteServiceProvider.php +opt/app/app/Providers/RuleGroupServiceProvider.php +opt/app/app/Providers/RuleServiceProvider.php +opt/app/app/Providers/SearchServiceProvider.php +opt/app/app/Providers/TagServiceProvider.php +opt/app/app/Support/Facades/FireflyConfig.php +opt/app/app/Support/FireflyConfig.php +opt/app/app/Support/Models/TransactionJournalSupport.php +opt/app/app/Support/Twig/General.php +opt/app/app/Support/Twig/Journal.php +opt/app/app/Support/Twig/PiggyBank.php +opt/app/app/Support/Twig/Rule.php +opt/app/app/Support/Twig/Transaction.php +opt/app/app/Support/Twig/Translation.php +opt/app/app/Validation/FireflyValidator.php +opt/app/artisan +opt/app/bootstrap/app.php +opt/app/bootstrap/autoload.php +opt/app/bootstrap/cache/services.php +opt/app/config +opt/app/config/app.php +opt/app/config/auth.php +opt/app/config/broadcasting.php +opt/app/config/cache.php +opt/app/config/compile.php +opt/app/config/csv.php +opt/app/config/database.php +opt/app/config/filesystems.php +opt/app/config/firefly.php +opt/app/config/mail.php +opt/app/config/queue.php +opt/app/config/services.php +opt/app/config/session.php +opt/app/config/twigbridge.php +opt/app/config/upgrade.php +opt/app/config/view.php +opt/app/database/migrations +opt/app/database/migrations/2016_06_16_000000_create_support_tables.php +opt/app/database/migrations/2016_06_16_000001_create_users_table.php +opt/app/database/migrations/2016_06_16_000002_create_main_tables.php +opt/app/database/migrations/2016_08_25_091522_changes_for_3101.php +opt/app/database/migrations/2016_09_12_121359_fix_nullables.php +opt/app/database/migrations/2016_10_09_150037_expand_transactions_table.php +opt/app/database/migrations/2016_10_22_075804_changes_for_v410.php +opt/app/database/migrations/2016_11_24_210552_changes_for_v420.php +opt/app/database/migrations/2016_12_22_150431_changes_for_v430.php +opt/app/database/migrations/2016_12_28_203205_changes_for_v431.php +opt/app/database/seeds/AccountTypeSeeder.php +opt/app/database/seeds/DatabaseSeeder.php +opt/app/database/seeds/PermissionSeeder.php +opt/app/database/seeds/TransactionCurrencySeeder.php +opt/app/database/seeds/TransactionTypeSeeder.php +opt/app/public/index.php +opt/app/resources/views/emails/error-html.twig +opt/app/resources/views/emails/error-text.twig +opt/app/resources/views/emails/footer-html.twig +opt/app/resources/views/emails/footer-text.twig +opt/app/resources/views/emails/header-html.twig +opt/app/resources/views/emails/header-text.twig +opt/app/routes/api.php +opt/app/routes/console.php +opt/app/routes/web.php +opt/app/storage +opt/app/vendor/autoload.php +opt/app/vendor/composer/ClassLoader.php +opt/app/vendor/composer/autoload_real.php +opt/app/vendor/composer/autoload_static.php +opt/app/vendor/davejamesmiller/laravel-breadcrumbs/config/breadcrumbs.php +opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/CurrentRoute.php +opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/Facade.php +opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/Generator.php +opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/Manager.php +opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/ServiceProvider.php +opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/View.php +opt/app/vendor/doctrine/common/lib/Doctrine/Common/EventManager.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Configuration.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Connection.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/DriverException.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ExceptionConverterDriver.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOException.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOMySql/Driver.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ResultStatement.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/ServerInfoAwareConnection.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/Statement.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Events.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/KeywordList.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/Keywords/MySQLKeywords.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractAsset.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Column.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Comparator.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Constraint.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/ForeignKeyConstraint.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Identifier.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Index.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Table.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/TableDiff.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ArrayType.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BigIntType.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BinaryType.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BlobType.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/BooleanType.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeType.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateTimeTzType.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DateType.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/DecimalType.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/FloatType.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/GuidType.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/IntegerType.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/JsonArrayType.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ObjectType.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SimpleArrayType.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/SmallIntType.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/StringType.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TextType.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/TimeType.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/Type.php +opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/VersionAwarePlatformDriver.php +opt/app/vendor/doctrine/inflector/lib/Doctrine/Common/Inflector/Inflector.php +opt/app/vendor/laravel/framework/src/Illuminate/Auth/Access/Gate.php +opt/app/vendor/laravel/framework/src/Illuminate/Auth/Access/HandlesAuthorization.php +opt/app/vendor/laravel/framework/src/Illuminate/Auth/AuthManager.php +opt/app/vendor/laravel/framework/src/Illuminate/Auth/AuthServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Auth/Console/ClearResetsCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Auth/Console/MakeAuthCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Auth/CreatesUserProviders.php +opt/app/vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Auth/GuardHelpers.php +opt/app/vendor/laravel/framework/src/Illuminate/Auth/Passwords/PasswordResetServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php +opt/app/vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Bus/BusServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php +opt/app/vendor/laravel/framework/src/Illuminate/Bus/Queueable.php +opt/app/vendor/laravel/framework/src/Illuminate/Cache/CacheManager.php +opt/app/vendor/laravel/framework/src/Illuminate/Cache/CacheServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Cache/Console/CacheTableCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Cache/Console/ClearCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Cache/Events/CacheMissed.php +opt/app/vendor/laravel/framework/src/Illuminate/Cache/FileStore.php +opt/app/vendor/laravel/framework/src/Illuminate/Cache/Repository.php +opt/app/vendor/laravel/framework/src/Illuminate/Cache/RetrievesMultipleKeys.php +opt/app/vendor/laravel/framework/src/Illuminate/Config/Repository.php +opt/app/vendor/laravel/framework/src/Illuminate/Console/AppNamespaceDetectorTrait.php +opt/app/vendor/laravel/framework/src/Illuminate/Console/Application.php +opt/app/vendor/laravel/framework/src/Illuminate/Console/Command.php +opt/app/vendor/laravel/framework/src/Illuminate/Console/ConfirmableTrait.php +opt/app/vendor/laravel/framework/src/Illuminate/Console/Events/ArtisanStarting.php +opt/app/vendor/laravel/framework/src/Illuminate/Console/GeneratorCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Console/OutputStyle.php +opt/app/vendor/laravel/framework/src/Illuminate/Console/Parser.php +opt/app/vendor/laravel/framework/src/Illuminate/Console/ScheduleServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Console/Scheduling/Schedule.php +opt/app/vendor/laravel/framework/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Container/Container.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Auth/Access/Gate.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Auth/Factory.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Auth/Guard.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Auth/StatefulGuard.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Auth/SupportsBasicAuth.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Auth/UserProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Bus/Dispatcher.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Bus/QueueingDispatcher.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Cache/Factory.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Cache/Repository.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Cache/Store.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Config/Repository.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Console/Application.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Console/Kernel.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Container/Container.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Cookie/Factory.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Cookie/QueueingFactory.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Debug/ExceptionHandler.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Encryption/Encrypter.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Events/Dispatcher.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Filesystem/FileNotFoundException.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Foundation/Application.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Hashing/Hasher.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Http/Kernel.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Logging/Log.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Mail/MailQueue.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Mail/Mailer.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Pagination/Paginator.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Pipeline/Pipeline.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/Factory.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/Job.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/Monitor.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/Queue.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/QueueableEntity.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/ShouldQueue.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Routing/Registrar.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Routing/UrlGenerator.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Routing/UrlRoutable.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Support/Arrayable.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Support/Htmlable.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Support/Jsonable.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Support/MessageBag.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Support/MessageProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Support/Renderable.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Validation/Factory.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Validation/ValidatesWhenResolved.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Validation/Validator.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/View/Factory.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/View/View.php +opt/app/vendor/laravel/framework/src/Illuminate/Cookie/CookieJar.php +opt/app/vendor/laravel/framework/src/Illuminate/Cookie/CookieServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Connection.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/ConnectionInterface.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/ConnectionResolverInterface.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Connectors/ConnectionFactory.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Connectors/ConnectorInterface.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Connectors/MySqlConnector.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Console/Migrations/BaseCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Console/Migrations/InstallCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Console/Migrations/MigrateCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Console/Migrations/MigrateMakeCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Console/Migrations/RefreshCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Console/Migrations/ResetCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Console/Migrations/RollbackCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Console/Migrations/StatusCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Console/Seeds/SeedCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Console/Seeds/SeederMakeCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/DatabaseManager.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/DatabaseServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/DetectsDeadlocks.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/DetectsLostConnections.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Scope.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/SoftDeletes.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/SoftDeletingScope.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Events/QueryExecuted.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Grammar.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/MigrationServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Migrations/DatabaseMigrationRepository.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Migrations/Migration.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Migrations/MigrationCreator.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Migrations/MigrationRepositoryInterface.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Migrations/Migrator.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/MySqlConnection.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Query/Grammars/Grammar.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Query/Processors/MySqlProcessor.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Query/Processors/Processor.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/QueryException.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Schema/Blueprint.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Schema/Builder.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Schema/Grammars/Grammar.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Schema/MySqlBuilder.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/SeedServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Seeder.php +opt/app/vendor/laravel/framework/src/Illuminate/Encryption/Encrypter.php +opt/app/vendor/laravel/framework/src/Illuminate/Encryption/EncryptionServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php +opt/app/vendor/laravel/framework/src/Illuminate/Events/EventServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Filesystem/Filesystem.php +opt/app/vendor/laravel/framework/src/Illuminate/Filesystem/FilesystemServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/AliasLoader.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Application.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Auth/Access/AuthorizesRequests.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/BootProviders.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/ConfigureLogging.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/DetectEnvironment.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/LoadConfiguration.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/RegisterFacades.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/RegisterProviders.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/SetRequestForConsole.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Bus/DispatchesJobs.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/AppNameCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/ClearCompiledCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/ConfigCacheCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/ConfigClearCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/ConsoleMakeCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/DownCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/EnvironmentCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/EventGenerateCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/EventMakeCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/JobMakeCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/KeyGenerateCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/ListenerMakeCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/MailMakeCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/ModelMakeCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/NotificationMakeCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/OptimizeCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/PolicyMakeCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/ProviderMakeCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/RequestMakeCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/RouteCacheCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/RouteClearCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/RouteListCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/ServeCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/StorageLinkCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/TestMakeCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/TinkerCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/UpCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/VendorPublishCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Console/ViewClearCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/EnvironmentDetector.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/FormRequest.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/CheckForMaintenanceMode.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/ProviderRepository.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Providers/ComposerServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Providers/ConsoleSupportServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Providers/FoundationServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Support/Providers/AuthServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Support/Providers/EventServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Support/Providers/RouteServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Validation/ValidatesRequests.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php +opt/app/vendor/laravel/framework/src/Illuminate/Hashing/BcryptHasher.php +opt/app/vendor/laravel/framework/src/Illuminate/Hashing/HashServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Http/Request.php +opt/app/vendor/laravel/framework/src/Illuminate/Http/Response.php +opt/app/vendor/laravel/framework/src/Illuminate/Http/ResponseTrait.php +opt/app/vendor/laravel/framework/src/Illuminate/Log/Writer.php +opt/app/vendor/laravel/framework/src/Illuminate/Mail/Events/MessageSending.php +opt/app/vendor/laravel/framework/src/Illuminate/Mail/MailServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Mail/Mailer.php +opt/app/vendor/laravel/framework/src/Illuminate/Mail/Message.php +opt/app/vendor/laravel/framework/src/Illuminate/Mail/TransportManager.php +opt/app/vendor/laravel/framework/src/Illuminate/Notifications/Console/NotificationTableCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Notifications/NotificationServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Pagination/AbstractPaginator.php +opt/app/vendor/laravel/framework/src/Illuminate/Pagination/PaginationServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Pagination/Paginator.php +opt/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php +opt/app/vendor/laravel/framework/src/Illuminate/Pipeline/PipelineServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/Connectors/ConnectorInterface.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/Connectors/SyncConnector.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/Console/FailedTableCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/Console/FlushFailedCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/Console/ForgetFailedCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/Console/ListFailedCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/Console/ListenCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/Console/RestartCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/Console/RetryCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/Console/TableCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/Console/WorkCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/ConsoleServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/Events/JobProcessed.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/Events/JobProcessing.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/InteractsWithQueue.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/Jobs/Job.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/Jobs/SyncJob.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/Listener.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/Queue.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/QueueManager.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/QueueServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/SerializesAndRestoresModelIdentifiers.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/SerializesModels.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/SyncQueue.php +opt/app/vendor/laravel/framework/src/Illuminate/Queue/Worker.php +opt/app/vendor/laravel/framework/src/Illuminate/Redis/RedisServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/Console/ControllerMakeCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/Console/MiddlewareMakeCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/Controller.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/Events/RouteMatched.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/Matching/HostValidator.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/Matching/MethodValidator.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/Matching/SchemeValidator.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/Matching/UriValidator.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/Matching/ValidatorInterface.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/Route.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/RouteCollection.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/RouteDependencyResolverTrait.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/Router.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/RoutingServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/UrlGenerator.php +opt/app/vendor/laravel/framework/src/Illuminate/Session/Console/SessionTableCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Session/EncryptedStore.php +opt/app/vendor/laravel/framework/src/Illuminate/Session/FileSessionHandler.php +opt/app/vendor/laravel/framework/src/Illuminate/Session/SessionInterface.php +opt/app/vendor/laravel/framework/src/Illuminate/Session/SessionManager.php +opt/app/vendor/laravel/framework/src/Illuminate/Session/SessionServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Session/Store.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/AggregateServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Arr.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Collection.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Composer.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Cache.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/DB.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Event.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Gate.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Log.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Mail.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Request.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Route.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Schema.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Validator.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/View.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Fluent.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Manager.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/MessageBag.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/NamespacedItemResolver.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Pluralizer.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/ServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Str.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Traits/Macroable.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/helpers.php +opt/app/vendor/laravel/framework/src/Illuminate/Translation/FileLoader.php +opt/app/vendor/laravel/framework/src/Illuminate/Translation/LoaderInterface.php +opt/app/vendor/laravel/framework/src/Illuminate/Translation/TranslationServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Translation/Translator.php +opt/app/vendor/laravel/framework/src/Illuminate/Validation/DatabasePresenceVerifier.php +opt/app/vendor/laravel/framework/src/Illuminate/Validation/Factory.php +opt/app/vendor/laravel/framework/src/Illuminate/Validation/PresenceVerifierInterface.php +opt/app/vendor/laravel/framework/src/Illuminate/Validation/ValidatesWhenResolvedTrait.php +opt/app/vendor/laravel/framework/src/Illuminate/Validation/ValidationServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Validation/Validator.php +opt/app/vendor/laravel/framework/src/Illuminate/View/Compilers/CompilerInterface.php +opt/app/vendor/laravel/framework/src/Illuminate/View/Engines/CompilerEngine.php +opt/app/vendor/laravel/framework/src/Illuminate/View/Engines/EngineInterface.php +opt/app/vendor/laravel/framework/src/Illuminate/View/Engines/EngineResolver.php +opt/app/vendor/laravel/framework/src/Illuminate/View/Engines/PhpEngine.php +opt/app/vendor/laravel/framework/src/Illuminate/View/Factory.php +opt/app/vendor/laravel/framework/src/Illuminate/View/FileViewFinder.php +opt/app/vendor/laravel/framework/src/Illuminate/View/View.php +opt/app/vendor/laravel/framework/src/Illuminate/View/ViewFinderInterface.php +opt/app/vendor/laravel/framework/src/Illuminate/View/ViewServiceProvider.php +opt/app/vendor/laravelcollective/html/src/HtmlServiceProvider.php +opt/app/vendor/laravelcollective/html/src/helpers.php +opt/app/vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php +opt/app/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php +opt/app/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php +opt/app/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php +opt/app/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php +opt/app/vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php +opt/app/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php +opt/app/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php +opt/app/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php +opt/app/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php +opt/app/vendor/monolog/monolog/src/Monolog/Logger.php +opt/app/vendor/nesbot/carbon/src/Carbon/Carbon.php +opt/app/vendor/paragonie/random_compat/lib/random.php +opt/app/vendor/pragmarx/google2fa/src/Vendor/Laravel/ServiceProvider.php +opt/app/vendor/psr/log/Psr/Log/LoggerInterface.php +opt/app/vendor/psy/psysh/src/Psy/functions.php +opt/app/vendor/rcrowe/twigbridge/config/twigbridge.php +opt/app/vendor/rcrowe/twigbridge/src/Bridge.php +opt/app/vendor/rcrowe/twigbridge/src/Command/Clean.php +opt/app/vendor/rcrowe/twigbridge/src/Command/Lint.php +opt/app/vendor/rcrowe/twigbridge/src/Command/TwigBridge.php +opt/app/vendor/rcrowe/twigbridge/src/Engine/Compiler.php +opt/app/vendor/rcrowe/twigbridge/src/Engine/Twig.php +opt/app/vendor/rcrowe/twigbridge/src/Extension/Laravel/Auth.php +opt/app/vendor/rcrowe/twigbridge/src/Extension/Laravel/Config.php +opt/app/vendor/rcrowe/twigbridge/src/Extension/Laravel/Dump.php +opt/app/vendor/rcrowe/twigbridge/src/Extension/Laravel/Input.php +opt/app/vendor/rcrowe/twigbridge/src/Extension/Laravel/Session.php +opt/app/vendor/rcrowe/twigbridge/src/Extension/Laravel/Str.php +opt/app/vendor/rcrowe/twigbridge/src/Extension/Laravel/Translator.php +opt/app/vendor/rcrowe/twigbridge/src/Extension/Laravel/Url.php +opt/app/vendor/rcrowe/twigbridge/src/Extension/Loader/Facade/Caller.php +opt/app/vendor/rcrowe/twigbridge/src/Extension/Loader/Facades.php +opt/app/vendor/rcrowe/twigbridge/src/Extension/Loader/Filters.php +opt/app/vendor/rcrowe/twigbridge/src/Extension/Loader/Functions.php +opt/app/vendor/rcrowe/twigbridge/src/Extension/Loader/Loader.php +opt/app/vendor/rcrowe/twigbridge/src/Facade/Twig.php +opt/app/vendor/rcrowe/twigbridge/src/ServiceProvider.php +opt/app/vendor/rcrowe/twigbridge/src/Twig/Globals.php +opt/app/vendor/rcrowe/twigbridge/src/Twig/Loader.php +opt/app/vendor/rcrowe/twigbridge/src/Twig/Template.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory/SimpleCharacterReaderFactory.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/DependencyContainer.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/Event.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventDispatcher.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventObject.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/SimpleEventDispatcher.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeEvent.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionEvent.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Filterable.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/InputByteStream.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/IoException.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/KeyCacheInputStream.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/SimpleKeyCacheInputStream.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mailer.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Message.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/CharsetObserver.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoderProxy.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EncodingObserver.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Grammar.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Header.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderFactory.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderSet.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/AbstractHeader.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Message.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimeEntity.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimePart.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ParameterizedHeader.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderFactory.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/MimePart.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/OutputByteStream.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Preferences.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/ReplacementFilterFactory.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SmtpTransport.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilter.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/ByteArrayReplacementFilter.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/SwiftException.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/LoginAuthenticator.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/NTLMAuthenticator.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/PlainAuthenticator.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/XOAuth2Authenticator.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Authenticator.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpHandler.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/IoBuffer.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SmtpAgent.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php +opt/app/vendor/swiftmailer/swiftmailer/lib/classes/Swift/TransportException.php +opt/app/vendor/swiftmailer/swiftmailer/lib/dependency_maps/cache_deps.php +opt/app/vendor/swiftmailer/swiftmailer/lib/dependency_maps/message_deps.php +opt/app/vendor/swiftmailer/swiftmailer/lib/dependency_maps/mime_deps.php +opt/app/vendor/swiftmailer/swiftmailer/lib/dependency_maps/transport_deps.php +opt/app/vendor/swiftmailer/swiftmailer/lib/mime_types.php +opt/app/vendor/swiftmailer/swiftmailer/lib/preferences.php +opt/app/vendor/swiftmailer/swiftmailer/lib/swift_init.php +opt/app/vendor/swiftmailer/swiftmailer/lib/swift_required.php +opt/app/vendor/symfony/console/Application.php +opt/app/vendor/symfony/console/Command/Command.php +opt/app/vendor/symfony/console/Command/HelpCommand.php +opt/app/vendor/symfony/console/Command/ListCommand.php +opt/app/vendor/symfony/console/Formatter/OutputFormatter.php +opt/app/vendor/symfony/console/Formatter/OutputFormatterInterface.php +opt/app/vendor/symfony/console/Formatter/OutputFormatterStyle.php +opt/app/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php +opt/app/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php +opt/app/vendor/symfony/console/Helper/DebugFormatterHelper.php +opt/app/vendor/symfony/console/Helper/FormatterHelper.php +opt/app/vendor/symfony/console/Helper/Helper.php +opt/app/vendor/symfony/console/Helper/HelperInterface.php +opt/app/vendor/symfony/console/Helper/HelperSet.php +opt/app/vendor/symfony/console/Helper/ProcessHelper.php +opt/app/vendor/symfony/console/Helper/QuestionHelper.php +opt/app/vendor/symfony/console/Input/ArgvInput.php +opt/app/vendor/symfony/console/Input/ArrayInput.php +opt/app/vendor/symfony/console/Input/Input.php +opt/app/vendor/symfony/console/Input/InputArgument.php +opt/app/vendor/symfony/console/Input/InputDefinition.php +opt/app/vendor/symfony/console/Input/InputInterface.php +opt/app/vendor/symfony/console/Input/InputOption.php +opt/app/vendor/symfony/console/Output/BufferedOutput.php +opt/app/vendor/symfony/console/Output/ConsoleOutput.php +opt/app/vendor/symfony/console/Output/ConsoleOutputInterface.php +opt/app/vendor/symfony/console/Output/Output.php +opt/app/vendor/symfony/console/Output/OutputInterface.php +opt/app/vendor/symfony/console/Output/StreamOutput.php +opt/app/vendor/symfony/console/Style/OutputStyle.php +opt/app/vendor/symfony/console/Style/StyleInterface.php +opt/app/vendor/symfony/console/Style/SymfonyStyle.php +opt/app/vendor/symfony/debug/Exception/FatalErrorException.php +opt/app/vendor/symfony/debug/Exception/FlattenException.php +opt/app/vendor/symfony/debug/ExceptionHandler.php +opt/app/vendor/symfony/finder/Finder.php +opt/app/vendor/symfony/finder/Glob.php +opt/app/vendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php +opt/app/vendor/symfony/finder/Iterator/FileTypeFilterIterator.php +opt/app/vendor/symfony/finder/Iterator/FilenameFilterIterator.php +opt/app/vendor/symfony/finder/Iterator/FilterIterator.php +opt/app/vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php +opt/app/vendor/symfony/finder/Iterator/PathFilterIterator.php +opt/app/vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php +opt/app/vendor/symfony/finder/SplFileInfo.php +opt/app/vendor/symfony/http-foundation/FileBag.php +opt/app/vendor/symfony/http-foundation/HeaderBag.php +opt/app/vendor/symfony/http-foundation/ParameterBag.php +opt/app/vendor/symfony/http-foundation/Request.php +opt/app/vendor/symfony/http-foundation/Response.php +opt/app/vendor/symfony/http-foundation/ResponseHeaderBag.php +opt/app/vendor/symfony/http-foundation/ServerBag.php +opt/app/vendor/symfony/http-foundation/Session/SessionBagInterface.php +opt/app/vendor/symfony/http-foundation/Session/SessionInterface.php +opt/app/vendor/symfony/http-foundation/Session/Storage/MetadataBag.php +opt/app/vendor/symfony/http-kernel/HttpKernelInterface.php +opt/app/vendor/symfony/polyfill-mbstring/bootstrap.php +opt/app/vendor/symfony/polyfill-php56/bootstrap.php +opt/app/vendor/symfony/process/ExecutableFinder.php +opt/app/vendor/symfony/process/PhpExecutableFinder.php +opt/app/vendor/symfony/process/ProcessUtils.php +opt/app/vendor/symfony/routing/CompiledRoute.php +opt/app/vendor/symfony/routing/Route.php +opt/app/vendor/symfony/routing/RouteCompiler.php +opt/app/vendor/symfony/routing/RouteCompilerInterface.php +opt/app/vendor/symfony/translation/TranslatorInterface.php +opt/app/vendor/symfony/var-dumper/Cloner/AbstractCloner.php +opt/app/vendor/symfony/var-dumper/Cloner/ClonerInterface.php +opt/app/vendor/symfony/var-dumper/Cloner/VarCloner.php +opt/app/vendor/symfony/var-dumper/Resources/functions/dump.php +opt/app/vendor/twig/twig/lib/Twig/BaseNodeVisitor.php +opt/app/vendor/twig/twig/lib/Twig/Cache/Filesystem.php +opt/app/vendor/twig/twig/lib/Twig/CacheInterface.php +opt/app/vendor/twig/twig/lib/Twig/Compiler.php +opt/app/vendor/twig/twig/lib/Twig/CompilerInterface.php +opt/app/vendor/twig/twig/lib/Twig/Environment.php +opt/app/vendor/twig/twig/lib/Twig/ExistsLoaderInterface.php +opt/app/vendor/twig/twig/lib/Twig/ExpressionParser.php +opt/app/vendor/twig/twig/lib/Twig/Extension.php +opt/app/vendor/twig/twig/lib/Twig/Extension/Core.php +opt/app/vendor/twig/twig/lib/Twig/Extension/Debug.php +opt/app/vendor/twig/twig/lib/Twig/Extension/Escaper.php +opt/app/vendor/twig/twig/lib/Twig/Extension/GlobalsInterface.php +opt/app/vendor/twig/twig/lib/Twig/Extension/Optimizer.php +opt/app/vendor/twig/twig/lib/Twig/Extension/Staging.php +opt/app/vendor/twig/twig/lib/Twig/ExtensionInterface.php +opt/app/vendor/twig/twig/lib/Twig/Lexer.php +opt/app/vendor/twig/twig/lib/Twig/LexerInterface.php +opt/app/vendor/twig/twig/lib/Twig/Loader/Array.php +opt/app/vendor/twig/twig/lib/Twig/Loader/Chain.php +opt/app/vendor/twig/twig/lib/Twig/LoaderInterface.php +opt/app/vendor/twig/twig/lib/Twig/Node.php +opt/app/vendor/twig/twig/lib/Twig/Node/Body.php +opt/app/vendor/twig/twig/lib/Twig/Node/Expression.php +opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Array.php +opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Call.php +opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Constant.php +opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Filter.php +opt/app/vendor/twig/twig/lib/Twig/Node/Expression/GetAttr.php +opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Name.php +opt/app/vendor/twig/twig/lib/Twig/Node/If.php +opt/app/vendor/twig/twig/lib/Twig/Node/Include.php +opt/app/vendor/twig/twig/lib/Twig/Node/Module.php +opt/app/vendor/twig/twig/lib/Twig/Node/Print.php +opt/app/vendor/twig/twig/lib/Twig/Node/Text.php +opt/app/vendor/twig/twig/lib/Twig/NodeInterface.php +opt/app/vendor/twig/twig/lib/Twig/NodeOutputInterface.php +opt/app/vendor/twig/twig/lib/Twig/NodeTraverser.php +opt/app/vendor/twig/twig/lib/Twig/NodeVisitor/Escaper.php +opt/app/vendor/twig/twig/lib/Twig/NodeVisitor/Optimizer.php +opt/app/vendor/twig/twig/lib/Twig/NodeVisitor/SafeAnalysis.php +opt/app/vendor/twig/twig/lib/Twig/NodeVisitorInterface.php +opt/app/vendor/twig/twig/lib/Twig/Parser.php +opt/app/vendor/twig/twig/lib/Twig/ParserInterface.php +opt/app/vendor/twig/twig/lib/Twig/SimpleFilter.php +opt/app/vendor/twig/twig/lib/Twig/SimpleFunction.php +opt/app/vendor/twig/twig/lib/Twig/SimpleTest.php +opt/app/vendor/twig/twig/lib/Twig/Source.php +opt/app/vendor/twig/twig/lib/Twig/SourceContextLoaderInterface.php +opt/app/vendor/twig/twig/lib/Twig/Template.php +opt/app/vendor/twig/twig/lib/Twig/TemplateInterface.php +opt/app/vendor/twig/twig/lib/Twig/Token.php +opt/app/vendor/twig/twig/lib/Twig/TokenParser.php +opt/app/vendor/twig/twig/lib/Twig/TokenParser/AutoEscape.php +opt/app/vendor/twig/twig/lib/Twig/TokenParser/Block.php +opt/app/vendor/twig/twig/lib/Twig/TokenParser/Do.php +opt/app/vendor/twig/twig/lib/Twig/TokenParser/Embed.php +opt/app/vendor/twig/twig/lib/Twig/TokenParser/Extends.php +opt/app/vendor/twig/twig/lib/Twig/TokenParser/Filter.php +opt/app/vendor/twig/twig/lib/Twig/TokenParser/Flush.php +opt/app/vendor/twig/twig/lib/Twig/TokenParser/For.php +opt/app/vendor/twig/twig/lib/Twig/TokenParser/From.php +opt/app/vendor/twig/twig/lib/Twig/TokenParser/If.php +opt/app/vendor/twig/twig/lib/Twig/TokenParser/Import.php +opt/app/vendor/twig/twig/lib/Twig/TokenParser/Include.php +opt/app/vendor/twig/twig/lib/Twig/TokenParser/Macro.php +opt/app/vendor/twig/twig/lib/Twig/TokenParser/Set.php +opt/app/vendor/twig/twig/lib/Twig/TokenParser/Spaceless.php +opt/app/vendor/twig/twig/lib/Twig/TokenParser/Use.php +opt/app/vendor/twig/twig/lib/Twig/TokenParser/With.php +opt/app/vendor/twig/twig/lib/Twig/TokenParserBroker.php +opt/app/vendor/twig/twig/lib/Twig/TokenParserBrokerInterface.php +opt/app/vendor/twig/twig/lib/Twig/TokenParserInterface.php +opt/app/vendor/twig/twig/lib/Twig/TokenStream.php +opt/app/vendor/vlucas/phpdotenv/src/Dotenv.php +opt/app/vendor/vlucas/phpdotenv/src/Loader.php +opt/app/vendor/watson/validating/src/Injectors/UniqueInjector.php +opt/app/vendor/watson/validating/src/ValidatingObserver.php +opt/app/vendor/watson/validating/src/ValidatingTrait.php +proc/cpuinfo +sandstorm-http-bridge +sandstorm-http-bridge-config +sandstorm-manifest +usr/bin/my_print_defaults +usr/bin/mysql +usr/bin/mysql_install_db +usr/bin/php +usr/bin/php7.0 +usr/bin/sudo +usr/lib/php/20151012/bcmath.so +usr/lib/php/20151012/calendar.so +usr/lib/php/20151012/ctype.so +usr/lib/php/20151012/curl.so +usr/lib/php/20151012/dom.so +usr/lib/php/20151012/exif.so +usr/lib/php/20151012/fileinfo.so +usr/lib/php/20151012/ftp.so +usr/lib/php/20151012/gettext.so +usr/lib/php/20151012/iconv.so +usr/lib/php/20151012/intl.so +usr/lib/php/20151012/json.so +usr/lib/php/20151012/mbstring.so +usr/lib/php/20151012/mysqli.so +usr/lib/php/20151012/mysqlnd.so +usr/lib/php/20151012/opcache.so +usr/lib/php/20151012/pdo.so +usr/lib/php/20151012/pdo_mysql.so +usr/lib/php/20151012/phar.so +usr/lib/php/20151012/posix.so +usr/lib/php/20151012/readline.so +usr/lib/php/20151012/shmop.so +usr/lib/php/20151012/simplexml.so +usr/lib/php/20151012/sockets.so +usr/lib/php/20151012/sysvmsg.so +usr/lib/php/20151012/sysvsem.so +usr/lib/php/20151012/sysvshm.so +usr/lib/php/20151012/tokenizer.so +usr/lib/php/20151012/wddx.so +usr/lib/php/20151012/xml.so +usr/lib/php/20151012/xmlreader.so +usr/lib/php/20151012/xmlwriter.so +usr/lib/php/20151012/xsl.so +usr/lib/ssl/openssl.cnf +usr/lib/x86_64-linux-gnu/libGeoIP.so.1 +usr/lib/x86_64-linux-gnu/libGeoIP.so.1.6.2 +usr/lib/x86_64-linux-gnu/libX11.so.6 +usr/lib/x86_64-linux-gnu/libX11.so.6.3.0 +usr/lib/x86_64-linux-gnu/libXau.so.6 +usr/lib/x86_64-linux-gnu/libXau.so.6.0.0 +usr/lib/x86_64-linux-gnu/libXdmcp.so.6 +usr/lib/x86_64-linux-gnu/libXdmcp.so.6.0.0 +usr/lib/x86_64-linux-gnu/libXpm.so.4 +usr/lib/x86_64-linux-gnu/libXpm.so.4.11.0 +usr/lib/x86_64-linux-gnu/libapparmor.so.1 +usr/lib/x86_64-linux-gnu/libapparmor.so.1.2.0 +usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0 +usr/lib/x86_64-linux-gnu/libcurl.so.4 +usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0 +usr/lib/x86_64-linux-gnu/libdb-5.3.so +usr/lib/x86_64-linux-gnu/libedit.so.2 +usr/lib/x86_64-linux-gnu/libedit.so.2.0.51 +usr/lib/x86_64-linux-gnu/libexslt.so.0 +usr/lib/x86_64-linux-gnu/libexslt.so.0.8.17 +usr/lib/x86_64-linux-gnu/libffi.so.6 +usr/lib/x86_64-linux-gnu/libffi.so.6.0.2 +usr/lib/x86_64-linux-gnu/libfontconfig.so.1 +usr/lib/x86_64-linux-gnu/libfontconfig.so.1.8.0 +usr/lib/x86_64-linux-gnu/libfreetype.so.6 +usr/lib/x86_64-linux-gnu/libfreetype.so.6.11.1 +usr/lib/x86_64-linux-gnu/libgd.so.3 +usr/lib/x86_64-linux-gnu/libgd.so.3.0.0 +usr/lib/x86_64-linux-gnu/libgmp.so.10 +usr/lib/x86_64-linux-gnu/libgmp.so.10.2.0 +usr/lib/x86_64-linux-gnu/libgnutls-deb0.so.28 +usr/lib/x86_64-linux-gnu/libgnutls-deb0.so.28.41.0 +usr/lib/x86_64-linux-gnu/libgssapi_krb5.so.2 +usr/lib/x86_64-linux-gnu/libgssapi_krb5.so.2.2 +usr/lib/x86_64-linux-gnu/libhogweed.so.2 +usr/lib/x86_64-linux-gnu/libhogweed.so.2.5 +usr/lib/x86_64-linux-gnu/libicudata.so.52 +usr/lib/x86_64-linux-gnu/libicudata.so.52.1 +usr/lib/x86_64-linux-gnu/libicui18n.so.52 +usr/lib/x86_64-linux-gnu/libicui18n.so.52.1 +usr/lib/x86_64-linux-gnu/libicuio.so.52 +usr/lib/x86_64-linux-gnu/libicuio.so.52.1 +usr/lib/x86_64-linux-gnu/libicuuc.so.52 +usr/lib/x86_64-linux-gnu/libicuuc.so.52.1 +usr/lib/x86_64-linux-gnu/libidn.so.11 +usr/lib/x86_64-linux-gnu/libidn.so.11.6.12 +usr/lib/x86_64-linux-gnu/libjbig.so.0 +usr/lib/x86_64-linux-gnu/libjpeg.so.62 +usr/lib/x86_64-linux-gnu/libjpeg.so.62.1.0 +usr/lib/x86_64-linux-gnu/libk5crypto.so.3 +usr/lib/x86_64-linux-gnu/libk5crypto.so.3.1 +usr/lib/x86_64-linux-gnu/libkrb5.so.3 +usr/lib/x86_64-linux-gnu/libkrb5.so.3.3 +usr/lib/x86_64-linux-gnu/libkrb5support.so.0 +usr/lib/x86_64-linux-gnu/libkrb5support.so.0.1 +usr/lib/x86_64-linux-gnu/liblber-2.4.so.2 +usr/lib/x86_64-linux-gnu/liblber-2.4.so.2.10.3 +usr/lib/x86_64-linux-gnu/libldap_r-2.4.so.2 +usr/lib/x86_64-linux-gnu/libldap_r-2.4.so.2.10.3 +usr/lib/x86_64-linux-gnu/libmysqlclient.so.18 +usr/lib/x86_64-linux-gnu/libmysqlclient.so.18.0.0 +usr/lib/x86_64-linux-gnu/libnettle.so.4 +usr/lib/x86_64-linux-gnu/libnettle.so.4.7 +usr/lib/x86_64-linux-gnu/libossp-uuid.so.16 +usr/lib/x86_64-linux-gnu/libossp-uuid.so.16.0.22 +usr/lib/x86_64-linux-gnu/libp11-kit.so.0 +usr/lib/x86_64-linux-gnu/libp11-kit.so.0.0.0 +usr/lib/x86_64-linux-gnu/librtmp.so.1 +usr/lib/x86_64-linux-gnu/libsasl2.so.2 +usr/lib/x86_64-linux-gnu/libsasl2.so.2.0.25 +usr/lib/x86_64-linux-gnu/libssh2.so.1 +usr/lib/x86_64-linux-gnu/libssh2.so.1.0.1 +usr/lib/x86_64-linux-gnu/libssl.so.1.0.0 +usr/lib/x86_64-linux-gnu/libstdc++.so.6 +usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.20 +usr/lib/x86_64-linux-gnu/libtasn1.so.6 +usr/lib/x86_64-linux-gnu/libtasn1.so.6.3.2 +usr/lib/x86_64-linux-gnu/libtiff.so.5 +usr/lib/x86_64-linux-gnu/libtiff.so.5.2.0 +usr/lib/x86_64-linux-gnu/libvpx.so.1 +usr/lib/x86_64-linux-gnu/libvpx.so.1.3.0 +usr/lib/x86_64-linux-gnu/libxcb.so.1 +usr/lib/x86_64-linux-gnu/libxcb.so.1.1.0 +usr/lib/x86_64-linux-gnu/libxml2.so.2 +usr/lib/x86_64-linux-gnu/libxml2.so.2.9.1 +usr/lib/x86_64-linux-gnu/libxslt.so.1 +usr/lib/x86_64-linux-gnu/libxslt.so.1.1.28 +usr/sbin/mysqld +usr/sbin/nginx +usr/sbin/php-fpm7.0 +usr/share/mysql/charsets/Index.xml +usr/share/mysql/english/errmsg.sys +usr/share/mysql/fill_help_tables.sql +usr/share/mysql/mysql_system_tables.sql +usr/share/mysql/mysql_system_tables_data.sql diff --git a/.sandstorm/sandstorm-pkgdef.capnp b/.sandstorm/sandstorm-pkgdef.capnp new file mode 100644 index 0000000000..2e53aaf560 --- /dev/null +++ b/.sandstorm/sandstorm-pkgdef.capnp @@ -0,0 +1,247 @@ +@0x9411e6c8b3c8a4b6; + +using Spk = import "/sandstorm/package.capnp"; +# This imports: +# $SANDSTORM_HOME/latest/usr/include/sandstorm/package.capnp +# Check out that file to see the full, documented package definition format. + +const pkgdef :Spk.PackageDefinition = ( + # The package definition. Note that the spk tool looks specifically for the + # "pkgdef" constant. + + id = "uws252ya9mep4t77tevn85333xzsgrpgth8q4y1rhknn1hammw70", + # Your app ID is actually its public key. The private key was placed in + # your keyring. All updates must be signed with the same key. + + manifest = ( + # This manifest is included in your app package to tell Sandstorm + # about your app. + + appTitle = (defaultText = "Firefly III"), + + appVersion = 0, # Increment this for every release. + + appMarketingVersion = (defaultText = "3.4.3"), + # Human-readable representation of appVersion. Should match the way you + # identify versions of your app in documentation and marketing. + + actions = [ + # Define your "new document" handlers here. + ( nounPhrase = (defaultText = "administration"), + command = .myCommand + # The command to run when starting for the first time. (".myCommand" + # is just a constant defined at the bottom of the file.) + ) + ], + + continueCommand = .myCommand, + # This is the command called to start your app back up after it has been + # shut down for inactivity. Here we're using the same command as for + # starting a new instance, but you could use different commands for each + # case. + + metadata = ( + # Data which is not needed specifically to execute the app, but is useful + # for purposes like marketing and display. These fields are documented at + # https://docs.sandstorm.io/en/latest/developing/publishing-apps/#add-required-metadata + # and (in deeper detail) in the sandstorm source code, in the Metadata section of + # https://github.com/sandstorm-io/sandstorm/blob/master/src/sandstorm/package.capnp + icons = ( + # Various icons to represent the app in various contexts. + appGrid = (png = (dpi1x = embed "public/images/logo/firefly-iii-128.png")), + grain = (png = (dpi1x = embed "public/images/logo/firefly-iii-24.png", + dpi2x = embed "public/images/logo/firefly-iii-48.png")), + market = (png = (dpi1x = embed "public/images/logo/firefly-iii-150.png")) + ), + + website = "https://firefly-iii.github.io/", + # This should be the app's main website url. + + codeUrl = "https://github.com/firefly-iii/firefly-iii", + # URL of the app's source code repository, e.g. a GitHub URL. + # Required if you specify a license requiring redistributing code, but optional otherwise. + + license = (openSource = void), + # The license this package is distributed under. See + # https://docs.sandstorm.io/en/latest/developing/publishing-apps/#license + + categories = [productivity], + # A list of categories/genres to which this app belongs, sorted with best fit first. + # See the list of categories at + # https://docs.sandstorm.io/en/latest/developing/publishing-apps/#categories + + author = ( + # Fields relating to the author of this app. + + contactEmail = "thegrumpydictator@gmail.com", + # Email address to contact for any issues with this app. This includes end-user support + # requests as well as app store administrator requests, so it is very important that this be a + # valid address with someone paying attention to it. + + #pgpSignature = embed "path/to/pgp-signature", + # PGP signature attesting responsibility for the app ID. This is a binary-format detached + # signature of the following ASCII message (not including the quotes, no newlines, and + # replacing <app-id> with the standard base-32 text format of the app's ID): + # + # "I am the author of the Sandstorm.io app with the following ID: <app-id>" + # + # You can create a signature file using `gpg` like so: + # + # echo -n "I am the author of the Sandstorm.io app with the following ID: <app-id>" | gpg --sign > pgp-signature + # + # Further details including how to set up GPG and how to use keybase.io can be found + # at https://docs.sandstorm.io/en/latest/developing/publishing-apps/#verify-your-identity + + # upstreamAuthor = "Example App Team", + # Name of the original primary author of this app, if it is different from the person who + # produced the Sandstorm package. Setting this implies that the author connected to the PGP + # signature only "packaged" the app for Sandstorm, rather than developing the app. + # Remove this line if you consider yourself as the author of the app. + ), + + #pgpKeyring = embed "path/to/pgp-keyring", + # A keyring in GPG keyring format containing all public keys needed to verify PGP signatures in + # this manifest (as of this writing, there is only one: `author.pgpSignature`). + # + # To generate a keyring containing just your public key, do: + # + # gpg --export <key-id> > keyring + # + # Where `<key-id>` is a PGP key ID or email address associated with the key. + + #description = (defaultText = embed "path/to/description.md"), + # The app's description in Github-flavored Markdown format, to be displayed e.g. + # in an app store. Note that the Markdown is not permitted to contain HTML nor image tags (but + # you can include a list of screenshots separately). + + shortDescription = (defaultText = "Financial management"), + # A very short (one-to-three words) description of what the app does. For example, + # "Document editor", or "Notetaking", or "Email client". This will be displayed under the app + # title in the grid view in the app market. + + screenshots = [ + # Screenshots to use for marketing purposes. Examples below. + # Sizes are given in device-independent pixels, so if you took these + # screenshots on a Retina-style high DPI screen, divide each dimension by two. + + #(width = 746, height = 795, jpeg = embed "path/to/screenshot-1.jpeg"), + #(width = 640, height = 480, png = embed "path/to/screenshot-2.png"), + ], + #changeLog = (defaultText = embed "path/to/sandstorm-specific/changelog.md"), + # Documents the history of changes in Github-flavored markdown format (with the same restrictions + # as govern `description`). We recommend formatting this with an H1 heading for each version + # followed by a bullet list of changes. + ), + ), + + sourceMap = ( + # Here we defined where to look for files to copy into your package. The + # `spk dev` command actually figures out what files your app needs + # automatically by running it on a FUSE filesystem. So, the mappings + # here are only to tell it where to find files that the app wants. + searchPath = [ + ( sourcePath = "." ), # Search this directory first. + ( sourcePath = "/", # Then search the system root directory. + hidePaths = [ "home", "proc", "sys", + "etc/passwd", "etc/hosts", "etc/host.conf", + "etc/nsswitch.conf", "etc/resolv.conf" ] + # You probably don't want the app pulling files from these places, + # so we hide them. Note that /dev, /var, and /tmp are implicitly + # hidden because Sandstorm itself provides them. + ) + ] + ), + + fileList = "sandstorm-files.list", + # `spk dev` will write a list of all the files your app uses to this file. + # You should review it later, before shipping your app. + + alwaysInclude = [], + # Fill this list with more names of files or directories that should be + # included in your package, even if not listed in sandstorm-files.list. + # Use this to force-include stuff that you know you need but which may + # not have been detected as a dependency during `spk dev`. If you list + # a directory here, its entire contents will be included recursively. + + #bridgeConfig = ( + # # Used for integrating permissions and roles into the Sandstorm shell + # # and for sandstorm-http-bridge to pass to your app. + # # Uncomment this block and adjust the permissions and roles to make + # # sense for your app. + # # For more information, see high-level documentation at + # # https://docs.sandstorm.io/en/latest/developing/auth/ + # # and advanced details in the "BridgeConfig" section of + # # https://github.com/sandstorm-io/sandstorm/blob/master/src/sandstorm/package.capnp + # viewInfo = ( + # # For details on the viewInfo field, consult "ViewInfo" in + # # https://github.com/sandstorm-io/sandstorm/blob/master/src/sandstorm/grain.capnp + # + # permissions = [ + # # Permissions which a user may or may not possess. A user's current + # # permissions are passed to the app as a comma-separated list of `name` + # # fields in the X-Sandstorm-Permissions header with each request. + # # + # # IMPORTANT: only ever append to this list! Reordering or removing fields + # # will change behavior and permissions for existing grains! To deprecate a + # # permission, or for more information, see "PermissionDef" in + # # https://github.com/sandstorm-io/sandstorm/blob/master/src/sandstorm/grain.capnp + # ( + # name = "editor", + # # Name of the permission, used as an identifier for the permission in cases where string + # # names are preferred. Used in sandstorm-http-bridge's X-Sandstorm-Permissions HTTP header. + # + # title = (defaultText = "editor"), + # # Display name of the permission, e.g. to display in a checklist of permissions + # # that may be assigned when sharing. + # + # description = (defaultText = "grants ability to modify data"), + # # Prose describing what this role means, suitable for a tool tip or similar help text. + # ), + # ], + # roles = [ + # # Roles are logical collections of permissions. For instance, your app may have + # # a "viewer" role and an "editor" role + # ( + # title = (defaultText = "editor"), + # # Name of the role. Shown in the Sandstorm UI to indicate which users have which roles. + # + # permissions = [true], + # # An array indicating which permissions this role carries. + # # It should be the same length as the permissions array in + # # viewInfo, and the order of the lists must match. + # + # verbPhrase = (defaultText = "can make changes to the document"), + # # Brief explanatory text to show in the sharing UI indicating + # # what a user assigned this role will be able to do with the grain. + # + # description = (defaultText = "editors may view all site data and change settings."), + # # Prose describing what this role means, suitable for a tool tip or similar help text. + # ), + # ( + # title = (defaultText = "viewer"), + # permissions = [false], + # verbPhrase = (defaultText = "can view the document"), + # description = (defaultText = "viewers may view what other users have written."), + # ), + # ], + # ), + # #apiPath = "/api", + # # Apps can export an API to the world. The API is to be used primarily by Javascript + # # code and native apps, so it can't serve out regular HTML to browsers. If a request + # # comes in to your app's API, sandstorm-http-bridge will prefix the request's path with + # # this string, if specified. + #), +); + +const myCommand :Spk.Manifest.Command = ( + # Here we define the command used to start up your server. + argv = ["/sandstorm-http-bridge", "8000", "--", "/opt/app/.sandstorm/launcher.sh"], + environ = [ + # Note that this defines the *entire* environment seen by your app. + (key = "PATH", value = "/usr/local/bin:/usr/bin:/bin"), + (key = "SANDSTORM", value = "1"), + # Export SANDSTORM=1 into the environment, so that apps running within Sandstorm + # can detect if $SANDSTORM="1" at runtime, switching UI and/or backend to use + # the app's Sandstorm-specific integration code. + ] +); diff --git a/.sandstorm/service-config/mime.types b/.sandstorm/service-config/mime.types new file mode 100644 index 0000000000..89be9a4cd6 --- /dev/null +++ b/.sandstorm/service-config/mime.types @@ -0,0 +1,89 @@ + +types { + text/html html htm shtml; + text/css css; + text/xml xml; + image/gif gif; + image/jpeg jpeg jpg; + application/javascript js; + application/atom+xml atom; + application/rss+xml rss; + + text/mathml mml; + text/plain txt; + text/vnd.sun.j2me.app-descriptor jad; + text/vnd.wap.wml wml; + text/x-component htc; + + image/png png; + image/tiff tif tiff; + image/vnd.wap.wbmp wbmp; + image/x-icon ico; + image/x-jng jng; + image/x-ms-bmp bmp; + image/svg+xml svg svgz; + image/webp webp; + + application/font-woff woff; + application/java-archive jar war ear; + application/json json; + application/mac-binhex40 hqx; + application/msword doc; + application/pdf pdf; + application/postscript ps eps ai; + application/rtf rtf; + application/vnd.apple.mpegurl m3u8; + application/vnd.ms-excel xls; + application/vnd.ms-fontobject eot; + application/vnd.ms-powerpoint ppt; + application/vnd.wap.wmlc wmlc; + application/vnd.google-earth.kml+xml kml; + application/vnd.google-earth.kmz kmz; + application/x-7z-compressed 7z; + application/x-cocoa cco; + application/x-java-archive-diff jardiff; + application/x-java-jnlp-file jnlp; + application/x-makeself run; + application/x-perl pl pm; + application/x-pilot prc pdb; + application/x-rar-compressed rar; + application/x-redhat-package-manager rpm; + application/x-sea sea; + application/x-shockwave-flash swf; + application/x-stuffit sit; + application/x-tcl tcl tk; + application/x-x509-ca-cert der pem crt; + application/x-xpinstall xpi; + application/xhtml+xml xhtml; + application/xspf+xml xspf; + application/zip zip; + + application/octet-stream bin exe dll; + application/octet-stream deb; + application/octet-stream dmg; + application/octet-stream iso img; + application/octet-stream msi msp msm; + + application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; + application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; + + audio/midi mid midi kar; + audio/mpeg mp3; + audio/ogg ogg; + audio/x-m4a m4a; + audio/x-realaudio ra; + + video/3gpp 3gpp 3gp; + video/mp2t ts; + video/mp4 mp4; + video/mpeg mpeg mpg; + video/quicktime mov; + video/webm webm; + video/x-flv flv; + video/x-m4v m4v; + video/x-mng mng; + video/x-ms-asf asx asf; + video/x-ms-wmv wmv; + video/x-msvideo avi; +} diff --git a/.sandstorm/service-config/nginx.conf b/.sandstorm/service-config/nginx.conf new file mode 100644 index 0000000000..b63ddaae07 --- /dev/null +++ b/.sandstorm/service-config/nginx.conf @@ -0,0 +1,87 @@ +worker_processes 4; +pid /var/run/nginx.pid; + +events { + worker_connections 768; + # multi_accept on; +} + +http { + # Basic Settings + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + # server_names_hash_bucket_size 64; + server_tokens off; + server_name_in_redirect off; + + include mime.types; + default_type application/octet-stream; + + # Logging + access_log off; + error_log stderr; + + # Prevent nginx from adding compression; this interacts badly with Sandstorm + # WebSession due to https://github.com/sandstorm-io/sandstorm/issues/289 + gzip off; + + # Trust the sandstorm-http-bridge's X-Forwarded-Proto. + map $http_x_forwarded_proto $fe_https { + default ""; + https on; + } + + server { + listen 8000 default_server; + listen [::]:8000 default_server ipv6only=on; + + # Allow arbitrarily large bodies - Sandstorm can handle them, and requests + # are authenticated already, so there's no reason for apps to add additional + # limits by default. + client_max_body_size 0; + + server_name localhost; + root /opt/app/public; + location / { + index index.php; + try_files $uri $uri/ /index.php?$query_string; + autoindex on; + sendfile off; + } + location ~ \.php$ { + try_files $uri =404; + fastcgi_pass unix:/var/run/php7.0-fpm.sock; + fastcgi_index index.php; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + + + fastcgi_param QUERY_STRING $query_string; + fastcgi_param REQUEST_METHOD $request_method; + fastcgi_param CONTENT_TYPE $content_type; + fastcgi_param CONTENT_LENGTH $content_length; + + fastcgi_param SCRIPT_NAME $fastcgi_script_name; + fastcgi_param REQUEST_URI $request_uri; + fastcgi_param DOCUMENT_URI $document_uri; + fastcgi_param DOCUMENT_ROOT $document_root; + fastcgi_param SERVER_PROTOCOL $server_protocol; + fastcgi_param HTTPS $fe_https if_not_empty; + + fastcgi_param GATEWAY_INTERFACE CGI/1.1; + fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; + + fastcgi_param REMOTE_ADDR $remote_addr; + fastcgi_param REMOTE_PORT $remote_port; + fastcgi_param SERVER_ADDR $server_addr; + fastcgi_param SERVER_PORT $server_port; + fastcgi_param SERVER_NAME $server_name; + + # PHP only, required if PHP was built with --enable-force-cgi-redirect + #fastcgi_param REDIRECT_STATUS 200; + } + } +} diff --git a/.sandstorm/setup.sh b/.sandstorm/setup.sh new file mode 100755 index 0000000000..9af16630f0 --- /dev/null +++ b/.sandstorm/setup.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +# When you change this file, you must take manual action. Read this doc: +# - https://docs.sandstorm.io/en/latest/vagrant-spk/customizing/#setupsh + +set -euo pipefail + +export DEBIAN_FRONTEND=noninteractive + +# install packages so we can install apt-add-repository. +apt-get update +apt-get install -y python-software-properties software-properties-common + +# actually add repository +apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E9C74FEEA2098A6E +add-apt-repository "deb http://packages.dotdeb.org jessie all" + +# install packages. +apt-get update +apt-get install -y nginx php7.0-fpm php7.0-mysql php7.0-cli php7.0-curl git php7.0-dev php7.0-intl php7.0-dom php7.0-mbstring php7.0-bcmath mysql-server +service nginx stop +service php7.0-fpm stop +service mysql stop +systemctl disable nginx +systemctl disable php7.0-fpm +systemctl disable mysql +# patch /etc/php/7.0/fpm/pool.d/www.conf to not change uid/gid to www-data +sed --in-place='' \ + --expression='s/^listen.owner = www-data/;listen.owner = www-data/' \ + --expression='s/^listen.group = www-data/;listen.group = www-data/' \ + /etc/php/7.0/fpm/pool.d/www.conf +# patch /etc/php/7.0/fpm/php-fpm.conf to not have a pidfile +sed --in-place='' \ + --expression='s/^pid =/;pid =/' \ + /etc/php/7.0/fpm/php-fpm.conf + +# move sock file to better dir: +sed --in-place='' \ + --expression='s/^listen = \/run\/php\/php7.0-fpm.sock/listen = \/var\/run\/php7.0-fpm.sock/' \ + /etc/php/7.0/fpm/pool.d/www.conf + +# patch /etc/php/7.0/fpm/pool.d/www.conf to no clear environment variables +# so we can pass in SANDSTORM=1 to apps +sed --in-place='' \ + --expression='s/^;clear_env = no/clear_env=no/' \ + /etc/php/7.0/fpm/pool.d/www.conf +# patch mysql conf to not change uid, and to use /var/tmp over /tmp +# for secure-file-priv see https://github.com/sandstorm-io/vagrant-spk/issues/195 +sed --in-place='' \ + --expression='s/^user\t\t= mysql/#user\t\t= mysql/' \ + --expression='s,^tmpdir\t\t= /tmp,tmpdir\t\t= /var/tmp,' \ + --expression='/\[mysqld]/ a\ secure-file-priv = ""\' \ + /etc/mysql/my.cnf +# patch mysql conf to use smaller transaction logs to save disk space +cat <<EOF > /etc/mysql/conf.d/sandstorm.cnf +[mysqld] +# Set the transaction log file to the minimum allowed size to save disk space. +# innodb_log_file_size = 1048576 +# Set the main data file to grow by 1MB at a time, rather than 8MB at a time. +innodb_autoextend_increment = 1 +EOF diff --git a/.sandstorm/stack b/.sandstorm/stack new file mode 100644 index 0000000000..79a9408192 --- /dev/null +++ b/.sandstorm/stack @@ -0,0 +1 @@ +lemp diff --git a/public/images/logo/firefly-iii-128.png b/public/images/logo/firefly-iii-128.png new file mode 100644 index 0000000000000000000000000000000000000000..70de2bc75ab25421558ed44142adccf5c2a6d3e0 GIT binary patch literal 14217 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�_) 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 R{`zyAq^O)om5@Qu{|Bgy$qN7g literal 0 HcmV?d00001 diff --git a/public/images/logo/firefly-iii-150.png b/public/images/logo/firefly-iii-150.png new file mode 100644 index 0000000000000000000000000000000000000000..0d97be47680667d16ca7ca1a50c67e0618189750 GIT binary patch literal 15229 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 literal 0 HcmV?d00001 diff --git a/public/images/logo/firefly-iii-24.png b/public/images/logo/firefly-iii-24.png new file mode 100644 index 0000000000000000000000000000000000000000..a727ae4992f6bdfc119bc874d7fe899361d41ead GIT binary patch literal 2560 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 YO0mhFH|AY2QvZ{YgI1HO1L9Ku0Y{|pm;e9( literal 0 HcmV?d00001 diff --git a/public/images/logo/firefly-iii-48.png b/public/images/logo/firefly-iii-48.png new file mode 100644 index 0000000000000000000000000000000000000000..6f81c5731beacb6ccd2c9babb60aa7699a33595b GIT binary patch literal 4823 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 PUrqWtCfW})9m4((KVD6+ literal 0 HcmV?d00001 From 5e5d4eca4b42d87d5ef138af46a75e5bdd7a8252 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 4 Feb 2017 03:22:19 +0100 Subject: [PATCH 635/709] Update some sandstorm.io files. --- .../app-graphics}/firefly-iii-128.png | Bin .../app-graphics}/firefly-iii-150.png | Bin .../app-graphics}/firefly-iii-24.png | Bin .../app-graphics}/firefly-iii-48.png | Bin .sandstorm/sandstorm-files.list | 64 ++++++++++++++++++ .sandstorm/sandstorm-pkgdef.capnp | 59 +++------------- 6 files changed, 72 insertions(+), 51 deletions(-) rename {public/images/logo => .sandstorm/app-graphics}/firefly-iii-128.png (100%) rename {public/images/logo => .sandstorm/app-graphics}/firefly-iii-150.png (100%) rename {public/images/logo => .sandstorm/app-graphics}/firefly-iii-24.png (100%) rename {public/images/logo => .sandstorm/app-graphics}/firefly-iii-48.png (100%) diff --git a/public/images/logo/firefly-iii-128.png b/.sandstorm/app-graphics/firefly-iii-128.png similarity index 100% rename from public/images/logo/firefly-iii-128.png rename to .sandstorm/app-graphics/firefly-iii-128.png diff --git a/public/images/logo/firefly-iii-150.png b/.sandstorm/app-graphics/firefly-iii-150.png similarity index 100% rename from public/images/logo/firefly-iii-150.png rename to .sandstorm/app-graphics/firefly-iii-150.png diff --git a/public/images/logo/firefly-iii-24.png b/.sandstorm/app-graphics/firefly-iii-24.png similarity index 100% rename from public/images/logo/firefly-iii-24.png rename to .sandstorm/app-graphics/firefly-iii-24.png diff --git a/public/images/logo/firefly-iii-48.png b/.sandstorm/app-graphics/firefly-iii-48.png similarity index 100% rename from public/images/logo/firefly-iii-48.png rename to .sandstorm/app-graphics/firefly-iii-48.png diff --git a/.sandstorm/sandstorm-files.list b/.sandstorm/sandstorm-files.list index db769f8f6b..5f1eacded3 100644 --- a/.sandstorm/sandstorm-files.list +++ b/.sandstorm/sandstorm-files.list @@ -229,9 +229,17 @@ opt/app/app/Console/Commands/UseEncryption.php opt/app/app/Console/Commands/VerifyDatabase.php opt/app/app/Console/Kernel.php opt/app/app/Exceptions/Handler.php +opt/app/app/Http/Controllers/Auth/LoginController.php opt/app/app/Http/Controllers/Controller.php opt/app/app/Http/Controllers/HomeController.php opt/app/app/Http/Kernel.php +opt/app/app/Http/Middleware/Authenticate.php +opt/app/app/Http/Middleware/AuthenticateTwoFactor.php +opt/app/app/Http/Middleware/Binder.php +opt/app/app/Http/Middleware/EncryptCookies.php +opt/app/app/Http/Middleware/Range.php +opt/app/app/Http/Middleware/RedirectIfAuthenticated.php +opt/app/app/Http/Middleware/VerifyCsrfToken.php opt/app/app/Http/breadcrumbs.php opt/app/app/Jobs/Job.php opt/app/app/Jobs/MailError.php @@ -262,6 +270,8 @@ opt/app/app/Providers/RuleGroupServiceProvider.php opt/app/app/Providers/RuleServiceProvider.php opt/app/app/Providers/SearchServiceProvider.php opt/app/app/Providers/TagServiceProvider.php +opt/app/app/Repositories/Account/AccountRepositoryInterface.php +opt/app/app/Support/Domain.php opt/app/app/Support/Facades/FireflyConfig.php opt/app/app/Support/FireflyConfig.php opt/app/app/Support/Models/TransactionJournalSupport.php @@ -271,6 +281,7 @@ opt/app/app/Support/Twig/PiggyBank.php opt/app/app/Support/Twig/Rule.php opt/app/app/Support/Twig/Transaction.php opt/app/app/Support/Twig/Translation.php +opt/app/app/User.php opt/app/app/Validation/FireflyValidator.php opt/app/artisan opt/app/bootstrap/app.php @@ -309,13 +320,24 @@ opt/app/database/seeds/DatabaseSeeder.php opt/app/database/seeds/PermissionSeeder.php opt/app/database/seeds/TransactionCurrencySeeder.php opt/app/database/seeds/TransactionTypeSeeder.php +opt/app/public/css/firefly.css opt/app/public/index.php +opt/app/public/js/ff/guest.js +opt/app/public/js/lib/jquery-3.1.1.min.js +opt/app/public/lib/adminlte/css/AdminLTE.min.css +opt/app/public/lib/bootstrap/css/bootstrap.min.css +opt/app/public/lib/bootstrap/js/bootstrap.min.js +opt/app/public/lib/font-awesome/css/font-awesome.min.css +opt/app/resources/lang/en_US/config.php +opt/app/resources/views/auth/login.twig opt/app/resources/views/emails/error-html.twig opt/app/resources/views/emails/error-text.twig opt/app/resources/views/emails/footer-html.twig opt/app/resources/views/emails/footer-text.twig opt/app/resources/views/emails/header-html.twig opt/app/resources/views/emails/header-text.twig +opt/app/resources/views/layout/guest.twig +opt/app/resources/views/partials/favicons.twig opt/app/routes/api.php opt/app/routes/console.php opt/app/routes/web.php @@ -389,11 +411,13 @@ opt/app/vendor/laravel/framework/src/Illuminate/Auth/Access/Gate.php opt/app/vendor/laravel/framework/src/Illuminate/Auth/Access/HandlesAuthorization.php opt/app/vendor/laravel/framework/src/Illuminate/Auth/AuthManager.php opt/app/vendor/laravel/framework/src/Illuminate/Auth/AuthServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Auth/Authenticatable.php opt/app/vendor/laravel/framework/src/Illuminate/Auth/Console/ClearResetsCommand.php opt/app/vendor/laravel/framework/src/Illuminate/Auth/Console/MakeAuthCommand.php opt/app/vendor/laravel/framework/src/Illuminate/Auth/CreatesUserProviders.php opt/app/vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php opt/app/vendor/laravel/framework/src/Illuminate/Auth/GuardHelpers.php +opt/app/vendor/laravel/framework/src/Illuminate/Auth/Passwords/CanResetPassword.php opt/app/vendor/laravel/framework/src/Illuminate/Auth/Passwords/PasswordResetServiceProvider.php opt/app/vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php opt/app/vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastServiceProvider.php @@ -405,6 +429,8 @@ opt/app/vendor/laravel/framework/src/Illuminate/Cache/CacheServiceProvider.php opt/app/vendor/laravel/framework/src/Illuminate/Cache/Console/CacheTableCommand.php opt/app/vendor/laravel/framework/src/Illuminate/Cache/Console/ClearCommand.php opt/app/vendor/laravel/framework/src/Illuminate/Cache/Events/CacheMissed.php +opt/app/vendor/laravel/framework/src/Illuminate/Cache/Events/KeyForgotten.php +opt/app/vendor/laravel/framework/src/Illuminate/Cache/Events/KeyWritten.php opt/app/vendor/laravel/framework/src/Illuminate/Cache/FileStore.php opt/app/vendor/laravel/framework/src/Illuminate/Cache/Repository.php opt/app/vendor/laravel/framework/src/Illuminate/Cache/RetrievesMultipleKeys.php @@ -421,7 +447,10 @@ opt/app/vendor/laravel/framework/src/Illuminate/Console/ScheduleServiceProvider. opt/app/vendor/laravel/framework/src/Illuminate/Console/Scheduling/Schedule.php opt/app/vendor/laravel/framework/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php opt/app/vendor/laravel/framework/src/Illuminate/Container/Container.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Auth/Access/Authorizable.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Auth/Access/Gate.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Auth/Authenticatable.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Auth/CanResetPassword.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Auth/Factory.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Auth/Guard.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Auth/StatefulGuard.php @@ -439,6 +468,7 @@ opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Container/Container.ph opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Cookie/Factory.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Cookie/QueueingFactory.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Debug/ExceptionHandler.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Encryption/DecryptException.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Encryption/Encrypter.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Events/Dispatcher.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Filesystem/FileNotFoundException.php @@ -454,6 +484,7 @@ opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/Factory.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/Job.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/Monitor.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/Queue.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/QueueableCollection.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/QueueableEntity.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/ShouldQueue.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Routing/Registrar.php @@ -472,6 +503,8 @@ opt/app/vendor/laravel/framework/src/Illuminate/Contracts/View/Factory.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/View/View.php opt/app/vendor/laravel/framework/src/Illuminate/Cookie/CookieJar.php opt/app/vendor/laravel/framework/src/Illuminate/Cookie/CookieServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php +opt/app/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php opt/app/vendor/laravel/framework/src/Illuminate/Database/Connection.php opt/app/vendor/laravel/framework/src/Illuminate/Database/ConnectionInterface.php opt/app/vendor/laravel/framework/src/Illuminate/Database/ConnectionResolverInterface.php @@ -494,6 +527,7 @@ opt/app/vendor/laravel/framework/src/Illuminate/Database/DatabaseServiceProvider opt/app/vendor/laravel/framework/src/Illuminate/Database/DetectsDeadlocks.php opt/app/vendor/laravel/framework/src/Illuminate/Database/DetectsLostConnections.php opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Collection.php opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Scope.php opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/SoftDeletes.php @@ -528,7 +562,12 @@ opt/app/vendor/laravel/framework/src/Illuminate/Filesystem/Filesystem.php opt/app/vendor/laravel/framework/src/Illuminate/Filesystem/FilesystemServiceProvider.php opt/app/vendor/laravel/framework/src/Illuminate/Foundation/AliasLoader.php opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Application.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Auth/Access/Authorizable.php opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Auth/Access/AuthorizesRequests.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Auth/RedirectsUsers.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Auth/ThrottlesLogins.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Auth/User.php opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/BootProviders.php opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/ConfigureLogging.php opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/DetectEnvironment.php @@ -573,6 +612,7 @@ opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.ph opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/FormRequest.php opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/CheckForMaintenanceMode.php +opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php opt/app/vendor/laravel/framework/src/Illuminate/Foundation/ProviderRepository.php opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Providers/ComposerServiceProvider.php @@ -585,6 +625,7 @@ opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Validation/ValidatesR opt/app/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php opt/app/vendor/laravel/framework/src/Illuminate/Hashing/BcryptHasher.php opt/app/vendor/laravel/framework/src/Illuminate/Hashing/HashServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Http/RedirectResponse.php opt/app/vendor/laravel/framework/src/Illuminate/Http/Request.php opt/app/vendor/laravel/framework/src/Illuminate/Http/Response.php opt/app/vendor/laravel/framework/src/Illuminate/Http/ResponseTrait.php @@ -595,7 +636,10 @@ opt/app/vendor/laravel/framework/src/Illuminate/Mail/Mailer.php opt/app/vendor/laravel/framework/src/Illuminate/Mail/Message.php opt/app/vendor/laravel/framework/src/Illuminate/Mail/TransportManager.php opt/app/vendor/laravel/framework/src/Illuminate/Notifications/Console/NotificationTableCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Notifications/HasDatabaseNotifications.php +opt/app/vendor/laravel/framework/src/Illuminate/Notifications/Notifiable.php opt/app/vendor/laravel/framework/src/Illuminate/Notifications/NotificationServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Notifications/RoutesNotifications.php opt/app/vendor/laravel/framework/src/Illuminate/Pagination/AbstractPaginator.php opt/app/vendor/laravel/framework/src/Illuminate/Pagination/PaginationServiceProvider.php opt/app/vendor/laravel/framework/src/Illuminate/Pagination/Paginator.php @@ -632,22 +676,27 @@ opt/app/vendor/laravel/framework/src/Illuminate/Routing/Console/ControllerMakeCo opt/app/vendor/laravel/framework/src/Illuminate/Routing/Console/MiddlewareMakeCommand.php opt/app/vendor/laravel/framework/src/Illuminate/Routing/Controller.php opt/app/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/ControllerMiddlewareOptions.php opt/app/vendor/laravel/framework/src/Illuminate/Routing/Events/RouteMatched.php opt/app/vendor/laravel/framework/src/Illuminate/Routing/Matching/HostValidator.php opt/app/vendor/laravel/framework/src/Illuminate/Routing/Matching/MethodValidator.php opt/app/vendor/laravel/framework/src/Illuminate/Routing/Matching/SchemeValidator.php opt/app/vendor/laravel/framework/src/Illuminate/Routing/Matching/UriValidator.php opt/app/vendor/laravel/framework/src/Illuminate/Routing/Matching/ValidatorInterface.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php opt/app/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/Redirector.php opt/app/vendor/laravel/framework/src/Illuminate/Routing/Route.php opt/app/vendor/laravel/framework/src/Illuminate/Routing/RouteCollection.php opt/app/vendor/laravel/framework/src/Illuminate/Routing/RouteDependencyResolverTrait.php opt/app/vendor/laravel/framework/src/Illuminate/Routing/Router.php opt/app/vendor/laravel/framework/src/Illuminate/Routing/RoutingServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/SortedMiddleware.php opt/app/vendor/laravel/framework/src/Illuminate/Routing/UrlGenerator.php opt/app/vendor/laravel/framework/src/Illuminate/Session/Console/SessionTableCommand.php opt/app/vendor/laravel/framework/src/Illuminate/Session/EncryptedStore.php opt/app/vendor/laravel/framework/src/Illuminate/Session/FileSessionHandler.php +opt/app/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php opt/app/vendor/laravel/framework/src/Illuminate/Session/SessionInterface.php opt/app/vendor/laravel/framework/src/Illuminate/Session/SessionManager.php opt/app/vendor/laravel/framework/src/Illuminate/Session/SessionServiceProvider.php @@ -656,7 +705,9 @@ opt/app/vendor/laravel/framework/src/Illuminate/Support/AggregateServiceProvider opt/app/vendor/laravel/framework/src/Illuminate/Support/Arr.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Collection.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Composer.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Auth.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Cache.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Config.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/DB.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Event.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php @@ -666,6 +717,7 @@ opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Mail.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Request.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Route.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Schema.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/URL.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Validator.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/View.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Fluent.php @@ -676,6 +728,7 @@ opt/app/vendor/laravel/framework/src/Illuminate/Support/Pluralizer.php opt/app/vendor/laravel/framework/src/Illuminate/Support/ServiceProvider.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Str.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Traits/Macroable.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/ViewErrorBag.php opt/app/vendor/laravel/framework/src/Illuminate/Support/helpers.php opt/app/vendor/laravel/framework/src/Illuminate/Translation/FileLoader.php opt/app/vendor/laravel/framework/src/Illuminate/Translation/LoaderInterface.php @@ -694,6 +747,7 @@ opt/app/vendor/laravel/framework/src/Illuminate/View/Engines/EngineResolver.php opt/app/vendor/laravel/framework/src/Illuminate/View/Engines/PhpEngine.php opt/app/vendor/laravel/framework/src/Illuminate/View/Factory.php opt/app/vendor/laravel/framework/src/Illuminate/View/FileViewFinder.php +opt/app/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php opt/app/vendor/laravel/framework/src/Illuminate/View/View.php opt/app/vendor/laravel/framework/src/Illuminate/View/ViewFinderInterface.php opt/app/vendor/laravel/framework/src/Illuminate/View/ViewServiceProvider.php @@ -868,9 +922,11 @@ opt/app/vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php opt/app/vendor/symfony/finder/Iterator/PathFilterIterator.php opt/app/vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php opt/app/vendor/symfony/finder/SplFileInfo.php +opt/app/vendor/symfony/http-foundation/Cookie.php opt/app/vendor/symfony/http-foundation/FileBag.php opt/app/vendor/symfony/http-foundation/HeaderBag.php opt/app/vendor/symfony/http-foundation/ParameterBag.php +opt/app/vendor/symfony/http-foundation/RedirectResponse.php opt/app/vendor/symfony/http-foundation/Request.php opt/app/vendor/symfony/http-foundation/Response.php opt/app/vendor/symfony/http-foundation/ResponseHeaderBag.php @@ -915,14 +971,22 @@ opt/app/vendor/twig/twig/lib/Twig/Loader/Array.php opt/app/vendor/twig/twig/lib/Twig/Loader/Chain.php opt/app/vendor/twig/twig/lib/Twig/LoaderInterface.php opt/app/vendor/twig/twig/lib/Twig/Node.php +opt/app/vendor/twig/twig/lib/Twig/Node/Block.php +opt/app/vendor/twig/twig/lib/Twig/Node/BlockReference.php opt/app/vendor/twig/twig/lib/Twig/Node/Body.php opt/app/vendor/twig/twig/lib/Twig/Node/Expression.php opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Array.php +opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary.php +opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/And.php +opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Call.php opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Constant.php opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Filter.php +opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Function.php opt/app/vendor/twig/twig/lib/Twig/Node/Expression/GetAttr.php opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Name.php +opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Unary.php +opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Not.php opt/app/vendor/twig/twig/lib/Twig/Node/If.php opt/app/vendor/twig/twig/lib/Twig/Node/Include.php opt/app/vendor/twig/twig/lib/Twig/Node/Module.php diff --git a/.sandstorm/sandstorm-pkgdef.capnp b/.sandstorm/sandstorm-pkgdef.capnp index 2e53aaf560..5bb401c185 100644 --- a/.sandstorm/sandstorm-pkgdef.capnp +++ b/.sandstorm/sandstorm-pkgdef.capnp @@ -14,17 +14,9 @@ const pkgdef :Spk.PackageDefinition = ( # your keyring. All updates must be signed with the same key. manifest = ( - # This manifest is included in your app package to tell Sandstorm - # about your app. - appTitle = (defaultText = "Firefly III"), - - appVersion = 0, # Increment this for every release. - + appVersion = 0, appMarketingVersion = (defaultText = "3.4.3"), - # Human-readable representation of appVersion. Should match the way you - # identify versions of your app in documentation and marketing. - actions = [ # Define your "new document" handlers here. ( nounPhrase = (defaultText = "administration"), @@ -41,27 +33,16 @@ const pkgdef :Spk.PackageDefinition = ( # case. metadata = ( - # Data which is not needed specifically to execute the app, but is useful - # for purposes like marketing and display. These fields are documented at - # https://docs.sandstorm.io/en/latest/developing/publishing-apps/#add-required-metadata - # and (in deeper detail) in the sandstorm source code, in the Metadata section of - # https://github.com/sandstorm-io/sandstorm/blob/master/src/sandstorm/package.capnp - icons = ( - # Various icons to represent the app in various contexts. - appGrid = (png = (dpi1x = embed "public/images/logo/firefly-iii-128.png")), - grain = (png = (dpi1x = embed "public/images/logo/firefly-iii-24.png", - dpi2x = embed "public/images/logo/firefly-iii-48.png")), - market = (png = (dpi1x = embed "public/images/logo/firefly-iii-150.png")) + icons = ( + appGrid = (png = (dpi1x = embed "app-graphics/firefly-iii-128.png")), + grain = (png = (dpi1x = embed "app-graphics/firefly-iii-24.png", + dpi2x = embed "app-graphics/firefly-iii-48.png")), + market = (png = (dpi1x = embed "app-graphics/firefly-iii-150.png")) ), website = "https://firefly-iii.github.io/", - # This should be the app's main website url. - codeUrl = "https://github.com/firefly-iii/firefly-iii", - # URL of the app's source code repository, e.g. a GitHub URL. - # Required if you specify a license requiring redistributing code, but optional otherwise. - - license = (openSource = void), + license = (openSource = mit), # The license this package is distributed under. See # https://docs.sandstorm.io/en/latest/developing/publishing-apps/#license @@ -71,32 +52,8 @@ const pkgdef :Spk.PackageDefinition = ( # https://docs.sandstorm.io/en/latest/developing/publishing-apps/#categories author = ( - # Fields relating to the author of this app. - contactEmail = "thegrumpydictator@gmail.com", - # Email address to contact for any issues with this app. This includes end-user support - # requests as well as app store administrator requests, so it is very important that this be a - # valid address with someone paying attention to it. - - #pgpSignature = embed "path/to/pgp-signature", - # PGP signature attesting responsibility for the app ID. This is a binary-format detached - # signature of the following ASCII message (not including the quotes, no newlines, and - # replacing <app-id> with the standard base-32 text format of the app's ID): - # - # "I am the author of the Sandstorm.io app with the following ID: <app-id>" - # - # You can create a signature file using `gpg` like so: - # - # echo -n "I am the author of the Sandstorm.io app with the following ID: <app-id>" | gpg --sign > pgp-signature - # - # Further details including how to set up GPG and how to use keybase.io can be found - # at https://docs.sandstorm.io/en/latest/developing/publishing-apps/#verify-your-identity - - # upstreamAuthor = "Example App Team", - # Name of the original primary author of this app, if it is different from the person who - # produced the Sandstorm package. Setting this implies that the author connected to the PGP - # signature only "packaged" the app for Sandstorm, rather than developing the app. - # Remove this line if you consider yourself as the author of the app. + upstreamAuthor = "James Cole", ), #pgpKeyring = embed "path/to/pgp-keyring", From dc348a72c8db9f9b5d50ba5c5080cac421deb531 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 4 Feb 2017 08:42:06 +0100 Subject: [PATCH 636/709] Various new scripts to improve sandstorm.io experience. --- .sandstorm/build.sh | 2 +- .sandstorm/changelog.md | 3 + .sandstorm/description.md | 3 + .sandstorm/launcher.sh | 4 - .sandstorm/pgp-keyring | Bin 0 -> 2775 bytes .sandstorm/pgp-signature | Bin 0 -> 688 bytes .sandstorm/sandstorm-files.list | 141 ++++++++++++++++++++++++++++++ .sandstorm/sandstorm-pkgdef.capnp | 38 +++----- app/Http/Kernel.php | 22 ++--- app/Http/Middleware/Sandstorm.php | 72 +++++++++++++++ 10 files changed, 240 insertions(+), 45 deletions(-) create mode 100644 .sandstorm/changelog.md create mode 100644 .sandstorm/description.md create mode 100644 .sandstorm/pgp-keyring create mode 100644 .sandstorm/pgp-signature create mode 100644 app/Http/Middleware/Sandstorm.php diff --git a/.sandstorm/build.sh b/.sandstorm/build.sh index a72ac8cb24..93daf1ba86 100755 --- a/.sandstorm/build.sh +++ b/.sandstorm/build.sh @@ -1,6 +1,6 @@ #!/bin/bash # Checks if there's a composer.json, and if so, installs/runs composer. -# Only runs when we connect the app to sandstorm (so once). +# This script only runs once, when the app connects to sandstorm. set -euo pipefail diff --git a/.sandstorm/changelog.md b/.sandstorm/changelog.md new file mode 100644 index 0000000000..58249de976 --- /dev/null +++ b/.sandstorm/changelog.md @@ -0,0 +1,3 @@ +# 3.4.3 + +* Initial release on Sandstorm.io \ No newline at end of file diff --git a/.sandstorm/description.md b/.sandstorm/description.md new file mode 100644 index 0000000000..e9f6874f3f --- /dev/null +++ b/.sandstorm/description.md @@ -0,0 +1,3 @@ +"Firefly III" is a financial manager. It can help you keep track of expenses, income, budgets and everything in between. It even supports credit cards, shared household accounts and savings accounts! It’s pretty fancy. You should use it to save and organise money. + +Firefly works on the principle that if you know where you’re money is going, you can stop it from going there. \ No newline at end of file diff --git a/.sandstorm/launcher.sh b/.sandstorm/launcher.sh index dba084dfa2..790f4e9711 100755 --- a/.sandstorm/launcher.sh +++ b/.sandstorm/launcher.sh @@ -54,10 +54,6 @@ echo "Installing database.." echo "CREATE DATABASE IF NOT EXISTS firefly; GRANT ALL on firefly.* TO 'firefly'@'localhost' IDENTIFIED BY 'firefly';" | mysql -uroot echo "Done!" -#echo "Generate key..." -#php /opt/app/artisan key:generate -#echo "Done!" - echo "Migrating..." php /opt/app/artisan migrate:refresh --seed --force echo "Done!" diff --git a/.sandstorm/pgp-keyring b/.sandstorm/pgp-keyring new file mode 100644 index 0000000000000000000000000000000000000000..b0dec3c169ac06a141e53541dd72b2819535ed20 GIT binary patch literal 2775 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 literal 0 HcmV?d00001 diff --git a/.sandstorm/pgp-signature b/.sandstorm/pgp-signature new file mode 100644 index 0000000000000000000000000000000000000000..82e5fc8a52cd88aa1fa315cd9492a83a0966c0e7 GIT binary patch literal 688 zcmV;h0#E&;0RgE3R{f9-0{{vE!D(UURp(1y0n2$}09ciTf=M7@Z6I`LWgua7bZBpK zAa7<MbZBKDQ(<mob98TVZ7ykVAYpKDAa`kWXdrZGWgup6Y;13LX>MmANklpzb$4?z zH8Oc&Ic;TdG;}vNbY*sKI5jgfGkAJ)XL4|7bZ9tnG<h*{Xlrh6F=%0JZFe^?i2@u1 z009aB1_c6Gm4t!-3JDOwX<_D7=SyCmM-TseIL9kqrgFpk;cM!6rKi}t*jBHu_rWBq z?YD3$M&sfCj^zcXbb-47p-H@yluXjs%80Y>CYwWkNumO%Vf9eT%Wi>WfXH5FkNXS; zD7F~l1TS%S<8I5LKAQZ;^(RI=H#c|=vco>Ae+X-SaWO&7)A|UxSUe%Mp<9(~QQ2Q- zvGKz`9^?9^dJDhJAR=266y+E;5($FhfP63bq<J9T=v$<?c3~B?X;)h(iVY;FmyTZM zq^ero{5xip3y?<~akXRg^||-ccvTP8bq(PMG;l;gev<7w$vu7nh!qwgaQ(*k?3%^A zh?TA&O=_|$=MWsA9p*7jNf|_dj*oLiQG>~HWuU+8A#9;gYFB6^iIvv)%D<PWCdyzA z(DF@DS%a~@nVXba&vY<0FjXuRypLgjf%y&c-ZsCzG<+K@xE-c&St<W-2R@<|{+ixz z?d4-<L}^Q^c1W#LdZc`5i=H(eFHSzulct~3KtNudRTxF^)%eI635J~t^KRJ1Fm~#; zz35|3B`Jref>yL18w=!MTeGdCHLTqrrzY2m>Gq8NM+e7z<|CRF-bnxl-KiloD(q4? z|BYisaz?>WKvdxfx|KBu7I^<U(2kD^cqBQ8VQ>C04#H%>-PLbI&fqn@%zziph)3IU W?~QZ|VG=>*yb+B=oyMWlv3N`v@H)Z( literal 0 HcmV?d00001 diff --git a/.sandstorm/sandstorm-files.list b/.sandstorm/sandstorm-files.list index 5f1eacded3..7d8d51d516 100644 --- a/.sandstorm/sandstorm-files.list +++ b/.sandstorm/sandstorm-files.list @@ -25,6 +25,7 @@ etc/hosts.allow etc/hosts.deny etc/inputrc etc/ld.so.cache +etc/locale.alias etc/localtime etc/mysql/conf.d etc/mysql/conf.d/mysqld_safe_syslog.cnf @@ -229,9 +230,21 @@ opt/app/app/Console/Commands/UseEncryption.php opt/app/app/Console/Commands/VerifyDatabase.php opt/app/app/Console/Kernel.php opt/app/app/Exceptions/Handler.php +opt/app/app/Generator/Chart/Basic/ChartJsGenerator.php +opt/app/app/Generator/Chart/Basic/GeneratorInterface.php +opt/app/app/Helpers/Collector/JournalCollector.php +opt/app/app/Helpers/Collector/JournalCollectorInterface.php opt/app/app/Http/Controllers/Auth/LoginController.php +opt/app/app/Http/Controllers/BudgetController.php +opt/app/app/Http/Controllers/Chart/AccountController.php +opt/app/app/Http/Controllers/Chart/BudgetController.php +opt/app/app/Http/Controllers/Chart/CategoryController.php opt/app/app/Http/Controllers/Controller.php opt/app/app/Http/Controllers/HomeController.php +opt/app/app/Http/Controllers/JavascriptController.php +opt/app/app/Http/Controllers/JsonController.php +opt/app/app/Http/Controllers/NewUserController.php +opt/app/app/Http/Controllers/ProfileController.php opt/app/app/Http/Kernel.php opt/app/app/Http/Middleware/Authenticate.php opt/app/app/Http/Middleware/AuthenticateTwoFactor.php @@ -239,15 +252,27 @@ opt/app/app/Http/Middleware/Binder.php opt/app/app/Http/Middleware/EncryptCookies.php opt/app/app/Http/Middleware/Range.php opt/app/app/Http/Middleware/RedirectIfAuthenticated.php +opt/app/app/Http/Middleware/Sandstorm.php opt/app/app/Http/Middleware/VerifyCsrfToken.php +opt/app/app/Http/Requests/NewUserFormRequest.php +opt/app/app/Http/Requests/Request.php opt/app/app/Http/breadcrumbs.php opt/app/app/Jobs/Job.php opt/app/app/Jobs/MailError.php opt/app/app/Models/Account.php +opt/app/app/Models/AccountMeta.php opt/app/app/Models/AccountType.php +opt/app/app/Models/AvailableBudget.php +opt/app/app/Models/Bill.php +opt/app/app/Models/Budget.php +opt/app/app/Models/BudgetLimit.php +opt/app/app/Models/Category.php opt/app/app/Models/Configuration.php opt/app/app/Models/PiggyBank.php +opt/app/app/Models/Preference.php opt/app/app/Models/Role.php +opt/app/app/Models/Tag.php +opt/app/app/Models/Transaction.php opt/app/app/Models/TransactionCurrency.php opt/app/app/Models/TransactionJournal.php opt/app/app/Models/TransactionType.php @@ -270,11 +295,34 @@ opt/app/app/Providers/RuleGroupServiceProvider.php opt/app/app/Providers/RuleServiceProvider.php opt/app/app/Providers/SearchServiceProvider.php opt/app/app/Providers/TagServiceProvider.php +opt/app/app/Repositories/Account/AccountRepository.php opt/app/app/Repositories/Account/AccountRepositoryInterface.php +opt/app/app/Repositories/Account/AccountTasker.php +opt/app/app/Repositories/Account/AccountTaskerInterface.php +opt/app/app/Repositories/Bill/BillRepository.php +opt/app/app/Repositories/Bill/BillRepositoryInterface.php +opt/app/app/Repositories/Budget/BudgetRepository.php +opt/app/app/Repositories/Budget/BudgetRepositoryInterface.php +opt/app/app/Repositories/Category/CategoryRepository.php +opt/app/app/Repositories/Category/CategoryRepositoryInterface.php +opt/app/app/Repositories/Journal/JournalRepository.php +opt/app/app/Repositories/Journal/JournalRepositoryInterface.php +opt/app/app/Support/Amount.php +opt/app/app/Support/CacheProperties.php opt/app/app/Support/Domain.php +opt/app/app/Support/ExpandedForm.php +opt/app/app/Support/Facades/Amount.php +opt/app/app/Support/Facades/ExpandedForm.php opt/app/app/Support/Facades/FireflyConfig.php +opt/app/app/Support/Facades/Navigation.php +opt/app/app/Support/Facades/Preferences.php +opt/app/app/Support/Facades/Steam.php opt/app/app/Support/FireflyConfig.php +opt/app/app/Support/Models/TagSupport.php opt/app/app/Support/Models/TransactionJournalSupport.php +opt/app/app/Support/Navigation.php +opt/app/app/Support/Preferences.php +opt/app/app/Support/Steam.php opt/app/app/Support/Twig/General.php opt/app/app/Support/Twig/Journal.php opt/app/app/Support/Twig/PiggyBank.php @@ -320,24 +368,62 @@ opt/app/database/seeds/DatabaseSeeder.php opt/app/database/seeds/PermissionSeeder.php opt/app/database/seeds/TransactionCurrencySeeder.php opt/app/database/seeds/TransactionTypeSeeder.php +opt/app/public/css/bootstrap-tour.min.css +opt/app/public/css/daterangepicker.css opt/app/public/css/firefly.css opt/app/public/index.php +opt/app/public/js/ff/budgets/index.js +opt/app/public/js/ff/charts.defaults.js +opt/app/public/js/ff/charts.js +opt/app/public/js/ff/firefly.js opt/app/public/js/ff/guest.js +opt/app/public/js/ff/help.js +opt/app/public/js/ff/index.js +opt/app/public/js/lib/Chart.bundle.min.js +opt/app/public/js/lib/accounting.min.js +opt/app/public/js/lib/bootstrap-tour.min.js +opt/app/public/js/lib/daterangepicker.js opt/app/public/js/lib/jquery-3.1.1.min.js +opt/app/public/js/lib/moment.min.js opt/app/public/lib/adminlte/css/AdminLTE.min.css +opt/app/public/lib/adminlte/css/skins/skin-blue-light.min.css +opt/app/public/lib/adminlte/js/app.min.js opt/app/public/lib/bootstrap/css/bootstrap.min.css opt/app/public/lib/bootstrap/js/bootstrap.min.js opt/app/public/lib/font-awesome/css/font-awesome.min.css +opt/app/public/lib/font-awesome/fonts/fontawesome-webfont.woff2 +opt/app/resources/lang/en_US/breadcrumbs.php opt/app/resources/lang/en_US/config.php +opt/app/resources/lang/en_US/firefly.php +opt/app/resources/lang/en_US/form.php +opt/app/resources/lang/en_US/help.php +opt/app/resources/lang/en_US/validation.php opt/app/resources/views/auth/login.twig +opt/app/resources/views/budgets/index.twig opt/app/resources/views/emails/error-html.twig opt/app/resources/views/emails/error-text.twig opt/app/resources/views/emails/footer-html.twig opt/app/resources/views/emails/footer-text.twig opt/app/resources/views/emails/header-html.twig opt/app/resources/views/emails/header-text.twig +opt/app/resources/views/form/balance.twig +opt/app/resources/views/form/feedback.twig +opt/app/resources/views/form/help.twig +opt/app/resources/views/form/text.twig +opt/app/resources/views/index.twig +opt/app/resources/views/javascript/variables.twig +opt/app/resources/views/json/tour.twig +opt/app/resources/views/layout/default.twig opt/app/resources/views/layout/guest.twig +opt/app/resources/views/list/journals-tiny-tasker.twig +opt/app/resources/views/new-user/index.twig +opt/app/resources/views/partials/boxes.twig +opt/app/resources/views/partials/control-bar.twig opt/app/resources/views/partials/favicons.twig +opt/app/resources/views/partials/flashes.twig +opt/app/resources/views/partials/menu-sidebar.twig +opt/app/resources/views/partials/page-header.twig +opt/app/resources/views/profile/index.twig opt/app/routes/api.php opt/app/routes/console.php opt/app/routes/web.php @@ -353,6 +439,7 @@ opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/Generator.php opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/Manager.php opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/ServiceProvider.php opt/app/vendor/davejamesmiller/laravel-breadcrumbs/src/View.php +opt/app/vendor/davejamesmiller/laravel-breadcrumbs/views/bootstrap3.blade.php opt/app/vendor/doctrine/common/lib/Doctrine/Common/EventManager.php opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Configuration.php opt/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php @@ -416,6 +503,8 @@ opt/app/vendor/laravel/framework/src/Illuminate/Auth/Console/ClearResetsCommand. opt/app/vendor/laravel/framework/src/Illuminate/Auth/Console/MakeAuthCommand.php opt/app/vendor/laravel/framework/src/Illuminate/Auth/CreatesUserProviders.php opt/app/vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Auth/Events/Authenticated.php +opt/app/vendor/laravel/framework/src/Illuminate/Auth/Events/Login.php opt/app/vendor/laravel/framework/src/Illuminate/Auth/GuardHelpers.php opt/app/vendor/laravel/framework/src/Illuminate/Auth/Passwords/CanResetPassword.php opt/app/vendor/laravel/framework/src/Illuminate/Auth/Passwords/PasswordResetServiceProvider.php @@ -428,6 +517,7 @@ opt/app/vendor/laravel/framework/src/Illuminate/Cache/CacheManager.php opt/app/vendor/laravel/framework/src/Illuminate/Cache/CacheServiceProvider.php opt/app/vendor/laravel/framework/src/Illuminate/Cache/Console/CacheTableCommand.php opt/app/vendor/laravel/framework/src/Illuminate/Cache/Console/ClearCommand.php +opt/app/vendor/laravel/framework/src/Illuminate/Cache/Events/CacheHit.php opt/app/vendor/laravel/framework/src/Illuminate/Cache/Events/CacheMissed.php opt/app/vendor/laravel/framework/src/Illuminate/Cache/Events/KeyForgotten.php opt/app/vendor/laravel/framework/src/Illuminate/Cache/Events/KeyWritten.php @@ -488,6 +578,7 @@ opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/QueueableCollect opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/QueueableEntity.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Queue/ShouldQueue.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Routing/Registrar.php +opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Routing/ResponseFactory.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Routing/UrlGenerator.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Routing/UrlRoutable.php opt/app/vendor/laravel/framework/src/Illuminate/Contracts/Support/Arrayable.php @@ -529,6 +620,11 @@ opt/app/vendor/laravel/framework/src/Illuminate/Database/DetectsLostConnections. opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Collection.php opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/BelongsTo.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/HasMany.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/HasOneOrMany.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/Relation.php opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Scope.php opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/SoftDeletes.php opt/app/vendor/laravel/framework/src/Illuminate/Database/Eloquent/SoftDeletingScope.php @@ -542,8 +638,10 @@ opt/app/vendor/laravel/framework/src/Illuminate/Database/Migrations/MigrationRep opt/app/vendor/laravel/framework/src/Illuminate/Database/Migrations/Migrator.php opt/app/vendor/laravel/framework/src/Illuminate/Database/MySqlConnection.php opt/app/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Query/Expression.php opt/app/vendor/laravel/framework/src/Illuminate/Database/Query/Grammars/Grammar.php opt/app/vendor/laravel/framework/src/Illuminate/Database/Query/Grammars/MySqlGrammar.php +opt/app/vendor/laravel/framework/src/Illuminate/Database/Query/JoinClause.php opt/app/vendor/laravel/framework/src/Illuminate/Database/Query/Processors/MySqlProcessor.php opt/app/vendor/laravel/framework/src/Illuminate/Database/Query/Processors/Processor.php opt/app/vendor/laravel/framework/src/Illuminate/Database/QueryException.php @@ -625,6 +723,7 @@ opt/app/vendor/laravel/framework/src/Illuminate/Foundation/Validation/ValidatesR opt/app/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php opt/app/vendor/laravel/framework/src/Illuminate/Hashing/BcryptHasher.php opt/app/vendor/laravel/framework/src/Illuminate/Hashing/HashServiceProvider.php +opt/app/vendor/laravel/framework/src/Illuminate/Http/JsonResponse.php opt/app/vendor/laravel/framework/src/Illuminate/Http/RedirectResponse.php opt/app/vendor/laravel/framework/src/Illuminate/Http/Request.php opt/app/vendor/laravel/framework/src/Illuminate/Http/Response.php @@ -686,6 +785,7 @@ opt/app/vendor/laravel/framework/src/Illuminate/Routing/Matching/ValidatorInterf opt/app/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php opt/app/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php opt/app/vendor/laravel/framework/src/Illuminate/Routing/Redirector.php +opt/app/vendor/laravel/framework/src/Illuminate/Routing/ResponseFactory.php opt/app/vendor/laravel/framework/src/Illuminate/Routing/Route.php opt/app/vendor/laravel/framework/src/Illuminate/Routing/RouteCollection.php opt/app/vendor/laravel/framework/src/Illuminate/Routing/RouteDependencyResolverTrait.php @@ -701,26 +801,33 @@ opt/app/vendor/laravel/framework/src/Illuminate/Session/SessionInterface.php opt/app/vendor/laravel/framework/src/Illuminate/Session/SessionManager.php opt/app/vendor/laravel/framework/src/Illuminate/Session/SessionServiceProvider.php opt/app/vendor/laravel/framework/src/Illuminate/Session/Store.php +opt/app/vendor/laravel/framework/src/Illuminate/Session/TokenMismatchException.php opt/app/vendor/laravel/framework/src/Illuminate/Support/AggregateServiceProvider.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Arr.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Collection.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Composer.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/App.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Auth.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Cache.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Config.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Crypt.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/DB.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Event.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Gate.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Input.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Log.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Mail.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Request.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Response.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Route.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Schema.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Session.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/URL.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Validator.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Facades/View.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Fluent.php +opt/app/vendor/laravel/framework/src/Illuminate/Support/HtmlString.php opt/app/vendor/laravel/framework/src/Illuminate/Support/Manager.php opt/app/vendor/laravel/framework/src/Illuminate/Support/MessageBag.php opt/app/vendor/laravel/framework/src/Illuminate/Support/NamespacedItemResolver.php @@ -738,8 +845,11 @@ opt/app/vendor/laravel/framework/src/Illuminate/Validation/DatabasePresenceVerif opt/app/vendor/laravel/framework/src/Illuminate/Validation/Factory.php opt/app/vendor/laravel/framework/src/Illuminate/Validation/PresenceVerifierInterface.php opt/app/vendor/laravel/framework/src/Illuminate/Validation/ValidatesWhenResolvedTrait.php +opt/app/vendor/laravel/framework/src/Illuminate/Validation/ValidationException.php opt/app/vendor/laravel/framework/src/Illuminate/Validation/ValidationServiceProvider.php opt/app/vendor/laravel/framework/src/Illuminate/Validation/Validator.php +opt/app/vendor/laravel/framework/src/Illuminate/View/Compilers/BladeCompiler.php +opt/app/vendor/laravel/framework/src/Illuminate/View/Compilers/Compiler.php opt/app/vendor/laravel/framework/src/Illuminate/View/Compilers/CompilerInterface.php opt/app/vendor/laravel/framework/src/Illuminate/View/Engines/CompilerEngine.php opt/app/vendor/laravel/framework/src/Illuminate/View/Engines/EngineInterface.php @@ -751,6 +861,10 @@ opt/app/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromS opt/app/vendor/laravel/framework/src/Illuminate/View/View.php opt/app/vendor/laravel/framework/src/Illuminate/View/ViewFinderInterface.php opt/app/vendor/laravel/framework/src/Illuminate/View/ViewServiceProvider.php +opt/app/vendor/laravelcollective/html/src/Componentable.php +opt/app/vendor/laravelcollective/html/src/FormBuilder.php +opt/app/vendor/laravelcollective/html/src/FormFacade.php +opt/app/vendor/laravelcollective/html/src/HtmlBuilder.php opt/app/vendor/laravelcollective/html/src/HtmlServiceProvider.php opt/app/vendor/laravelcollective/html/src/helpers.php opt/app/vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php @@ -765,6 +879,7 @@ opt/app/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php opt/app/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php opt/app/vendor/monolog/monolog/src/Monolog/Logger.php opt/app/vendor/nesbot/carbon/src/Carbon/Carbon.php +opt/app/vendor/nesbot/carbon/src/Carbon/Lang/en.php opt/app/vendor/paragonie/random_compat/lib/random.php opt/app/vendor/pragmarx/google2fa/src/Vendor/Laravel/ServiceProvider.php opt/app/vendor/psr/log/Psr/Log/LoggerInterface.php @@ -912,8 +1027,11 @@ opt/app/vendor/symfony/console/Style/SymfonyStyle.php opt/app/vendor/symfony/debug/Exception/FatalErrorException.php opt/app/vendor/symfony/debug/Exception/FlattenException.php opt/app/vendor/symfony/debug/ExceptionHandler.php +opt/app/vendor/symfony/finder/Comparator/Comparator.php +opt/app/vendor/symfony/finder/Comparator/DateComparator.php opt/app/vendor/symfony/finder/Finder.php opt/app/vendor/symfony/finder/Glob.php +opt/app/vendor/symfony/finder/Iterator/DateRangeFilterIterator.php opt/app/vendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php opt/app/vendor/symfony/finder/Iterator/FileTypeFilterIterator.php opt/app/vendor/symfony/finder/Iterator/FilenameFilterIterator.php @@ -922,9 +1040,12 @@ opt/app/vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php opt/app/vendor/symfony/finder/Iterator/PathFilterIterator.php opt/app/vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php opt/app/vendor/symfony/finder/SplFileInfo.php +opt/app/vendor/symfony/http-foundation/AcceptHeader.php +opt/app/vendor/symfony/http-foundation/AcceptHeaderItem.php opt/app/vendor/symfony/http-foundation/Cookie.php opt/app/vendor/symfony/http-foundation/FileBag.php opt/app/vendor/symfony/http-foundation/HeaderBag.php +opt/app/vendor/symfony/http-foundation/JsonResponse.php opt/app/vendor/symfony/http-foundation/ParameterBag.php opt/app/vendor/symfony/http-foundation/RedirectResponse.php opt/app/vendor/symfony/http-foundation/Request.php @@ -944,6 +1065,11 @@ opt/app/vendor/symfony/routing/CompiledRoute.php opt/app/vendor/symfony/routing/Route.php opt/app/vendor/symfony/routing/RouteCompiler.php opt/app/vendor/symfony/routing/RouteCompilerInterface.php +opt/app/vendor/symfony/translation/Loader/ArrayLoader.php +opt/app/vendor/symfony/translation/Loader/LoaderInterface.php +opt/app/vendor/symfony/translation/MessageSelector.php +opt/app/vendor/symfony/translation/Translator.php +opt/app/vendor/symfony/translation/TranslatorBagInterface.php opt/app/vendor/symfony/translation/TranslatorInterface.php opt/app/vendor/symfony/var-dumper/Cloner/AbstractCloner.php opt/app/vendor/symfony/var-dumper/Cloner/ClonerInterface.php @@ -970,14 +1096,21 @@ opt/app/vendor/twig/twig/lib/Twig/LexerInterface.php opt/app/vendor/twig/twig/lib/Twig/Loader/Array.php opt/app/vendor/twig/twig/lib/Twig/Loader/Chain.php opt/app/vendor/twig/twig/lib/Twig/LoaderInterface.php +opt/app/vendor/twig/twig/lib/Twig/Markup.php opt/app/vendor/twig/twig/lib/Twig/Node.php opt/app/vendor/twig/twig/lib/Twig/Node/Block.php opt/app/vendor/twig/twig/lib/Twig/Node/BlockReference.php opt/app/vendor/twig/twig/lib/Twig/Node/Body.php opt/app/vendor/twig/twig/lib/Twig/Node/Expression.php opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Array.php +opt/app/vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary.php opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/And.php +opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php +opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php +opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php +opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php +opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Call.php opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Constant.php @@ -985,13 +1118,19 @@ opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Filter.php opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Function.php opt/app/vendor/twig/twig/lib/Twig/Node/Expression/GetAttr.php opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Name.php +opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Test.php opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Unary.php +opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Neg.php opt/app/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Not.php +opt/app/vendor/twig/twig/lib/Twig/Node/For.php +opt/app/vendor/twig/twig/lib/Twig/Node/ForLoop.php opt/app/vendor/twig/twig/lib/Twig/Node/If.php opt/app/vendor/twig/twig/lib/Twig/Node/Include.php opt/app/vendor/twig/twig/lib/Twig/Node/Module.php opt/app/vendor/twig/twig/lib/Twig/Node/Print.php +opt/app/vendor/twig/twig/lib/Twig/Node/Set.php opt/app/vendor/twig/twig/lib/Twig/Node/Text.php +opt/app/vendor/twig/twig/lib/Twig/NodeCaptureInterface.php opt/app/vendor/twig/twig/lib/Twig/NodeInterface.php opt/app/vendor/twig/twig/lib/Twig/NodeOutputInterface.php opt/app/vendor/twig/twig/lib/Twig/NodeTraverser.php @@ -1046,6 +1185,7 @@ usr/bin/mysql_install_db usr/bin/php usr/bin/php7.0 usr/bin/sudo +usr/lib/locale/locale-archive usr/lib/php/20151012/bcmath.so usr/lib/php/20151012/calendar.so usr/lib/php/20151012/ctype.so @@ -1170,6 +1310,7 @@ usr/lib/x86_64-linux-gnu/libxslt.so.1.1.28 usr/sbin/mysqld usr/sbin/nginx usr/sbin/php-fpm7.0 +usr/share/locale/locale.alias usr/share/mysql/charsets/Index.xml usr/share/mysql/english/errmsg.sys usr/share/mysql/fill_help_tables.sql diff --git a/.sandstorm/sandstorm-pkgdef.capnp b/.sandstorm/sandstorm-pkgdef.capnp index 5bb401c185..175103b39f 100644 --- a/.sandstorm/sandstorm-pkgdef.capnp +++ b/.sandstorm/sandstorm-pkgdef.capnp @@ -15,7 +15,7 @@ const pkgdef :Spk.PackageDefinition = ( manifest = ( appTitle = (defaultText = "Firefly III"), - appVersion = 0, + appVersion = 1, appMarketingVersion = (defaultText = "3.4.3"), actions = [ # Define your "new document" handlers here. @@ -42,7 +42,8 @@ const pkgdef :Spk.PackageDefinition = ( website = "https://firefly-iii.github.io/", codeUrl = "https://github.com/firefly-iii/firefly-iii", - license = (openSource = mit), + #license = (openSource = mit), + license = (proprietary = (defaultText = embed "../LICENSE")), # The license this package is distributed under. See # https://docs.sandstorm.io/en/latest/developing/publishing-apps/#license @@ -54,40 +55,23 @@ const pkgdef :Spk.PackageDefinition = ( author = ( contactEmail = "thegrumpydictator@gmail.com", upstreamAuthor = "James Cole", + pgpSignature = embed "pgp-signature", ), - #pgpKeyring = embed "path/to/pgp-keyring", - # A keyring in GPG keyring format containing all public keys needed to verify PGP signatures in - # this manifest (as of this writing, there is only one: `author.pgpSignature`). - # - # To generate a keyring containing just your public key, do: - # - # gpg --export <key-id> > keyring - # - # Where `<key-id>` is a PGP key ID or email address associated with the key. - - #description = (defaultText = embed "path/to/description.md"), - # The app's description in Github-flavored Markdown format, to be displayed e.g. - # in an app store. Note that the Markdown is not permitted to contain HTML nor image tags (but - # you can include a list of screenshots separately). - + pgpKeyring = embed "pgp-keyring", + description = (defaultText = embed "description.md"), shortDescription = (defaultText = "Financial management"), - # A very short (one-to-three words) description of what the app does. For example, - # "Document editor", or "Notetaking", or "Email client". This will be displayed under the app - # title in the grid view in the app market. - screenshots = [ # Screenshots to use for marketing purposes. Examples below. # Sizes are given in device-independent pixels, so if you took these # screenshots on a Retina-style high DPI screen, divide each dimension by two. - #(width = 746, height = 795, jpeg = embed "path/to/screenshot-1.jpeg"), - #(width = 640, height = 480, png = embed "path/to/screenshot-2.png"), + (width = 1200, height = 1000, png = embed "screenshot-1.png"), + (width = 1200, height = 1000, png = embed "screenshot-2.png"), + (width = 1200, height = 1518, png = embed "screenshot-3.png"), + ], - #changeLog = (defaultText = embed "path/to/sandstorm-specific/changelog.md"), - # Documents the history of changes in Github-flavored markdown format (with the same restrictions - # as govern `description`). We recommend formatting this with an H1 heading for each version - # followed by a bullet list of changes. + changeLog = (defaultText = embed "changelog.md"), ), ), diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index e8dc229028..97320a2b3f 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -20,6 +20,7 @@ use FireflyIII\Http\Middleware\IsAdmin; use FireflyIII\Http\Middleware\Range; use FireflyIII\Http\Middleware\RedirectIfAuthenticated; use FireflyIII\Http\Middleware\RedirectIfTwoFactorAuthenticated; +use FireflyIII\Http\Middleware\Sandstorm; use FireflyIII\Http\Middleware\VerifyCsrfToken; use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth; use Illuminate\Auth\Middleware\Authorize; @@ -80,6 +81,7 @@ class Kernel extends HttpKernel // does not check 2fa // does not check activation 'web' => [ + Sandstorm::class, EncryptCookies::class, AddQueuedCookiesToResponse::class, StartSession::class, @@ -87,8 +89,11 @@ class Kernel extends HttpKernel VerifyCsrfToken::class, SubstituteBindings::class, ], + + // MUST NOT be logged in. Does not care about 2FA or confirmation. 'user-not-logged-in' => [ + Sandstorm::class, EncryptCookies::class, AddQueuedCookiesToResponse::class, StartSession::class, @@ -101,6 +106,7 @@ class Kernel extends HttpKernel // MUST NOT have 2FA // don't care about confirmation: 'user-logged-in-no-2fa' => [ + Sandstorm::class, EncryptCookies::class, AddQueuedCookiesToResponse::class, StartSession::class, @@ -110,24 +116,12 @@ class Kernel extends HttpKernel Authenticate::class, RedirectIfTwoFactorAuthenticated::class, ], - // MUST be logged in - // MUST have 2FA - // MUST NOT have confirmation. - 'user-logged-in-2fa-no-activation' => [ - EncryptCookies::class, - AddQueuedCookiesToResponse::class, - StartSession::class, - ShareErrorsFromSession::class, - VerifyCsrfToken::class, - SubstituteBindings::class, - Authenticate::class, - AuthenticateTwoFactor::class, - ], // MUST be logged in // don't care about 2fa // don't care about confirmation. 'user-simple-auth' => [ + Sandstorm::class, EncryptCookies::class, AddQueuedCookiesToResponse::class, StartSession::class, @@ -142,6 +136,7 @@ class Kernel extends HttpKernel // MUST be confirmed. // (this group includes the other Firefly middleware) 'user-full-auth' => [ + Sandstorm::class, EncryptCookies::class, AddQueuedCookiesToResponse::class, StartSession::class, @@ -159,6 +154,7 @@ class Kernel extends HttpKernel // MUST have owner role // (this group includes the other Firefly middleware) 'admin' => [ + Sandstorm::class, EncryptCookies::class, AddQueuedCookiesToResponse::class, StartSession::class, diff --git a/app/Http/Middleware/Sandstorm.php b/app/Http/Middleware/Sandstorm.php new file mode 100644 index 0000000000..ff4093cede --- /dev/null +++ b/app/Http/Middleware/Sandstorm.php @@ -0,0 +1,72 @@ +<?php +/** + * Sandstorm.php + * Copyright (c) 2017 thegrumpydictator@gmail.com + * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. + * + * See the LICENSE file for details. + */ + +declare(strict_types = 1); + +namespace FireflyIII\Http\Middleware; + +use Auth; +use Closure; +use FireflyIII\User; +use Illuminate\Http\Request; + +/** + * Class Sandstorm + * + * @package FireflyIII\Http\Middleware + */ +class Sandstorm +{ + /** + * Detects if is using Sandstorm, and responds by logging the user + * in and/or creating an account. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @param string|null $guard + * + * @return mixed + */ + public function handle(Request $request, Closure $next, $guard = null) + { + // is in Sandstorm environment? + $sandstorm = intval(getenv('SANDSTORM')) === 1; + if (!$sandstorm) { + return $next($request); + } + + // we're in sandstorm! is user a guest? + if (Auth::guard($guard)->guest()) { + $userId = strval($request->header('X-Sandstorm-User-Id')); + if (strlen($userId) > 0) { + // find user? + $email = $userId . '@firefly'; + $user = User::whereEmail($email)->first(); + if (is_null($user)) { + $user = User::create( + [ + 'email' => $email, + 'password' => str_random(16), + ] + ); + } + + + // login user: + Auth::guard($guard)->login($user); + } else { + echo 'user id no length, guest?'; + exit; + } + + } + + return $next($request); + } +} From b68d5c4374a5b509153a536506f52defc32f3e43 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 4 Feb 2017 09:02:07 +0100 Subject: [PATCH 637/709] Include CSRF token for Sandstorm. --- .sandstorm/sandstorm-pkgdef.capnp | 6 +++--- .sandstorm/screenshots/screenshot-1.png | Bin 0 -> 180984 bytes .sandstorm/screenshots/screenshot-2.png | Bin 0 -> 227686 bytes .sandstorm/screenshots/screenshot-3.png | Bin 0 -> 286366 bytes public/js/ff/index.js | 4 ++-- resources/views/javascript/variables.twig | 3 ++- 6 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 .sandstorm/screenshots/screenshot-1.png create mode 100644 .sandstorm/screenshots/screenshot-2.png create mode 100644 .sandstorm/screenshots/screenshot-3.png diff --git a/.sandstorm/sandstorm-pkgdef.capnp b/.sandstorm/sandstorm-pkgdef.capnp index 175103b39f..c0493e5fb6 100644 --- a/.sandstorm/sandstorm-pkgdef.capnp +++ b/.sandstorm/sandstorm-pkgdef.capnp @@ -66,9 +66,9 @@ const pkgdef :Spk.PackageDefinition = ( # Sizes are given in device-independent pixels, so if you took these # screenshots on a Retina-style high DPI screen, divide each dimension by two. - (width = 1200, height = 1000, png = embed "screenshot-1.png"), - (width = 1200, height = 1000, png = embed "screenshot-2.png"), - (width = 1200, height = 1518, png = embed "screenshot-3.png"), + (width = 1200, height = 1000, png = embed "screenshots/screenshot-1.png"), + (width = 1200, height = 1000, png = embed "screenshots/screenshot-2.png"), + (width = 1200, height = 1518, png = embed "screenshots/screenshot-3.png"), ], changeLog = (defaultText = embed "changelog.md"), diff --git a/.sandstorm/screenshots/screenshot-1.png b/.sandstorm/screenshots/screenshot-1.png new file mode 100644 index 0000000000000000000000000000000000000000..f72ceb48e783b52be71ea32652825da41f42125c GIT binary patch literal 180984 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%0Ak#nvAF*Tu=^oP1QOu;BJvd{fDy=*BSA<> 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%0DFaj_SM^4ytur670vQ> zb%iQW;69C=E6#ZCA2mb6QhTJ;g{=4DZPm2UmBrHpS<`(Sf>Z;s_~%6LDysCnV6?B_ zUjx%rP(rfTc{5E@pu$@EFfYY(Gn^249CuO5#3vwch=fksawO{-%qM_!a)=$>oCmZS zk{!`jLJZA6k^D$EsJCAM2YUyQ9qtzz+7H<B+yu(Yn@?lw&Bub>V&O9QBo;2tp>17T zuVw!EcV($vp!($Mul6SO_$-7i{*?4;2!Gir09gu$y{a=B#c8RdmGB(Dhp4SDe)EHf zIVHnSxfhPxC%t}0u?9t!M%q3;*<{Us*X=QVNDkuE9~HujA}{X6fe1rMNS?26=T}yi zQqy~_^4EDee7Z<5wS6^?3+1CiPgE9bxO3o*A1cg2U8_JovT?NT*sl1`jT$Pcfa+=c z@~gFK(EsYuL78n0Q@1aAvNrwqHDF;=m(-R=v?~5LvGqIT;iFO9?{^*`$x)*Lybvk? zxDfExmi)EU{whgwnIs>K?_2(?3;uD+B9q;$^_LtU?$U177BzXn3!sPv2mh^@*94#? z=cJ?ujfQq$e|&DD%Qk>|IOUrqHZl8j)(5|L^Cif`9+nmV_0wU0;_O1sM3d$>lxrF2 zcRtEid2_tEiBt9+QS}g`OrEe&Q@6_p9_i<8V=Vt#iZu}^HDp{|MiS{gBBza}2>sqr z`!b4p9C*&_j@`|Fellk8pC_lWkmh4nJXOH;`?1iC7pOV8ToV4j7swfA?ohC1Df{2Q zkcGi&n4hZZ{-5ewSQ>n9>1ql7wch`_jq_kYYy!Tb63}({*Tn>n|KA$2e?3D}7VK={ zq}sZFyegnzp&ea5(i2&r7}`HOPora)qKGOy1V1EGL6WNIo9G8+gMV({yjZyG#wYJv z4+}Gk&$|pp%rDjOFNY}O&1@9pn!nW7VP!b+ySDJYdNVbiMGPQh(<?W(dix`aAFi`Y z<)6@6Z>v+?Es2h)!{}GSd>pT=1QAxvX)HQ8d+T(4xoKH|;=uyK)>2w%|2c(7vf~Td zm+xq#$1L)gjZXTd&Z{TV##&+yf2fsN#$9*fK>;Lp8qEOS_Nwu;W8tl%@w9N^2P5W> zW_SmJ>AF){_yL3|_nqNGI{RbQqk;GH6%C%sJ+n0Lf^3SxuvbKiMLz@;7GnY=#UKO` zP_ik(I5G_8IRINvH1R!CpmGGnaKPE+0L!1K+i}W0GY|s-vn_!zX|S<@`Zwi}kB=|# zZAnQQh*gQDkr~dG&>#-!lu7~Rk#oIj_@7S)hu3dytMs|vr}!!_Tvvg}Z&u?@e6IZD zcfYumlnqbWuLXMJUs%`*Kh#>Y=OkZDhf}@#_<hCU@rqmT@Pm(HUkuLY1}WtFzN=Qh zTX>LSTk}QC>%QGXTAEjxca3gLq%s7`0a9Tq%l<OakLlCXCv%UNkK8%Q;(1=^m-`&m zQF3;lNNP^?&~>ZyeCI1>?%Y8PdQ!1b64xAC=4$$S!Lybg&|F+SmvV({y7{6lJ`NKk z&Hy{B!*#v)w5y7VoEZ1FBGl_W)FUgl9RW&?A~YTIleqY+y~*z(;mXf4>(ofO@*c#z zicYc>*2+k;7Bkw{=PozO&QiMBh<gNSjBNAuZkXXn`O^Br<xuEG;@ifYX*h}DD>(3< NlAOA1xwNtW{{!TCBdGuY literal 0 HcmV?d00001 diff --git a/.sandstorm/screenshots/screenshot-2.png b/.sandstorm/screenshots/screenshot-2.png new file mode 100644 index 0000000000000000000000000000000000000000..93018518505bfd0f6982d856b99a0bcdbf93c7e9 GIT binary patch literal 227686 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𝔥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≺fSF<ug} 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%0Aeq%=rDkFD(EBc<Dq_;0TR<P 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 MCMQ}ctpDTx01dR6xc~qF literal 0 HcmV?d00001 diff --git a/.sandstorm/screenshots/screenshot-3.png b/.sandstorm/screenshots/screenshot-3.png new file mode 100644 index 0000000000000000000000000000000000000000..20fcab548fd2112fb01875a52d0cd67b3f622d0b GIT binary patch literal 286366 zcmd43byS>5_CJi%xI4imxVyW%LvVL@hsNFAAp{E$g1bAx-9vD9=VfMhcC+)H-~TV? z^m)3f?x(I))l>IY-6ld&UIGCY7ZwBr1VKttR2c*Wq6P#6@)Q8}UUOX|Uh)0_byk)T z2KhRHfAC&_agfw<1_6OX`{M%&l9`SD4xzAA)pXI6ljSzHx1~2Uu{ScM_po(%rv?Gx z_27R0YHR9ZNaSH_W9Q86!AJ5Z1^4^+KdKo>i2fvTvF0Pulv5-Uv3D{hVxwoJXC&c= zB_bl?buuyIRu&ciH~hWCM`GdP;=s+o;O_2D@6JMR?_|!v#KpzMz{t$N%uM%ALFepg z=VItVXXi}%uTK8zN7U5W*vZnt#nRr6=#PF4jqF`r_((|pVDz8Af63`$Y4%@EcFzA+ z>s>*HKUx@==ouOQ31;eH`Tv0Z(enR<nHc{^D+gC6n?H3jF=jBeF|{?db8&vhG5sUE zcia94_&-8=7&`om*`G%Imu|d&NY1Tn>TGZ0`iBYB>?~dQnR)+I=<n74waC9vBKEcp zPNvSz?=XJ0e?xw+`#Zhnf7$SJ{Kez<%D)2?oGjlpHT**^ex`pj_`UA$^jiPK;P=Yk z0DqXCThY?P)J9X(^4*|+YQe<J$jHm^k4=AvirCxOJE=Gr8k_zh+Mkf$s{T&>CnK%j z7_l*N{?6#Prr)3@#@x=XMpmZAF29Y~`?&o%|Ah_B8GftgW%x&G-uKzSEoX0HY33<v z=wiyx%*ez}$HYv>#HPx~$<4&f&BejX@Hd*@B>1C6#L3jq#okHP-rk1)Pd#k^C=#*I zv;PbEd;7nkybOP=q~BK7zn1NvweRbeANC#j*E@h8b{?|29|S}YL`qah)dTc62iga} z|7Os&h396y)nqu)RI6dtyH^#B2pF%3miP>FKpY5+kDo*Z<hX#5`3MW2&9UM!4h*zQ zHP}?#tv{_vJ^H&3>P;lBl93)&x;yr*A18Bk-kIIL?2aF2U<x!X%kc*V5D@_YVE_C0 zi3ECgv0!>o`wwkF0iglN!nNQ+g2>1a5TL(52B^S<0fS}RB`g2i-M>o2=y-nzyu%H$ zNr-?B+0=S1bpJ<;pg$P>4~KvA`~L?c%}*#n9e(`akQ^DSt8x^sqd66UcX2N{+z)vX zg)erqMbx~?Omx9nxf%>5xU(_{r3a+-BnSUs8XreM6B+UKw{*JQUZbqZ&h^(<0*UIl zIU`37*ClW7Cn%+XqC$(Kle)|`Dm&-vjByI2sMaEl3Wp(K)e~E`84^!gv!s5T(^_jT zUtU@_3Bm)B)I`z$jtn*ufb;S3q}`Vmtxhl6J<yz)z>Cfo9ReayB?<%{m;vo2+%$4* zcL|)v-&p<W1&lwqv98-rD+UHPVHV5?JeL+WUv0%9`nVfPdnLb-=I!{a&u&_BMY9q} zi5N(9I8#dKa<a{jTD@-U79tb=i2B@b$6))svdp`C(G6MPzd$EVZ?*4Xt}qvUKl!U{ z3+d|;(vE;m!tLS2C7)VV>BcP5aj>HzRWIxyK^e0Y)?c&g6E-M8Dsp$_B9Tw9gMW2q zL$0f5eJgcV+yVoS`a;f~WX|f|*|{5+WeLCgAYzi9Z5zpOyvhSAr~Qbx3QlyZxF2b+ z;oO0KG_MbjbVD~}Tt=ZM*UPNuSahCwe)w{&84+W?M4tKFAptiF0yVEHR1x(E?$<QX z1m}?6CUM?5A^Q7lMf0$!)eEetz9~}{G8g&^xFhyrvskCmW+>OH@4fYb51YJ$c}C%p znewSYv7I->&Zux#tXUKCak(kTMr>J^)+<|72ssTu4avVs00N1K7tN(zh_HY+-%&pM zYx)-T2CmOD>MML95>bVO*&6(Gt|TPJmm`d8ul{HZ!@x9q_PLJo$x}NLk6SVt=6WO? z*5Z^*Gdvb)g{ORBXLtWd2Jz}B|KatO$Ubfs;7{ILJOtv;KqI$5;HJo^@rINWjdc6) zWLg)xGw@`+^3~sPB_N=x!d+|UKQ;~x13?;Rp%h#7xizWiU$s5EVQkgsWZPvR{Ggel z;yHPOt=H~FMXi8%7jt`OMyV>N<F01SF)YG8LOBc2gTt$^<2Jv1YMyHy$#RU3t>g65 z2xx@f3ZJ)o3Lt3`?(UdZ)c=u6@9paycbWS$$me{LI<s{#Fr@X@o~a>mN12Lqsl%71 zScJ>nme2L(Lv-3hI2fBL$cM&tcWiFAs;urm<8sjTv***~q9qZpd~JLKOZ$2A8ASZV zun8-;dN$B7(%9Wym+&`3(IlbqxWB)0``<&UD+QQ3R@<B&HvDY4gMRV=K6%bJuHbTe zYck{;<fqlQ4+c6>MM<g>9RFNeHLb8e;%ejHYl*wVEeWb=uSTkCBVxg1zkfWv{CRwQ z(ZE#c^Cj;KJmPcKQVI=AVPgt=Dw{i(GxoDW-Eh_56i2=n<HffoaB`+v&_*a9a!O2s z-Mxb&%Rs-P%H@XKpN&`D^#zZ0)1(|298exw9f=t33*O#L!Jtdx4>VA{;{AC!32y3M z=nbMr=B0NGvWTsFFRRm^5u=`%`kbYN0SZNVJfm4}b+zGCA}GECNLN$f70&Hdm{3T@ zh~$REbh_-(v_mT+ZA->y#vjF@!IHhztg!<5>z3Aj%y84O1K87b<{n!czl~;cmk!{Y zWcd+b`rB8GPmP+CTpylU_S56>u+fiXi;=)G!YiF1DRC)eb;{zv_pD)v2oR5Cwa$*r zTh8bog#Wb@F!Dk;!0%FoydEA8-+XB3^vNDnFN)spdY|*1uhHL^8&2i=5k%;#2>f-L zxHZP5VDD~d?T*7?uJy9?@6_EO6%1FAysI0gW0HbZfY{544Ow`nh!MPfQd>Ycej2QM zy62Eu5A8Loe)5tbe3&dpck|knUU;r4lFZe@%2btViPoJOS^dsku>*TsNx;q1@VD8X z$J<W7?7h{f4*o`Dfg?3%)Gd~#O9mT;6}@>*0jlHaj0>Ow+#yfys%E%B+7J2kG;FV@ z^6bP=d3&V0iaiR7ko(%DlLy-p?+R@w7QT^OCMqLG>HF8rhDE`%55MGQqZKo=Z}}nA zgNPX?h$HgH1t1ZdJha(94^9zqvXWu48kiCZ(n1>M!wT^e+<Q00mUo)Cg--1Ae*{HB zpp}V9Pr?qu(^_A>SS*E9uY0)Ef*Dort@ibTD;~v(Utjkl8Hl0`RcR=${dq7>?ofBe zkQ;s#A^-MZ>8Gosp#EtxF7<OqP1SK#Htf0u6V=GuVY3t~ES`^#FDTXoM(#da2vbZ- z##A2WEXHg-w1<x5!E}1lKp7)3f=E@E9X2?O1?apS!C1ruwW~8B?-W=#RmF@n7{#wg zwhC|#*l8_lP-Q+{7Iy0G)DbBJ#iZPI{zD=dxj+!}?(k|w1VmopsV`sa`CoE~KI2>n zq4?=ln`_j7q6$Zye#wTV@-Q@$)pFkZTnH6&WK|@nL!>xgaVP{;Wyvv<T|9YEEA-ut znYa=L^+L;#Z(~S2WK)WQ=34m-t^^wrsE8xslB#T?$d$0(Yx7y6FNQOJ*Yqax{BAmc z=Xz_|b5;?YnZ;;!vDl3H3q28*D@lBd>zAOd=d|1ID`*)CKPr~c%j#oV8r$RNrAR53 z?O=O;^UJgKF(^$rHHp{v9aVKr2#rTNs|IRLC>lz4-NWp$7~pTV71Yx3M4zj-F;&N2 zeR{<5v&F`@)5})FDE!PdN+nU)XG+LaX2G8W!#`VN^izY8GMVc@*!`+!_pE@wOTbcK zH7(D$Jp99YB08h5#kk*mBMErh7lGA{tH8OZ$^8T+zN%A$GS`zej<z<HjWfm?>#ozI ztsZsO8i7mQzg#A4Ot2<MPO^nDnIO%Ra*OUCuj8ILaekQ!lLx0!pHakzs_af`xID@c zD;|SGK0d3<|Dxd;r4+wkYhXsBs4e|w>S+bBLss2z5_1eIESppW&XA58ObVwT<kzLl zTnNsN$=24|Xr(Qym><VRM?y3-__%#LEjGLbl+#;Li>(M=>3!skuBh$3okmO@w6z&( z;iN(Q+^3<rF?LTx;)A2~YQLwdME9Z0;&54SSv2H&c$j?NApotTtq4D1;zzU=H&gZp zO>>1<FEg3p_44_RuVV(*7#2R-ig}o)ND@7C9gKVf3%!q73+_LYe-?j$A=h5g)O<^; z_q=!VXnAlwIl4Te5PQ_;CMB?Yn!a_~K|F8JOG-h<+6I4ku<$>bo;_z#CYADcl)%}i z%fXKkU?C+oakayE%1V`wzTX!d5AE@KZb8<M!&8?fDStRTb>gJN|CxAjkyP~D{G?~+ zs&2!mI2H}JthWx47C;7q+R_Era#UD06}WEDWadA<T8s=_i0+meM~+M!nE1iJxG-<) ze?Ogp*7HIU`anD_F#k)pmC;j7dxu3wX7XoXCi{1Bt|ad_jy;O@rlRb1F=(%t)jePc zZ610A%D0S%13Ge5$;z9-QI+oJhm(@}sx_rRJ^ehKxP!t~-CuIY!L24<_}Msib<M>E z1-(#)!RfYimZG@`Aqbp{=JJuW_NO*SZZ+0O&e8~X{;nFH%3tiwTsF)3)V#~JzikJ> zILX7`kzz*H3N^482hC&6*1PclV1#!7sd_B_!s*pvjbS)&1$*&~Y_aOa;zn~J2q%Pu zUtSKc?-z;kkoA8wI#UO_oB06bUyoX8l6u=1GEf$#)}p+BL{!yLh|%R@Lrp}-HW2W1 zi*tf85gaqpFNURLnzN_X(1V+noNc71iDFZ=7G~HuqltKXdBd3c*Hm-!4-IaNGL!P} z1g}~(Ab(9q55cHS26z25tUh*+{QdUv6gHjmS-R0PW)r7;mz#bFuWn0?)MT{6T0~g- zmnwT_f-cN4Z6d^zW^GB~W9;$B=M)tqYVDSNET-}zH1?thVusCZ_!DQA!r8TPayi;= zLBInn<tHxkOk~_!UK6bpk7$r>CF$Hto#^(cZ$_QWq^C49KRGYMz&0(Y4E@HKLG{Kc zDL!|4N)Zd_Ck$XhevW7|I9r*>>(QQl&75zas{-kh<zwN$7u40!Qo&j?Og8G^4x8s5 z3<99Z+2sGgDMMLwZ!Ah{hoF6Cbj6&nS9zn7FmQn|u!IKDcyoDJ&dcMv-w>!y?`2-y zmn5nBPXuL*30g=-%ufJ>i+3<|+NYyo*;Jim!8cNNAajt<{xrCC7wiF+P;&YC>q~tV zmEkK~V(+<I?((NGKN%kjlCX_j0YiJ&QRpYD<5)d>GIptM&_&*Xjy51N;iq%(`YNED z9gjd=OFf+XZcy1Wl(0!KMq6zK*xTXgjL{a9#)MQ*r|ritVR$voD4h>MW9fA7wo9Pd z!$^zBsR-r~vt2bpLWvG?Kv-w7!&$4gI~Z{PYIR_CMMj1U)xRsn+$%<m0dT(E8KRm3 z^-<g3)I+oCt#ZF@Hgt4gY%n#`s4oUS35j)ZF<9PoWUgV*ZD=!sc6;PNAPa%xF58~x zK}s3^u%YWO5HNQld-vG0_6?cp7%nvS9C(NDLa*j<9EU<RuzsDjmlP+|F~+l=H(OXm z=au51j(XEflY*m)_;fUKLgIptmJpE9+8^|sJP)*qE*fL_6gDC?7##gl{MCh|yrt}R z&H2n*xk?HT2^S>i2!!Daee^;msjxeNVm%F_WT-nQ53pwTb@jr30+ApH77W-9ooKlw z(5qEipM{yrK*dPcojUw!I4~MQ{fUVOWdr;5Ii*rlyVFg{$xTeg&K0io%dKjIeB^nn zog?!jSazM3{rR`)kfn$MWICJIOi#2;55+$HysW?hL(a5u=}DhUhtBGWaPwghn#b2f z)$d;O#gbrCl_}W?m{ODQtzZueZW>(>Oz_t|%b>@OddmBdD#m)tTIz}$>x-7g_Kagz zDvs^2iXyo<a-%anTeZ)lUv1VutquhZMM|N3%ps`#80>MJ&Fe2c))Mr1x~|vY&fnhb z;q?8jN<Y)-P2+Rj^o6`U2CuD^?BwP5aP!6#;G+k!xv=NEs{?;`6$Jj~u+(SiDD>%5 zBE}#t=<THKk*}jSP81k~w;`wuMQE=Qlbp!Xr`m*b%F!9*rx)KjYzSt?{oFNeRJ@(1 zk}(Ta8R$Lo5FzFt>F9`@isJ{4Lf8Uk#<MKZaD9NQD}zrp2=~wam200?ySljUstMl? zie6?1(06!)LMt~yjCp7Qa*?x5)sL&MZiq)}(c&G`whpFh!Il;>QZM@a{E9QnH#v~Q zv>wP9JA^Ze5(?b&P>!89<M@kwM|ux8C!!}=+$WL8Op7?TM{q~)k(IF8*aw^E{7bec zt4u%}fz9pp8xjkf<Vfn&jQ-5D$iKqgzyxAJ2tjwj?+ag$;ddknIxZK?$rWjy&SDRe zauk0aPab@$JDadw>T~$?Ls8&|Tb8jAP$28#+;e?Jr-XMdi&6~rE?^?TTv}ByY)y#l zFsY3j?9=_}abZu&AVb)eBJuagyA44|&vAm$qo!S;jpUvhn9Xak3gKymR+|238U8QG zgu*XV2E)z$p|SvQ0>&vAe3xt*4}eb?attQ)(E>Y@l_)y9fl*^Zh5_PJLkF8b7zzQe z8ESAl-(rOr8jyqd6Sy|<7qFzQ8w8YW6eIn-RzJW;3J2jYXXDjpHfK}pwx|GMsy?6l zEM$1fM&uOjER865^Uq@2ry_P(B|sGBo{~^8duq$gl)yT)U^Ee5tWqG#1u1-&l2|?| zW4y*ni<JWT6BkPMh$DIgUl+ZHcUWp+=h(L7D{)Re*CTO3pSBDhPe2G}xB62>@0T*c zYD}K3O0+&>Csh!;lj?VO|5uC+{sQQlz*M!O`#!Z$LQ5<GJKxvw>X(nzp*bx?_=Lwf z7<f(Xl@X-5jD%kp?AIv!cJ@ql)A12!M6lxZ9%I4S+7E3BS>2Lg>@z%Y`PpkR!J@N2 zkdsx|)*YYyD6?47UJj>1l_!HAvYQ*80_EHTfb+@6!Fu@k8p?}Z?F<Dd3Lsgjx%Sip zhEG4*n{80sj`6d(rS2}cI;7zO5RI8}lme^Y-K+iYb-uVQ0R0!TPCIu~XEB+*7c6tb z-c2beFD5o_Ko{2y2PM<>@Q=u}l8`#ysp;wW=#(N|_B@Hx(9xJOt7J(&I)ll^umH<! zz|SSWq2}w)ZA)a~xa<*nO>wZ<Nt;aM!^Wv1(0+tX2M9?s3W#3nh<isQ1U&+*HZOm+ z3V0BYgXkvLhZ{GEH;MjwctHbm(?FC#ZE(Y3gqIu2@=4H#6waC@9LH`4Z|)%O8qaTK zIdR~ZNcV?5KxF)g*<e~yMZ5vEDv=54xT+t6h)ryC^yw35+afyf8n&6v&ZHw_BN^%; zn5FY(=e^=iohvI#YRJO3c*u2n$@l~CmwI+7xV7N<yt<v!T1(7cEz}y1<o`NrbNGh5 z=_nN+m=ixqd1&&JytHI-{j++;QfSx_3@EmzvxlDtsRPl(kYF~mF&IpPU@ah_+ijZU zg-GCDY^120L7kFkQ(F;bqHU|31PVe#35A|@Poi1i{t;h%J0Sb^RFT62U9@86^nT;B z5#sXZy!*v=Xs}kj9}&#k#~yv#x;$|V#16-Vk%%=E<tMUEOj&@w%d)p`dnMf(qQ64a zEh_FyGzmT1uYxF`azp{j))sW<=YFUjkOU#FRp>Wo0OuUOBg^eaKlQ~f8R52v2ZkiP zR*1L8vT#zJS(`Av14-}9wWT&_&f_X_<_Pmj6b@hG!g;Kd#4Dh$M?|P|^;!u-Hc4Ij zl9H6qR__a2pU<uW!1lzc$@wTAv02=pP`u5fx~a@~yh9ryM#K=t>0<62TM4%Nqfzsn zZw(SEqCnX9kKZCSpH(Ew<*#1hioHA~aM-~O)3Hw#@NFa+?vSYmU#r&{_*M$OIvqx< zPvKz2`_u%Xx3|;<fxt{H5Yo%p9y!*)p?81$eDfi(8nkFBPFl5!x`goC#~i=(@7&EY zQm=+L$zrdKcd979{S;AnD-jpdgTXu)NJ8b*eYsOb(m$)&ExsG)`Qq1+xgp35;zj^x zf~XNK(3QT2DQ1~HdKosnPVqXPwYcHgR|STUknIrGWuRjSb|qOrGimCT_B9X2h~0QG zjSKI8fbE8F+9U+2ne9o{W=BGM+gbgBJ1e~$yf!Nyh^H&!1X)AR#!#okD|UqV)+L=J z7gukwTN*Yf6#6~EXWviD80&)xDaf0hFHIQdJRg?166J@$q@=C=n^s2R9Xd5dfAM5t zlr{J+GzT1oD&1QV5lI!~66`rzQo~RmaxeMAglj?W3Pd2*l3JH&moh>+suf|ua_rZ{ zTDP=e;dN&2`DBvIf4f=HLJ+HKYZ0L(V)u0=2n?vp!QheEC2~30GrbpG`uGux9<;Mv zhL|MOG%Ut|`}0eT9=N`5ncT;~8cvdsJ0Cl7c%|xHdLAXPbPRjJ`+LEhIiqcQB_VQf zJ+uWt@XrY-8CNE9iu`Z&QCGT8R@zaAD+z?l&8-EH)8gkqn+ZQgepJfV`#~-yFqG#< zth1A@pSt>sU38ioXA`44FBvt8AX{>N{Rk&fNxgS--_Nb_J?l?IbrVHi79EDBd0WYs zmL*zQ(1aJ6wO77=xb9gnf+O$7G%2ziADjt}WR<vEA{mmAZcJq(B6jL&hdbgK>iMc_ zV7NiLw;B$;tIDR2J;1~wAr}WO2=Kp%%X}!+-G9aBbm-?m91>(U?}ttb<|Ow1Knl(a zLpkl01GOfU9}k__)iC@ki3IkL8{mx!7xac^CyhAQ1Aj~^hAmu<Kefa9VWn~?u4_X# z`FTT5Ognyf4MNK7ZRfdI5ph|FWvJ^^e_dHfuczEbFoeLFF*41x#<sq;$UGn_3ZC*Y zP*Dje260<&rMqBUXF=$_8G`>*v9cH@>k#5z*>JaL4Boyt9-btt3BNAvXG%8PC9h#Y z$BIXHOlw_8;R-hykPQE<uWRtNkAM<*Z`KcevFpd{kbO%KX50;|@vmY4+A`AvGKBL1 zRw=BO=71D>4_#zRdCSb7Wup2_;Ocw^E-=I$f)erZ;Q9bTKUIaeMT~K8({LKh`9?aq zx#a2jUk1k187N3cMo84HA0tNexF}Iz7%~XpZhW0on7U0qa+nKwmLXl%Y$bJkte6}Z zsY9B>THon|v@)HkZ=9F2y$>L8iK>TTsLKdIa}+DAJZ3eEW2!)khBVS(l@7-^!D4fn zMp4H>r<sa88bxLu62QeBhGq;;em}Rlev+XmoJ_${szVJ@4LLihQfK$MejJC{^Sj#T z8d%mHi^7tv6#+5p><D=2TTTUM{MAe6*2r>E>(NzN>c-gFR+nvycO3*cn_bMeHI?(% z&uTR5Y>4b-2bss7RQD<tfCB1%a|biljmIBJ^@dLXG*wl{Ie&+6vqVX$gUj<^y+WVC z{Ar>g<}BSLAeun<C<ufr?eD>^^(Z;6g<7lmMcnkG4nYz}75vo^kgj;&J;ibiRh<9V zdEe$EU{iV<{gNN-BeEShF(hw{FPkk=<<el*7zP@i6a#SvzD_18S~>wnxXyFuc>g&o zL0^X+<~m+Cg+lHWv`0Zl`&s4i6rhWW-wkTJ1UV}b>Gl^0Ha!}i+||*F2RTo7r0<-q zM;8F9mMnhAn*E~OF$RDpRAJy~UM%L|67MGzzo?)&vn+=$%=PU)zpa>zd<=^a1hF@c z(fM1bNVLsuuyD-@vPPs%9h&fcpoQLHxWTai@a^18^($9Cq;2nkkuwD)jHfs36Pc$3 z34?{Nu{$K(W2P~*0?U=OLOItylSuDAA_6hNDjvV!W&(>Deo8h0AuumjXDwBKrQsvF z&pJ?1{ky=bKAiJrKl`zKrOn9zTPX&*=wj%MHMA9s?CpCbj^WV#Bpi`14(;*c?#yI_ zjB21y@|9N<B@hMNa;vcgsvempOU%=r(!47iVLL%S20`>^%nAE#!nSgcGkq4ukE4)q zyk7G%=xoCY)<8@LP8hY?1k~7W0<ob*cdQZz8^*~Ma16BO9-jWS{x=_Ud$!{XWRPA~ z!pOS2#5A4U41!^^<y||xC`iufcq<E%Qiupxr-4Mdlz1J3t+3-RF8)4ubz#P#E^M_8 zC2)xfO*;d$%hYe;4VwjEvf;R0&>#;>sFK*aU4*+k#7~mMEeWsd5E)B$P!gBlwZp&h zHdX;SalaVbcl9HI8bnSSxPTHJZ}nyDZR!K_D8-|)+hU-uc5N&19mv7d*Opag7lq^- zqUp1d2R{CEl_us_M@L||-$*s$^`HN+NVu|WY|eYWoVzRkMAJIL#XJ%yGSDANBEQ6S ze$F*Mlp!g<RNIY;<AHI)Z{>bH1sSX8c73gkv@B6@4v3D_q-P(X*FvOWhT2Mk>0SpS z6rfHj9OJt=obK$8EeiStCxh_!BGc97WVz!i(b0V?ONp&hN+{?HK)Aix?89UA2CfDQ z_0%e;hcL>-dZKEIvmI}~tWbKA;gar4LYZhkZyu7a;bk2lH)s7A-{h;y+g;eGrG^G) zgMw)!Xg5Nv0JN1vE<RQ)&=LRoM#<h~vM*y)bLN{S8+UzYLSjG+tuuIR=g;G<CO_A& zRoj@&5rUDqvOcjjA7fsk%f%DXd@!Kl#;p6z?xKr)CWCYG;)cO)@YUnTv50<7g38?G z&H%~x3CA5mzRnoepuy_gry)wu%7GZ4X+o7va8gcdr8gl$^|^E_XmXjLzZNN@JW2oz z@+*lfFil|*6gF^y^O6X}VmsD3H%1XD7s-$=2z~^zXKI%Iy4{Dh*j`}PO<)T$F{I9o z^QeaOvaV3B2^J~0dPr(ASb}#oloKySkaIY$^s5yGlG@g^Lw7CBSIan!9PCq&38qG5 zc=G@fo4sC?H=;)I7iw_#ISMYasfW^rBZbj1EuK1_s&3Zg2*p%&RaP}Pa4TXSSMyDM z6Cym01mK+2oUaFBp)&%`WXtFFd3`iB-#K=|!zo5(bX;^%Mo!9@M9D$oMiUCkHL`7J ze^N>+NlAi2H~bcsd=?YsM38Smsz(Wr^)N|LL{E60TEFUaqyqq;h(;w95r{#&C#XAx z+B=}4eh|VdS(0P(Yke@BUM>#76$F^<xF3c?ris9g!noKf0}wCJEokW{wTidNKOEou zYJ^1&$8w0jKM#8v7AdvJvAc|;G3j62$DNyo9FNEMm6Yes`31%B5C)4Hs>F4G)p7ks zC%djP_kJpiQLiKbrU(w=gr&)fvES(gdRc4{I_SU3nzS24F$u|SOh`dzZFD5zJS<Ew zE2Ms`G#3DGvWZx-x!%SVk!~pBja2RM3!fHLikdigU<8a|DkS&iVpQv{BX9I6wMMOk zLG9C=h&v;;RgoCATJ=B)@$%f5$O1)`^}+X2B-qm_((#sS;<B+6L@Pd1RRUbOk>i6k zNmlD*F<xO#+DRhZ?`YESL%6(yk4-zNO7q&99k|b}c`m88P$_f-HCVHtkme?iV%(ZF zspl&kZddd9*Wm%P4rmo9&*^<((BE@F@Igw1=VOMDEGjd<JT~v%(^{a??Z}B%P9n#P zMUByYUtd2V0=E@H$CzMLFgr9Z@}sSau7Aoel_!d3>lP*W>jv7Ok~ko*Yum{d?+L{9 zviHaQhlq{r{C!SVWSCckj}owr@OL6yY2uNWQz`J76%}y78s(nLEx{0YJHl$#$s}Sw ziXQH$OSi#&*!X8{J|#_P^JW$2TaqaQHIp3E;FK?#qS1G1@n7rIX3thz5@sHPz9P8g zSWbE1Kx8<EjC1!Z2TXo(t`8l<rJEIiz5v#HS>%|O7Dz)=L*|s`8VQ!3UTVOM^$Xb? zB&ei04sNXK7NB5LPy3^<fBZt9JdZBEL$tk<9jN>q-cq+S;U4CW;4(55a;6Nv&=SdZ z!8++eABNo*2owHt+eSre-gVA3BI&R=nf<w`_e6_0q6=21oK!V{a)LX&HS6}tdp1sI zg5GEy8v>LxzuZP@A$h*#Jv_iWQ7rumcS1oZ7Xe)|H(eE0EaWHelo<F%!+Uf1og}mn zB^n`{@ru!30m<Ec+YFzg{v4IuS>WswdF1Fa0`>MDAu*y2)GRCy2FIZi2@ZvQc)22* zIP`TKvDrsN%#r#9;v!{>_)E{%BvHteMUWm?^exj&5Iqx>-O^KnU)4uqfCG4S*U9`F zrrlLOSD70E`DiUp@Paj0XSmDl{>ok4`|Q`5RCLM|9o5gwJpTRC0EIUbPwEOde;<al zg=Xs3g-dH0`(-eg`tl$z@?aCERE4jQzol+)en6S@q4g=6skQFiM>E(YZTX@SE&|6! z;!wJc-+L5dhNRiI1?5=&$rL#CTB;&3?z<pBCcKf~3N%MJ{Aw4|*cnE*-Xy)pcI(>Y ziId2)5rl(xa40;*?rYmMz{aXwk~GCtcYz`D(64oHEJVI?_`-%Sx5N0wS?!Zbh@ob= zUy9Hx>~C2vjDE=2J8m&~Uy-I%#{2R2!w?f}Z78-Tt;|Ed*NmJ);!hLEr&L5U1`mTr zIEdE&JX3{X_wiA%l1d{3l8PEgW?{F;C#^_$E*~X&o}dcI^@i>`+R=*?k(K>#en%+^ z7^y)JeP~a7-(3GAqmknvDKW3#BsTmHTRNo&Ee&LLkLO7#`M*}me{yQDp8x=2-q2Ve z1OYfv^Z!kap^xe+Sh#MoffjZ6KiKyyo4_sN_pBO^CMl|aX#GS0N(`j07C!i=)Sq4v zLBQ6zLz43U>hWi(iT6FfM^`Uo`nQ4oPfAZ{SO9X*))qy{|K<e!S#t1%pw+$AOFIL) z7?=PoI{B=aZ|?5m@(77P;20=t|8*<&@4h(YkZUT(!ujP|NY=3F!Q5Jl8moU8)po&z z>4YVt{goZ^A2tgjf>ChIxw9k}Qeyc<Rqd^VTSt4O0}jfLDI@<O`j;63YhwfYDRjgK zTVgvjAKpVq9^3Y{TfL+z4Zs#wRQ)Xd-%^mGnILvbn2lwLNn-!nYJW?XDhhemcuH7% z8Rh@TBxDf%#{fiAiT3?d<9B3aVNPvH8J>SI@;gr;ax7CwIOjh#VuGcTvdvB}uTL$l zEiAYf!zZ3<12k!OH@iR>7?3<uzF1Sq7i0WQo(@ikP$D2jQdv<&Yv~{+)(5Dl`|ah_ zObIklRqcgITss)%EOJ`j%q|(7zwzu^6f*$W88A05*|-Vl+ZzTOYuHoLXEdgjX6g7> z1AP?mxv7Z%X0+_D0C*vQnue+I)<LP$)WJci%0a^4%*Tid5i1FhP%caUyR3hX*q^f> z|A`wH4b|z}?3!++sqDTm@IO=S-=ivz3AVkRM@y!#=f%s{&ThMU{>0<Aur>$D!t13m zY{2#GeEsaC@wdr^3?YvJCemhltr|gc5~~H_!DOkTG86dn6!##HdxBB_;RB&>Q%h-U zRyc`(4k`)`n}4@3cA@iGrFzAZ&pBkn%Zr`wi!WlUZY(Fa2-(8l?e&q!Aw7#9_CyMe zKC<h=9_Qw~CN&jZ;AuAL#qM**`Z`s{jM-SWW&I<bPh9zYm5#T8{#R+@XKWM{F6)Wa z&CSiJw>6o5ZCzC=l7_$AOc=ssbRPi9YubO}##775|0$c97>W&k@ahgpv%yD9RP}pW zX=!6zTq&i1JCj4&fqC9n@2zI@2HUl7)_bw_g#Pz^odhojaUG4PNpa8+sL*Qu%xC~5 zbq%$bV@iRKABQN7xT4GF1~k<aZIju0a(lnXMgHer>)(nN#dU>~01?B$BP6!_dI@py z@O*uI+S=PCI<LO27>SAXPvr1al$UQ3Gchr-n2m);Msky{DJ^TH27ss|#T_57`9F>J z%EiR^uuVe`nYnXr|3o`|{P{v$P~hwPA{GL9x9+?5mg^JbU}RxoVQ*ipo-!A#-(w<D z)+^*TK7HTSbl(+-fz#{Pr2e1=m6NVDr(m&M@03?(YI1Z&YPNK=wI#%UxpLCr+w5J| zx3V&#KNr~##y2|v$yJu^qIR{}2_Y;M9UDPDCb=}dO~OE!H#GERD$W@(RZjWu?~0#7 z2KT_4{QUfy8sxL=9KV^*azD2{mhs}D7d$sQd@tgIfk4p4oudTg0ECXK37)PFhb>`{ z)mHbja<z&Hfw$+WJ>aR?RRdj?-09~f=iMb6_VC%SCo5;2XQzoq@V(z0E|f(_hSz<` z<U`JVHg&1W%Je(851-B+L|k3fL`8wAiwk$%PZhU2J04yhP7qAi1G3+Qz>x44HMBqR z+IRPiXR)8I*Rv228EkEn<KSda%fd2=MAs93U8=VZ3oG5Ld}lhAgA^?tfVA$-K}$=^ zM0JqUxN&%TATKX9yp$tXf7X6=8*8hhLh*STxu3)CQ>WnzcTyIW?|bN2GOAUMO)G#f zw=n0|;Fpt@h6Taw*aK|$cKYUSD{<+pp|^Q-G#FCAFUF6+6z$TC4GDpMpJ%Q3_WGa^ zv_O$`r6G!|xdTyx|EZu{P_5!C^mmUSy6m>&7TJj&_|vC=Q}_vo%i@I0|5|->mWWYS zcyWC_H!F+B{q)nS>DQEF&Y5EaGDrp-%A&fuBy@d!eK2Mpa$r!JY-DGrzdJ{D#jz0! zF=3m}w2qEW0U9bL?`Mqb>uY2@AVRMDx~GARU(<E#S%=KFmU#|j`e5Z&u}K$0an;Qc zfAiaVYrAioee+GMR-M_&a{W=@(<#L&w0>r%!xV#E%hKp5kM|ut>(Q|bE?B%=6>0j~ zxNQ~Pb4^GFnY;Vr{azU1V!g4qmA1CK`;Ra}zw>eH4(F-<w0O_A4qpY{*L|Lfbt`Mz z8zjI-Iy%p@HMPby$A#pvGDRNQi!JEWwPs^)CnsqQ_l3i|jiOnOjt4r9ByWHJjic%g zd;)^l#a6eIY(8^(rm?Cl4&t$Sx~#H?vAQ2xZ6?Ds44toiKi_2Kr4V2d{cg+N8tT7) zx~w0C4~&U=dAhZGwGr_!F%cBpY^_)aAi!0Zea+-^_gjDUweNTx=@FCpiH53{1`1mS zP@QJ5x6e~ji6=h9asf4R!O^88r<4#CMJU_omb<&<1~kPY0*K<IT-@9=$1oe{Nr|b( z1jhLI`8$U4O(;S-@;?-lgU=+Z_ZbOk1^*H-i&W<5j}X*QQ%B`9i&Q~y{oZ(Yd((2$ zTB|Dbi`EOjuHolc*S3tQ7)}u_D{m0?y<_V1q(Sk`wTGuSix>+`QqTA3s>g?i3TZ`5 zC6{&{ZP5?O+)G8LHF@%>KG_aZ?0>m3o2=pn>Cd1Gpo;na9*fAlUoX72$VOT-`MlXr z`*Zoc&)FRsHe-C&I(&I1QuFfkKYhY)lr(fU5EqA9S9WxCw6vr$-5bv;wc?3EAv9b7 zgw(3IOcjbnA}Hf2eS4ohRXUAMa0|A;8$xqO+~o$>tKhqhnMp-^D^@;_4Zbve&l?#A zehpRC{jZSld+Yb3St7#1Q=AL?UJmtSMgks%_xa~7dwIUx<gP}tvb19-y=;Uw>rGp! zI%nA|?gUE&{?`c_dVZjxI9OORd}?Ybd)e;mDH{4{==jR<Y3@gKonEiarT)I7wl=(e z2M^{P_Rq(mS$6zSt0`>_$;Oci&reKXzQ9*6?<W+SHA-uS&dc&a|62in0Rcaa1kG*2 z)3%$1##L@QYwm{Ssi}t^X9aV)JkN!8B!g6pU#1Q)*#_nhwa>Y^r^mWHaIlwSm@Et` zboA6@lzeiMl2&$hl^+>Of7Uu)>=e^BmRC?Oxk`8DLtk7(%!We$015i)P_;)TCz&mX zz*?Sd{}L;X?Gj>DR8`&Cxv)jVf}eS<C&7T>6pUn9tcXEKuUdG;re%@0JBAt6I^0M1 zRZWPLg{qwPo8wLrzKM!ETcMqs{g(=323tXceAoe}EV1nGQb{**oxajGYAiQIs76c@ zlT;nJc_SmRl7NzZDi-jA@`ZBZcG4P71n931qhY)|s@qf#QmS)SRr`d>s!)4lJ7wjS z7B)75(KYAci}%2Z+uNXLaLL#*GRo4zj`{giKCV_6L<w<m(zZ1-k*p6NPRyd%Y?ZkO zq(aJJPMxZXnNc6gS${cPH+*Le2@4@Lw3TD4!>55fv90p;h30UYDMQHn8WSBUF25WT zC1ZdR$X&J-FI=xTlL`eVG%CWqO%st!QkMt>gRU<F{n9PwrJ-SNpNFMWf`J}ZT*Aso zO=l60D_sCsqh+8w2u1et6q6W)f`VjYV^2;-2iGuga8OoehEmtjQBhWnPtEPtByBI~ z=qMCsxv80~X}X8lfq;GZZC9=!Wt4pd!OLJVmv?hxcNCV?RaQpj5g_(RqErYyPT=%? zbU|#W<$isAI@8e5s6(ph=-{WJp<!Z5NJf7NHZSw}aH{NZwL7l(ff(4!Le)!5pm%{r z9dh?}&{B`Y=b*2vTeaKS(u{$*v*CSrc7guT;xLtn=jW;7;Q=&4^!O4?Lz$_hq~y1E z(pf^CuIa1$!RIWJrka9&VRG`m+xslr`SgKNN^NW8s`L9{701Y^G#VP4@Q~*WP4z2g z_1A7+N}$IDdD`nqwg2NoOy_a!jnCu3GS}5^p6`Tb4I)|d`F3>|oxblw2xJtdNGE=K zJ6d8=r`u8r=qsr9(CMVQa+^8-$cL~|HT7hy-92&&xqzH<9q=7WhRa@-E%_?CaTc=) zJ1aYQ7{s#j@~+Xli`(wAfdcnNP3wvZKvC^RN_u?J!ur~rT!><FP#buxUX0+j8k)^& zv#pG_mt6UPAn%gL^7L$SJS!-uCdn2}fqd9FBC`r}-Qkh-X4eo4ldZC*riZsTo+D+v zP-1!$Cvfn9vTJU775;%tN5h<QoR|!ZQbBffsXmyrbqfXMAgNT4INv<y!(?j1{(d?P z6`COy@^aftvr9aH6282sN{(iI87!x&tZbLo{FBdf@vsVZ!(^^+Ggo(af%6^%(0gz9 z^x)t$GCER9J|kRMCo9YP$ja*^wrs|n6E7gNbV4jaOCWG5j;x!wAO-<Jv`}#Uw5Fs~ z0R{jcBvyAd|6_x&m!67VIx{|+>3DpYBJud-D2s~?ZnOXu(A-eDd619@);)8J8_(q^ z{H;Nzsgv*#Hah>izdk*DRps}Oyd=!@#0ea$n3(wK$q5I)4*CUJ<+8#V({*_o#RmK2 za+y;Q;O!{J?WD}hDoV<j_?ckjp@f2D!I-y9<EyKygCAx@m8M>Oy`2J>d`DEVnJs3x zO%X=7Xbh!{h9nnP#~7N9gP}u1Ls7(NsmFqy%;mEtilwie{7>~*W1T2J%Uju7ucWnx zy`U#eQ(j%s(RO^VuUFGh)1;ffj;8X2zFleYrZAO-nP1F=QL|fXLy1wCythTKs&md7 zBG$<)mCF-?sstk!zOsPx+!wR)F;Y}?Ff`PA-yk#U_LR0}Eoa(&ed^rpDJfGJ@w`2} zlqd^PlNe~|#PvJ;UQbS{@~=GaqWwlf{_CAL5rOrR0k2t^nG<88Qc_ahe{5G7e#PTT zN1MpOw$<MQ@Az|3P?8;==r-1=o13){DX~i;$F0|v1kX>?z<}r!E@ktvi72#)npp{M zodo=7cauDPioK?Hz;Ky5{~|;>ICFBjG_TV4{MGx&%DPF<Zvd9J7irx;&FzAjYs&qn z9LLXz!e2#_U^GNEo&%9}U$$Zv^de$nUZ&!z!>$aFJo3~qFjQSP2+xA}CP|1z>l?=% z1k3O{Qy-afCu<nP*5>Eu<w;gUyAUY`>B@VSeoYcxn13O?nf#ax6GmXiS!~lZB=CH3 z<Nq)d%<<{X_~RfXmM*LFJU%Q*w6ovg-Nj8#qXSBHds*d|^lT>6g^81$)n;FsQ)bYy zZ;9J}jefVC>S`K7W9LJLJhNTM`LsQoM>>_M*=w!mm&V3FM`dJGW~Lt=VpjQ<Bmqjm zl>WYfZj9idphOW$yNwnuYVOZPMRWpZU465xb;E7u3b)72S2ld?@&ab^qd^Zlr-s;% zGp*B!g<W09at5|xCJGa$A5`khdQ_LzBdP`X9EBYniCN?1PsZ2V9fq0jJlrE$IoO0G zh~?1|RXoTQ4E^{aLgP{UOHIegdZ3`xk!VolAw%-~1!ZON+lz9+pPb7GrQxdshC+a$ zzjk*g+4x3WuwA^g5ELAS(zy`|TkX%*#YR&_0HdSQ_r9p;<LX<#%ySVIg#7@-o-`w) zn;G%sE(nhAJ8S^Ayku^=yMexe;`vVWSUXp1mQH5MT({~0*fYUEIOu+hN+6SMv5AS9 zkiM2&04<6h&<Pbsq#qzIzhBb72LI#w;wDfqu`om599lw$IAaJNsTe(Bny=MeK8K%% zjJEO%5n(GiI{-2-JvCNsk8n|;9yp7xIG}G8JY$r4&e253i<t=3Ka$Pe{`qtH7b1JB z`DnhS)`G$7gq#eMiM1>d{nfQ`V3CrLt{j34r51}h=C3{b)$@bvj5@>P&Ck0E@ug6i zMd)}O;k!axlOJ#nt*k-ATq?tf>SH7DR`?G-7=yNhvvIB*vy(~m#K$vYGH*75d})Xy z5ivIAYhubMs;mhIKt+`g?&p8{Z4Zkgd3XH7<0g8z?4WDA3kylwAhJ=n1N%TQkh9or zdk%)j##U<#!DEx1s+ziFX66^NGMg%<hkt~C626CZy}%LAp0LkORs%O2m(hE0nuJ8o z7V$_tVu4W}v<YgQDVNrriVo-5jW)|)7K!%0PH^w7zHeYG6Est(eym38L1M?~d=z-1 z!a;O_fkJyZZ{(>^LBYYNuf1oD_A6ck8Rm{~I^EW>;n8D5W56EEHKq)+iA*{C0jB*~ z3|h#y+*oip8k8Ne@*gj%_+Fd{wFGo}r5W6f_xLa$j~R`nciWMkEyoY<_+QU^SNxui ztgD~8(>gEJ<e*BCjzLSCI~r0KQXAMgwzwPXS%1c&Nd?ESvd`XGny8WDAj2a@PZd&E zWe!m8rLd!iv7(*@ff%f3hxHvV)ur(=rP#a%2AZ0d^gSxiP;4diMzyuKMF@V|W8~#U zedz7!jZlAhf&@XIjlxBSqDLSm3r|i@E?hR+frJbj=o@HgbXWA0w!#%nM$3ZN6VMkR zBZd`~PX~W8FpR7Duw5t#cV%g32@4DV*+W$pK06SJ@`4B+UY40L-VhxiUftT#YL1-z zo`3X7935`CO8`KZkrUPc21-sOpkorjT<^j<CGou{J3cuZ3@3E%?8lmjy!i*mn0W1` zNr8aJVfZ0M(dq86W)f2)Z6!ng6@KE#T1Q6{b=xWK3~TF(Etce6H8E=4DhdZVd2(X4 zTHJnB71iW3LS#Tc`K45v+Ut*ysRc5|(VB2c+--WVzE`RsY*wLUWelznecXNu%hrMd zM9GXLqC+5pj*d29WkO0ff1Y_{M0%2<tIj?noZL|(C=yLsnK3=Jh9W8q!)Tajxxo}I z-_Qx)2U1WJ0Ty=3wk)ByeNo^!yPT16GAcHPZQTQi?X%C1CsE5iTONb%>q3MogRR=H ztGo|4Z&O#R)t;D%g5)HLf8(qfJFV0F?=-7F&J2wS0*zZ4MBG600=kI&FM1+Ww&u}B z0-n0B9mcl;9s0qc5xdYeQz#82$usW?yz$x+UG7fT9v&W=6rloDt2PZcliuSJyQOcY zAt5164Oe?V^gI3dFvKM!kf??^Wr0sspPCcVvTE#j*^Pn&M8=Re(nQjqUxFA5%wl8s zZaUvo)l)@gfAHV6;rThT*n^tRtTEVknBKNM$fVJ6xJ44g!E^3=WJIOW=J8)uQNF%> z?BqfrKsh}+>g6%#t0chEyT*{$v1AXvixya(>E#ksqf4wAMa&HFh9xv3B(kGts3BHN z&1pZntmpuxWlRkn2+ce^Y`W)UhuUUELWbEOaMx;X!zLhPK2IU~VVb6Y^DBzs<<2<n z`KVSRwVD%dMI3%_Tk!axq&>1%`mVi=P8}&aBY@Z*t~;2cUMa=wgpp(s%&x^*t3moQ zRC)a^5ExCg&RobJ4=SP0aIJjwZMbh<<@O*YCAI5Y3wJB+V^+o@jiec3vJh1Gr4ZnK z#>(@>7jE}}-4l=nHdT}-l{f8*X0OfnzKS&Pv$IX>38c)ZTv{j}LZ5GbrKsa^6o3Sn zC?u6G7|zWbA$ItSnX0qlnI`CKgt<*1bPUz<eF>R+^rRx~yb?RjU89BVx>y~@JyL8% zDkF>lnc*sJfkKX7ZKdQ%N65gmHn)e?5+XPhc1$PnC;l!c7t^LESuH^$LDeucudX(p zUS%sVFZI~{IO`wX`8`?=<&iXVL>mKpUR{GyHr>Dk+ee7T1)uz|5uH8Eg^TmfkZRdZ zHC^3Xhi8!`Gawc-gO1Dl%ejQ=NxROPZr@fnupRqaj74!xG=gh}o~su2(ezpa1~iVG z%%bkj#pSYv0XEPXq@*gBpv%<cGj+!|xHJPZO-%?;O%XV3>{Pi45!*wAySppQlIUpJ z(DmJzBOJxWPCt>?-`?5Q?{SAP0A_~+DIeMco^#Rz$|sdUUmh0AL$%4`T<{ZSdhl*y z=D186y&BEwIZjW62haUv@mM;O2$!0rrKLecT)jaz*tCkWGArT4c#QjWjq>R-W1h{y z5*zHV0m@!Wa2mm%;NwtVK=Gf*8lu_3!N96Jc2}NrpJpw!7T%7rSgVw9IXG@FeQ&0# zjp2hm7p5R5Y5P{5zKvGDuG!mp-K1doofqW^yLyn1^&+iuINxSN!eQ0<Cb)v8!l>@R z-lZxIe~!tc({6*PTdMV4DR11ke>BxFkilLLf{fDh{1w7R*w$$=to*J0=~kzv+Fsu~ z2i2*D%;uI<eZaEedN|232<7!Bl7_Qe!f61dY8=4@jv}%>0mZ?ulaQIQ12($opIbul z5>FMKMjF3Lt$~}jC*u(^l0-wj8W_DMPaE$?F>P%=03=+t8GN$w-tBIA$zS0tCYOlc z8yXuOE#l=0va^e|$YxnjPA3o*#BsPD6k+@yM*Ish_W{j_P#S9B%db*2OojW4oT{Ib z2;DisD@B+fhS;+V`1snamMil8(TzOb??_4dekp#yeIu4+1Cc#1uvYD7q+EqAB|#z+ zO;Prz5*FEyQb|7MF-^6wv&(&dF>uk0$RM$AehTpH+Chvc0TjqQ3d=7NNzex-*E!hQ zR+(t{?gh2>bqs-2DPLMgJRVtH-R+sljtfgHf(y9A8qdzt+VF)75fo3i6#K3!5vrm& z#D|H-bD+mP7uR$upYVO&j3>0hF`%2!Z-xS+Ep0AKP|2BMVp8NsT@sLe`P9|5<Xk~* zSRd@TWql=+8857Zd)~%q=1|dctM*O3lOE)TQeLv4Gy8mp$ihF5g4YdEZvq1(8PQKb zNxi0+)1znFGPC`yk9b(NC|PZexs;T>Wiv)HoI9U=FZ0)LS52Sb0u51xxg*SBC>>Af zR^#Flz>ZkHHam1c=Is=nD#x`j_TZN1C-@8UO}NJHS#L5UgF_3Kd9?R^>)@0<msT42 zRua`COt@V_%FnMCHWV~Zv3_Ovx=YOU{n{_4p9>A9LzpXB1Zg-hm`<<dcrHy}yX6?3 zgjXi5YV&TJu;q<_-Qn9-6_G&P2K;lsTxYlWTvPhe^JUbJ_PYrk(H*xlHEB`Tzy24% za>0k&)^j5t^}~l{Qw`PRw3mZ|r*;3g4{o)W14E<L&jZ<9SE?d?g5Q5Chw+m+Av&+O zLHa*k^;!$OGSXNNB$|#s$o1^ydcuN*YUI2hQLAa_|2*@bR76S%eoFVa;;-d_Fd(Gp z&U}2-*3*Z<5g}DX1SBr|4x-S8Z|5HC78GnjGy!01FEEsq^z|iNk~fif1VCf;Nk1|$ zn3fXmEy2QF8o1bG#zaO3B_TtQ-j|(WAIC<<u6MfICuDp{PRU8COWZ>>njT7j72?5X zF|Pg@t>p}FuIqdeQSl9$jR*uw;H@h#{L4r7rNksXB3?WdPaIi^cQ<!*(*U57?hj(r zjVLZ0>$@D~!P*nIqsPDQl5|g*17=H?@B*}XL4Y-)y^_Z0sWbg$3Ih{S!|7GbxF8m6 zz>4-jMxd#o!RI?*Gf`Clw;3e(O#!t8x1C6UPMIk|Fm?`Q#m3IYAgt=MM-m<$GLfn) z+uVL8x-R?p{yy9j@!jz{pY5ZpM^0$xE%PFQ-f@JD&5>yoRjaLMn%R2Cr!JEtQu|*8 zs151!sv6~zq&)V4B~2d*^En|$QdQ?5!@02(*|?)dz5uIXF(JXm4K>Jmsuw?wYVdyv ze$)cXcY*|4rlq1TCi3=GVsbPd!mv*x{w(b$9}g7<tQr3Q_<HN8xSD3+cLp7NaF@Z| z-QC?K1b2cv3GVLhZXpmLIKkZ|SP1SAoDlTRbM8IoJm0t8b^q?Yd-v+@s_v@#RqfQ! zQhPnR!`m7rCWATCcU&Q+U6xvuSD6mohVUW0_f?U_+gnp&=!lp@zupTC&EZ%X6u^R2 z_lPXgzU=SQ2z_(P<`#feqTP{CO4=A*VJypG8BCq$A}Cf76#ej7baLKN)!N-o!xhp@ z$W_Eb6ZHJAP@JAz@1h0qLkQY8RUY0}Oi~cK;Ddv9{V#=N6NtLzdu$>sX_7%gu<&xl z!4Ie0(C(b^4UeAMdRGOL!1MH{i*|=!&*5!PXM15YTWKfUE-v>2niOa~qCH*f?&kJ2 z+p@ce?!UpTkx#0nZIus?Lf@S8IjtB2g!aeAg!Hb_NrD6#LUCGoC@E7O)}ik1Q6-bt zzYo~q@LdLFlpGw<-ab38HKtz=!q*+4fr6rdcly7k<f&8F(*8_dK&e`ayoir`ho2zC ze07H+gxL!fQ@P)(8mog<JY?YEf8)6u8b00<a<z<3LJo67qQ-yp#Gi^~3)qnLXJ)3W zWDCZdYz67N<-~^SxS+nL?65uN#I5O%{8G;xNxYSV7#`LOM}Z6122NC|oW*T}TLgXU zePZeRR`Zoa9CYPMnUI99?=W(I=JoCSbbc$qdVtI^3T`pngx9o?4#*var@39kx40xp zhsR<N0JeFFJfJ9HN25lhOyCc}NN*a$BXsjR-&?9UYu;O8hO-_EB{1Ame_}}$|Ah_v zfEas<C{)Wh9*yr>2L2g@GyWOgT12E+KZ)d|QfP4!ems1M{ujs^Ju<#k_uj*k%3V9{ z9u}kPW!}lVp+{UgS*lVS{;S~4ab+Sm8mLRPN$zQN9*`p?JpB;N;s__6Ra*4n(q4qR zYG^+Q)V70-EhX7sRfu!#z_w=z%a}3lH4-I%3;&SkvnMCl-%qt&KC8`7{BwR{9kE}k zbPJ<21TKV~|FW2o-Py5f7mkM5LxcEl@odx}OB*&`$R0Bs_YXb*dKb!4D60Im8<9(q zp$=C%gg6@$f%KS%kB^5NtC0G27~QPOS*84*iiQXn8$($X(`yM!e^y9eXpd4O8`u|Y z)Fq7YQUcTO_bB8*qHf~#JdbyqJ1gjbousxCAISvowK4m1QF%%phVxbrU(=#edX$54 zXPBbS)j8k!=3aZT?6cMFos6u^9Nb@A>Fa+5hu;zX14Vr9!Nv|)2?GbZKHYy{6a9l) zza8xtyf>2CbA}9p8116hi}ot4WJj<$Eh6|9;cDV!7ejh>{H`GsvJ#>(V9}3nB5dEU z&>ML^niJq0BmPZ9N7w`YkfX$xPTdld#pYjqw-Cl<zbJ0BSR|w8v4$YC4MP?rKRu+w zknX!+bLtz5L}(k8U*5LM|MPjDh|kadhsUqqwtJD%5?D!*KaSB&{<4{j_3B|Qv9MO< zIiyFbW1w<bjBAJZC$*lhe`7Z6A~`SxsqtRlXr#}w?eN`_<|U8Y8e45K;YTtqGX|4f zqoASSvGna8GNoVYDGY_H8gmc#%WY^bgT~}$imY@^+N~DrJc@g291V^A5IEh7IRT_g zH3#y=!p-p*+3i+)8Vu9@NhNFLZON(6OH1j`nv~mecof>5%`ynxiZx3+7KR2phJGn0 zIE16J0Uvy&62nvaS@xE8?P@Bd8j?~OZ+Fr(dFaiWb8v}Fs}eCaM@u<t4I8*$m=Hl2 z-())8BY$jW_Unv<L=*VY5UJnJ*~UtX%HneaFd%hnoKDq(I{P?!$BY;z=?pOF$pgUj zs*t@RYn?ot9=FfMzN4nvI{)XI1`mYDux?;uzQ#%czH+4N8~wQ5<_Shb!sc9|NEdHL z&<+<<{5c>$=xIGs_&Cl&m5<_-40EB0Ki%3k{6PmEF>NP9qu3oYREWVOTT@m-zRF$I zQ>D};;x7um%~aM<%Woxej~CZ`g}$+fHX?XWNQ+;bJ?;W~kDC(DRCs~GDYHH%3Z?qM z%pIn>y@RugYC)8{(;hVj`Sne)vHh+$j%gXxYfB`;$$8|P&$RpiTg>$<MnU3GF#ii8 zekF@e`lEv-t+D6GQJSlR_=4;e@Nd3FgDlN(xJEQque=_91CE!{b!Y&)nK3CMF{FtL zhf$}o2WU=U(ys0~4>=qJZfs`&!(C5rVx{N_eYJH$!y(4$g4#Rmq{pAdwOi&D8B3Au zc^Sp1FC}no+c7@H88P<JJAEWglFYA(8fV{Pik5vHI1}H``2N3~!`4?QndD)x6kop; z+C4D$!NdWLka<n$v1L<@_~l1D9wsdCV@qZm>I-CGmQR%$_XK7jn7U~pF+S={+atk1 zU|HHdsRRjIGfIt!wbMPKZy5)234)NMl%r0IWoR97u9N-^7s35WJW&$k_`hGJ06}v; zpj?bZd6c{qm3Wl%*jFfC0JSeTRgsU0I<j|_Na2B?Wm`J5?k?L?c}I056ll<K)Ag%F zDTvz*$ItbnO9DN9XDmaY<OaEhzo!1bu#n&dP%sr%zZP+rKs=aC%GlL2zyJM8{21Q& z)$ZehmFS3t)JXPJdyN^6E32qiy;^QU9I3nv)_;d1#lZw(ppUN!$fYw6g$23}{x*N? z8uBNP(%yM^xl<=@l95a9vcjR)zde|RvX<dv>ip}|K#5)%Oae;DsF^9Y;dOuiiCUt; zzg`;M=y<uAX<=}GZ*noQR0&ZN%Kl|}{g>l;7dsdNDu$u#orw(G#ZFsjTkc0tv)ET$ z{@-}|f3al2*fPB>eBc6GXPWQ->#_b<Z^6(SOR&i8|2JC$?Z))uKM2#uV<SJN|BLYX z=k@>J_gn@u$K%)t%34Q)gT|z3&)=^9p`h?3w=BCA6|DjInvVbtwF0zJDpNpn{gn=C zFVF35xB>&hWw^vC5iWTm5W2(Aj1%A)NE1XmHv)updGN|`387;Gz)e~=MMmD0iEq2y zuAQT2167f;O7)}1?DzE|Ri~SNSvOfAI61wP(tqdXk%UvN2d-Sg4FY7VQs$VQII|zR z8&mmUqkwT0_o|Xc<A6HdQ;xz7x<iO7mUIT=Fgwf&c7myvR_v6uP=!MLc0iPENN<}| z@5aW)=H~LuOs0nL*|ZiO?d;D<R0<`@0of~U+{K@Lz2bpTQUz0Sc6D`WZH+a|C5%16 z&vgnA5(3lJB`mk%A%}=C`(vF6+8~}_?;4Mx&k_uLr5gYUW&@#P(+Q+<QYI8vHw~(+ z9GPRnmh|!XxK}S(RJcMB8&XgZMig<Oi<|=$1@<-2?H?h~2|o!l7`@XjBUm3Rh0t^b zP_Ps7@=~|Ijn*K@?x?^`+_1`G?Y%P8z?2+P2@@Ou)4^+mAp|dL3qbGOu;(!Pyu(+3 z%!B@2#R|ZSVI73YjB@|S!GCZN5b-5aOZWPQaO>!_zk-Z7(f4nh37n54H&3hAItesv zC>Qg3cXwwn^;-fcx{FVH+)lC854zR+!R_mZCMG_Z(onf1GHYTD(BldDO=hI!fS-3M zpZd%pG@K*p5|kL36l_7sL(Apixt_BwYY%_~_gn^Ztcqct5P&?q?wUP3{Qcr=5kM%n zJh4C$*kcaAPAIPSIaH^(l(6@58D+$_Ql^d#0r(+Y;-8{rTOpuZF2BETQKD2VVkN`n zm!p{CZ&VB0Q7X23pOT1VLxQY<RE%PrPlO5!n)j9zMIs2YniyQYQR9ha9~gw(;1UMV zpb(y0Ryo4|3iAI*!8@#fZUfc4nEx^u|68(xlc}I`u}7eY_HXs{|A|x^^*@ND<_F5= z{~&Eta!|H%lp~Gkzq|SuN9m#fRc{?-Ytpv=VK}l0P|`5jf}ZDJg8Se7Z>9XBj}=;? zXaB=+;z)oKmo+Zl%UPBtCgg!jE%&+Up#d%PWr5n*Qq}}N1!?3x>SJ&Yzl6q;*o=?W z9P;N9>#Yh33BBD`im4QezC7Q|kWVEnFD=b^n49m|@-luvf(kzu40*sq@rIMwpP%me z*lNoTkpmj)>g=vN(oL%$JMOQfpTdD3Z|CI3v5(Hrp;!BHT)lU$CFyNWuU%bTD=Yfs z<mAie{U91BM)maV!}{yR+pF4YFUWy7a>(D$kCBmaXJ<!PSomGz6G88~^Y^KrQJ2Cf znp#?4+7F00BFt*BP$$O6vvh<-L?VsH*pUld!oY^%IX5>q<o;)ct(yVsA6j327e|tr zvL2nE5*Gz{r&bpin@wa)ttk5X3d}?U2@ztI_5&XVF2w#?eqJgOaTqhv(z=P<+{`X4 zw3*XFz!xaMIjOF%uP6u!c}`w*7yb}GBMh}QCa0%|=}g}ftHAL(u3QciJI7ha&SyZE z0#zIk;<42m42-TkU53WfM#ux7%jz@=c)`Ul<KyErN*T;X-_GK=rh~u*yv9NW3D2h{ zZ~aRG1$mVYWX<1wZ>$M%27Wk0QA8~@k)@?2o--oV4-zXA&<V<5HLkG^s;gtp`>Vy2 zqSX3pZtiLjTkX<2&EM>G<HAWKnAR+mcHv}+T=;dRl)T=+{kQMoR1VMKT%hPzIyz$7 zqyo_~$5-J!+m;-?F9tVTuzxZ$GrOVu(;XUGcD9y*PI!vJ<ZtUH|L?hYj{{D^b<~}m zxf@T9k8e+Vso1p2S;7LwR#2kC=X^=;V5*Ru6jwpf*+gt!^0E%^GXh^~r#l*BXbpHx zk%?QPWOV~dr#_BPuuWz22r&xg=&BY?RQl^~o&5godVBBdqo25_C+_9V=j%`AdVUg) z|9*6I)KpVRK>-O3P3Nw;wY5o8U0>g}Q6B~dMxG>3Q%7gW4;Tm~Y!=rDmZ9pWudi>f zQnCjD9^TX2yD8vce;@v4@agvE#zG)N;3yPB)O0Ke_19x%Sy>n!<c(YIfXY*D9zOUg zLfl9$bI5NH7@*9Z1<aD~7LKepgL>F4v!K|}w%<D`JRZWzp`oGafrj#OtBDMj^78VZ zR?qX4R5@wG!?MF)bTU7G`<8JCG7Dt1Y|Iu2-u~`+s#_m#5MHh|{P6nn#86_5OCx;D zEOc$V(qx0-ovr-*^0LHIR+0h@XwjlU(KYFEyK5@O#{8%#5Y-Qxrd6-A6$w|~@9gj# ze3Sqs<4J)@x+oPk-VbZduDh~(X3^M4b|H%N;d^FgW;l>=hN${EWG%wwn$Sr-dDRR0 z^2Z654us6nsVN!xijm%4aeHye3QkK?T`gmRzb28Tt(_y&+QMfw+`e7H%_F~yFivF8 zE<PR#33sNZrczN+xqdK<t_ms$K}`ywyAc5J^6`bj;`fI4Xj@r&4JUOHz*+@k{zR4P zHv|;oI5E7yQ243?jbTNkxY-BE9%4w%Ro_+FAmh`k+GYRHKx5FZx-JEka4SZaE$nyu z|1k#$2aQJOQ;4goqOWQj)I}XG=kPcrB@x+%cvxZZyKirA*DzWQ6Nn8pB3oe`k&@Wv zB+$#$zF(*c2|>M;mykxYFQZtx6wLvykWg_0$WgbF#`A_ugD#6q+TCd8u~>?P=+lzr z|KRpp%3he3|E?hfzy||L0ZQz$aBUoT@`r2yPWkrXytuybVej*`@b~mPb*?T_s1^8G zNXVKe>XMxG99TIOQ90aI8PEI}d)$U}T(Ec!5~0&6Ora1um0G)C6py{U{;hB<uCZC~ zB_Z8}*|vEK?>@QgNG{Rw7y^pIfJcTQ26>~=-MI6yj&DLFBG3apO#(eBMiqE~5%?fw z4an;~I<mb)BnQ|Wlxfk2ALEN~0_RO=EfM%1c@Ewe!D>bTusOH5cCugziB?c&tE+Qm z*VonnF*xw~SfR(z1s{*ctehuwhzhQc?~GVMwLeR}sx4qvRzc<C$+L@199M9+99YS# z^mdvfjZ5l0O5>10<RJcPW~VrzT~13lfZje-BtB%?Z|oSS+g7m~)JxAErj1e8;;^iV zn;~0?+9>B{)+*$8Hj)KYjRRw~NgZXH_#M~Oe%CNIZh7l{ou9{&-D+8W-|Bvtkh7LU zxX^@SBjr>;0$5$x`1C~jy^xdUruywA^R=>~;?w<V1oUm+k%WAWIgP%);Lo2weCd9D z%ysTr-PlM?OS}EmdExW);JDt=Qq(}tz|mOSd79eduw7iM_oX<1a*lm`J|*St_0M-y z$N^D-NZj`Frq6D^&V{M+*cd~cRLHWn(iy#|D6sV7Np+=?q7pQYhtF(?gNwUF7dAUq zciYHEDLJa|x1X-pa~1E*PQ@;{c+IZ0{qkpXCW$t@nA7=}FhZsKcnCa7R_WV)>vs-S z0%J!(MKv{>uL0*R%LGVuTo9_B<q5X$lPO$@15lzf3sW8&I=ErbSg$w@?buByYlA~t zbcAqPiH0m&6V|aRX}Xex=`-bJIF{rvm6|jNGR<cJPRt%zhZ3g~?ta_-;lk%_ZGV3G zUxBjW2nx<vY~rwON^xfVcb>UyrU#u(KOKs_{l#GAOd!1h+z7O0kz|cwV_}(_n`f;< z4>At2+R4Ap-FJA2u%YyBr{r%BBTpCK6E9?3V;X9)mt#hrVKIb*geT>dIUkrV8!HO~ zh_0$45pNc&*Enf1B=XPvZBih~**4R80+yz$WE?(G>Rg!Y2ZCW`xouP~s<heoCLfa{ z@zq|SploD&)M}z{v|W6CFl=`(RUWq;7WcuN9C<lz7n~rE#W3olQu4A%U_SZ|a29#O zej<}?q}%lIahLo}{^BxTHDgS$M_w`tE4ff$HOuW-l<B9bs@m}`UE#;CK3C>Z3si~1 zA*>ByI<>mRoEexB*MAQS4{Q6uaxT|<W46J0{Q5re^hAY7mv%7vK1J#z-pIMztHIr_ z_#0QV-tQBn0glJzneQd7e8Pl_U0CNtS6*K(0_AuXWij(u&SLUTe|#R0+hZNBe)oP7 zVBvq!{pSehWI$jWrgTWu_lG*9W4L=DsVbiGBREJ7tCfP70JUWfU)<-i53w4pl28jm ze1$CwH!N!cCgUoiu#N@<{+%!>QutU!2<Sts1QS!_PhWek+!qn{j%~q8VM%sG$l^e{ zb`yly-8zDF05JADjp(-Co1gd+orkFg5RU-oGl8#M`Q|P_qO2|QlGDQi|Ad+#__|q9 z$E0yWD{;6ffF>&JjcJyJ9x8aTk{xK~QXw-5J2?k49-o+Kqis%IrcP{~k&%J0h^k!A z!LARNfT+rx&Ms8!N-ga}4TT{F2P_>qgV~4XxC>C5kh8O=lKSL}vHT)+X5o0K7`ZUk z2J5o`gvW8N+&%}{Hk@`1pA)`v0~z|Wvw0lwNU+2Z^^<@%;$%yusZW1UXu_1!leksU z2)F<P*+^*Q7;psd;-qQ$4u(5_cf=9~&5bxJGaMjV3(KxH&4?-)U~%BGOW^Mfq5_c9 z!~<TR|DrN^1T$vS1mQ5jsGWsjv=T%=%X~+0`qJ~r7zzsK6f?!pk0O!?yDi38A>#sk zE@j&rthbHD!2V)5B!0IiEpN?O6XMWdD7{a8{c*cGm@Hd|T@v*I`mNH5#Q{gP{4#cA z+mO66DqK!eYGt-K+m5$Lv1j1lM(hfzJLSG<yKveDwa5OpA~JR}bPhl6l-ONJ7+u5r zD9ikO<Jw;V{uc!zN!)ehd6g>pcw~6@b8-$z1p;P6gf@~lS$=u@6?$5gs@ZwTjTR&K zH`7K)`AYK2()ns)cZ>0SuOT(ObtZw+0*B(^gs8ST8iMWzb$ssw(LO8Z@}^(CODe=K zOuBq_pZV+a#Qd)9=oRrd3JRLVqt8(vqEq*|7iW<F_+a#k2^0`icAU(0?v5_VU^CLy z)_$0gBg^LfKt$;0$h(a=?I&QcEWFO{Ku<^p`4KoLgU?|hdNawzJiO_#pc1El;KZ$j zjL~lLdY#D#FSDD`II@+Z#42%zidzwP`sI58qJT-d<Mc8rof{_u`vtq!){X0wd(PzM z%Wv;Ss1{*vZBU%L9f$j%tV~y%tC<MBu99E_3wr9%wtsQNi`~vj33vJae0|z{8!mjk zN(@wXMGy^Ow<wI>2|;1)HK8LW+aAI<nbbeXemQpz%w2st$aOw&eS%(Ka}+`y2#6L? zxUn*0<rr>|Fnpea3SZ7_-Z54CI5zos{PI0acAF9Tvz4s@R*OA6g$joI^%njP{43PF zkO35!h&|3Fx^UnU@3H=^e*W|QP2`n{FiSQm`_ar}@p*mo70*|HxW6`Z!R?)5d;96~ zmr9ei=%3-q0Z<7Y{v>fg{=9`Kd>43=ySW8kVE9S;80#qf`7#S4kN@5W62J5GE8yuI z$7cNTVK?zgDEo(tgpX-*)g#aS8w~$%wsG?xR@ZMsYyrUtY~P>V03#wWC8zbB31fsy z?wJln0|Ns#k|sV%51NzD4Llu*M)&1xpQ5E<%!A@)!c4+v2A>nX^a-Drz61<00@&y2 zQL$CbFvQ_(dOyQ)TVcqD{J1=Bloh^)>cEsklqm^<OSy}#8&xtX+dK@fA#7wm9GR^f z!uz1GwliBWYy~c3F>V3RgS#yvY^llS^8uf0$Q8`@*SjyfH)fN<Kn~uRB7?zUQy+N^ zmOgC8J_^Wi8|y4tQv3aygR9vw=fL(4qn2*Nhf+ObPu$Z=;W?KC2BksVV9Z1*1B*?p zBrqQlKAz>4l0J-@{l|BYN^(7ax-aI~I4><nv~2i$A%vuHpa`yrKGY!A`kqJoHj;X= z2QD&Tp2HE1RP^__cczw6#jdlo8g_XVc9`+76rbt8kUkS8mMy>$_7cKAK#%Rj;9?5q z$aGq%-8PO%Bz=6WUr?3UoS+x#kf7-z-~t6^CIR(TM=^?KB|)p^mwZ26V09Whf@@(< zOOXarw~2B!PD<VSu|Q)<Bcw3o{47ntZ}Bnh<zL%>m_!iMbHGyYg6A+JZlLD?a(`W5 zFhonlI+2pVdlEGZ&JrW7sIM2D!-9FMs>!lfT-}|20e;OgJ35CjVk?y9QooOyz@ccw zvY&;~MpZnKnd7oZVt1GkGUbYjQ3!T`5YNC2npRNKaRHjZTo@ThT0>zz+7RA@LFMw^ zN~%dRTf)0WENN>5+R*&frwk(}2?T8gi8wILSKV+QeYTCTWe9;5+_@%l^}%$0tcp3T zb~)n+?;;O=bJW!Wi5A#p4*9-U69n6*e?dU%&(9&bvi3vQNH165!%2r{A$uIsPnCS$ z^HrOI17J3Xm7<ABpp}l5@}zjE^|+Sq{Nl>qwwXbPqp#P!6C2yihTkYIj-cRLr+)|j z)cgJ(sKM=a5W~gReOvqV=OWZ>N57Ye_51U9Hdo~U+-!MkD+cx{srOGp0)pG_)1HZ7 zDUXQbG@MM+G$HTvxcJ!8p;)%p@l~7rzoTO0<;BEKU5(Anj)w{75uCD!Kd)wy)k|^W zfSopjoa~n%LdiIH5}^Y{@!nGNJoeXot%vd5R66e6Fbs<n8!p%g23!%wqc-;XY&rc2 z8|1CznTNjB5}a&IaWvC+hzEW@wyRQcE3N=Nw;Esq2M~K<t?RfT6Nb`(eHZ<^zf51c zQXp~7&qK8qdssIRSPW-+Faucf_m09Yxl8cVXkCcOD9>P=Vp}h<DS9NrC4M351#l)s zv27?4MUg|sWvZWRqFx!jP8*A+mzV<6%mtHPiJ^d*pT>~mWk4&NN3jC^fE6q2>%+uu zo*c1oJfav3dpnC^bJS{tLbPsHAV_38v*B!Nhn293+&SIIClWMpCa3xsd#cbme!hj5 z7L;)L%0g5w85L|Fn?+rZVA~sj8_{9QdWk3x#C}VBf76Em&WR?nK~DtpO@RE0oWj{( zDMv3GwpP(1gr)WM_c(wNE@CbNU;0VmK9I24!@i#k(zK6=97+b+lnVGzEEB+z*Yulh z)jn>~)_hrkUAg|)N0x?^9Q=HwBqHHsiF(-$M!Ha+bNeW#1IY+wfzW+SuTUt%SDaXZ z#p`E5MT&QqczLFQ4KlsFjiP*~jyKWgtf5wq%cKj|q@`==6K98wahU=Xvml^PhNZ^> zcNlz!q18J^%8r@wM<-W++<AP$0agS-V_W-3=v_g>h~C*zXrF)zF;&6Q^?h)xDuRZ+ z1F~AC6M9wrfsNQcoNIe)5A3k?UtquF+`;n*2a|3F30Dz+3GIuLXO>+E(?~**Wvwxr ziXxyANY0jaM76Nch;~3=3p99iRW-xmdS>f9<K(%S#kWSaFVg6c3b@nw(ccmbt@82N z-kK_T(q30UyFdLk7OzNZ5^x(*P$NL%Ho-{n)SrdOZv9yLwb=X`AUYl%0i=-aXC%;- zAfttoMdS@4%Lev_t!43z4xj?ME}3ZUZ<P=bh>j<F;~dyW;P;ubalN=lP>X2Y<IoC* zlPrJl*K<M^r@}%R%netvxQMASB+c_|v6ZBF@v`Ix$-q)7YxYhM_-gG^`x0?7W@+OA zH$oJBwhV=EMfO+0b%Ys38tN<xh__n0$Vb*f$dLwX@7nUnpU%w}GBw49p1s;k7)sc< zfcLIL>B`mbFz8iV`_iaB%7n|L&M(x~*#G|i_I@!bi$8cZoBlqAYvy?yBe2Wi5SlD^ zOUuc$m?gxH<FdfG__jsvf;v36>~JEdbN0@(tKES42j8a8%^2MqHZCqE3D)F|v{m9F z-@1>#Z*X1qKv?7q5o}o6mjT%ziBTU@%dZKs>yba89;@CX7?&iz8mm`mgoo_+wTHHE z7Ky@kXdl728yT17E^DA?ccAD@&Cc__#yF!W7f4C;Y`Wht&dBYb9N*H2AP!ZXSo<?# zs(fi$FIf()?(trh>nexg9-`Ojzwr)aglXjz48pc%M6xtBZC9DdIt&Lu#cdsS(ruW4 zQ=?7IyV}aQWb$l=9bqfi*q}X=w@1B`?c-_1rW{ReSDhqHpS)3ViFdO7@1DbttKlv1 zinl$5DRm@D@%Qwy)T5gSZ1O$v&{@epQmvHQ_tU(FAiaW>5y$~2<v!?Jv#w58gg;Tz zRsW1dpj5G!VM%^{){$wGk$}f!c+i1Ec3~}kgmW=Pf%-Log?I*FYR2-s>%J*qn_rHL z1ZIKOUYLDN6;HYllO*nlqqbb!+U3cm6QohPSH1RkA<4b0hvtJiF6CTo;qxBdLrWO5 zixFh_BhG;K8lQ5eF1Yuo?rPCuJ4m{O5KR1Yvk!|XB)%o~&E`S`uv;^I>#V?)BHL|f z{LOOgt7E&~&kwz~=(OUXu!b<pRL8Hmj@KqwlDr?9>`YQrj*v{T3~gCg5OxuMf6HN6 zKk07Gp1Xa0k;TXO>Nr7UC!3QzO)+fqTjWmQPT<1-7nKfqyIYHI%o!uw3=Kv-);1c3 zCW8-6w`cf%`(EmyPwEN$toj=)lswUNS?2qYs3#5cLItYcwI&8t$HF}f0aszfje>Pp z{XU&3Yorej|LM&$xkg&ryOwHxp>Z)BNkWzc>*Xu1x{G+H&)rF1ZzXOf!3-<d!6jvJ zl^1={@Y3Tz%!sW?b16M*FlP*B9YZt_B-OIVVAYS3xSo9I{Q)r-^h+JgzeGBKlk-f) zd~mbB_o3pgiAFAuDaQmzCiH_Q<6HMqXL?D#G6dnlmY^Jw55%SshNS)wcg}Q#)Ke<W zDPDEPaM44S-Su)kL<qBH3EDQ2z$v*=QSv8Sa$<fZYM&$KD1X~*B6lduJ#JhC#A0T? z`R&NTa-BB_5fKsdIR^|e<fDMPjw^DvG0s1nhkbDP0R8qtGF9ZA-@sXjfRJ}UP#$<m z^^yjwqmesNlGf~|oQ5W!loaaUzNlMmlEf`$>$xP!hCJv@WIB)h{5%vBOqi~jos)u) z)-1`8A`^>u@#u00wOJ~c>`jYz!>`4aX|r;>If^PUl-40<uO<6#e>S|ET4oZMVVP74 zvvd1GK9FgT8LK^vy&{tmrxTyR1F4>5Z#Bujl1rh@mmbAP=@D)it$hRPXqqJ=ds5wp z>9d;t$t%Q0{h&b-925u<f3tEGuN=k#oJCD?oi%w&A<EGptPSwbyiaM~_8Tf`R5wL6 zD;Bm@^n$nS#OG5&M8Kn^J<yvshs`3j$|V2wUbv<q#O8e38ZQ#xB~@XVvJ6iie~!~U z$~}DNvqOLxN}fW@=gr^|;T5t2y0ZjG@>bbI51T-77fEHGn0iB(=^0TP+Wkad@C!2n zE9b>{g9(3c<nc;Nk=crkPS6(qXLj`G0jzSyjH1#rWs<a5N-my;_mL(V?NXUIztPO~ zOg`~6Bi{*IqNu|R4#6^ctD`C-&ttPk)@pfc<J~%{V-sm(YJ#srAIVR07C9BPxXZwM zB(lMXdk8p$h$@(fXb61C*tpo>J0_WaKH>`L7O2k@^jZ5eH!5c+NtWhrur8wiUNKHF z69r2Wf|NhSIxLlk?<GE>mH@5_Uu0sk4P!V*@O&^GlN)$<371&-R}|u4Qf9v(b@ccU zNd6Qk++-U@EY4m=!JQjiaK(OExo3^p|AW_@HNXu<#t+0LO)Za|E+{$`1VR)sYmGvX zEu{o)6uv6e4vYCC;$+l)O)1`2!hC<7Yinl_O4Lj9>G4d8g6562CKQ&3V;ZK&cnl=& zS%lUnh48TmKn~Xs1wP_5Rc$Syp|EjJPmoyO_Y$ar@Bca=DYe6oMVhS<-j^*8{@UKv zPC||NYKe-t5q~ems7)^P>5Ojv_?;-*D%q%*gT`&@aeEof5<1eH>XrnzT?(yIGkDnR z*3@p^GO-!q8#qQGm)f|7;EfyUnF=P|a<m>5)h$WK8d)^7!2hX|I1D^$Zc$1Y%lG}- z%|#wG^`|B9Io~z@3SB+tNiD?M%BnMNoc&yNB8QjD+1lHC`Rl%(ao`IYD1Z>e`=R5{ zL!*U2yFlwNKcOd&xJtRiGhlTjnIAt7&3+9o6Tg|fe%m__51bd@-Iir2f>R*mfAES( zR>Tmr$oKuPvr+4ZIZw}kyQ?o;%l%v$f&y+wvmJNDnp&Cz{WvQ<@k64CQ^|bp2gcsN zjJ4lM-=zY{-hbWcj=zRWD4a5^39_b0Pe$IU;`1Z5RWCvabrK2XbY~=*&-=O+hlVVs zF42KKg0vN%ciOA6DNcz|OO0hMI=$Y&F;f-eQWzY{SjwbW3uM6k$t>zZg|Uaj&-mn6 zG=FuJ!7ON(#5_u1MLn})L_q<*WQ3VCQU+3UG!)K%C=VEi8w<|PeD4=6Ix(1Nld)KZ z<q8KvBhR>-Ope2V^BxM#1(oyDKre(O4a~!EW!xU_2ijpWtj5y6ICo3D62jv9;EZuq zk*|_`bBy16MO}`aO^f$|q#Wx<uc$upzm6n@+hpLQ(&ph=_?Q{Xi>@kD7c$>ojqM~l ztBOOw7TwdylG8yDe@lkmGg<01{ByYo=>SSbOIN^Bn2vd2g#0M+(?SZs#nd-CwK^Vz z8RGhyv{aVH^!>s5Q8sC)NCj7Y0syZtP=de=Lep;}XCyv5;#$@cB003!hbx9*x<|`K zznpXxVlV6KjkP6~4l!cvTuc(j?PEwqF^YjknrWDsVGd@FN$q0{5N;8$=u)U~#jSIL zs)5?kNiTBtk&_ftMca)<meU=U{z0A%dHV#^xGM8`CuTHq7Dev{Cz%HpWq(!KlKkT= z!X_jvQm<-OaQ(Wo$Bnb37}J(LF)L1swKh<plG_{12}3$b=&6Vtl?(lq!mvj3?h3K5 z^_{?F`<HgagFMPht#L?ih?vv8jR2y&3;@3y$<sjFY<t}UF<{<E0^saJ8-;_{ih}mj zLw~kHbH$dHu?7Q(z6%gByq@7xZ=wi>on0^plix8pULmkHT@fwXXSY>99;E$zVQl6) z8)Hky?lNzcm+a+k8^6c{3o1VP9##1pYS4qWd+Bf@U3XV)3d_x;E(oVwdVvoWW>OIP ziZIw})PA}C7+UEAJAnpT(YRDtu7f8hLFNy+m_gQYxgt1*q9X7IvgsOH9^gwG{nEng zy!RODsG+Epx3q%z*l?vZ6abu?@Ws<|!G1K`j|)Tk7mc0N48HS;Nt8+8(S_Y36%T9# zOUh(0m30*TSTd6rmO%)oIFRiuA2z^)b&YIE`i*EM$k;ZO4~El8>KKW#MOLzph0uNL zO`C}&iB-*EJB`694K#|Oc68mWDJV3Rt|I$YG1UUSWVs1L@+obN%dY(q<V2=;UU(Ic z2}?P(H9&tdp3+JhOi)4^KvZSt=0CXFr0USG`Ma83rV1uq^~V`~`@(H9Y$v6(BDu$V z1Qd}}`%ER;cVx`E(&wsdj?0%HzODEu`8a%sb@thcK*|gh-hvigr;i<~CgPsO`GzxS zk6}83H3OSU<-o10=hzQ~xdoTkU_4o1Vs3l1E163Ujadb|&J`9;x%gVtm2E4=QXfO4 z7{v%Nvz}J}mR53rXW6T0j*)aIs(-SdaLj#8G0iglNC)CbLiEF5cVwO@%DL6o)%Ni8 z!+b$2^hx5y$u^A2OBk0$9{`@hM%YC-W>I5vp`hr|hD%YfXJZykG<)4L$~MYK7Fton z$9LQ=tuw<DP#%3|9y*#k>zQjo3aKIHD)F$66LFJ8u(q}1E)qQ2Wez_ca^(Cfx`lMZ zWEtHE!<QDvfBe*DKo8EPmXO2=MTe<HrghUr@bD78G*cN%o3sb;xii9sIG|0P#a6{6 zhvAYrh+2DFJGk|O7RYaJ*gjGQ7U5VD7g8(Ijv+bmMrYqqtI7v#LF#0-*dK~<q(|Kf z{}u!#X@*c_TY6;@uvNeSf~y|b7dp}N*vY;=5@w<4vr_UnQp>}?3EPx<5yo3v6_V}e zOrTxsDF@dLk4T5XnV45A!WIRTm$O`HRpKCn_0i|Cs?cSv*#|w~+WNwaq82&trq850 z;NYb0x00#3c)%Is=Gu`@+rb`4s!b3^z##GZ;3WQhnUqzds73kctwflnMe2^_9D+re zK?q}3r=>bKB!?2i&a+wP#}2olHC`qnJ`R|`5M9)F=u8UgU{_X~S4?x$*l0%9wDFuc zA-A63n<8`OCJ0FqWYVBzFfYmi45eTin_nvcuM~g`6^r%sJSrjTSj?88Ul#}{AkEx* z^|A--1S*X23+N-ZMB<W;Wgc?3MZ!S<V=Q)wP)Q9Q<q>K)^jmd1>&mV=ydxZ+()+8i zJk@SkobGfTI9JV&?3MMdusEjy(c4N?u=yeC+T`i|MvUln4;h?NlOXq%prX(wQ&X2B z48<=|sj?x$F`^Uh!bybh(P~_)zb_C}@mwoQ&Swoo@bnRyRXOEwtL*R4-FiH;TWSdE z9hRdc>BPuRl-b5%G@D%(zs%lWPPR$dB+D||Y4@oZchbkF1txwxMU!p$VYg(*jYh%2 zs~J$u)K~pUuthpMs&ENEd>?5e`w?E>rD(p|>6`Y}3@Sc1mLJV-gSKD??>X=zXf!e! z;NsHIq{ddXpkWZ#_)x4dhZ>Y-Psu!nDbO-JtLR_Ebfp`EeJ>Bq519-1m9Z{66s89G z7P)e=YLKey+JnXF8&cHlla^@XvjxlN%QF;4Y;uYuvqPiu4hETIc@04Zl#<uV3+2l< zVYJe@$tC5XTtw=YL=Xx2Okpibij+0RDJ)b5p6Z2uv!*?;D1()C+NNN4qndX(!WJ2X zS}Js9xtCe-)^RI!qFus9y|n9Ltf)cnmVNZCx3CmRh`j*m*!nji8YC|%D@qaxjus0B z_1k-zu$~}XEw%_a%u}>9%=k~k`b?(wGc6%DbC-LmYUX%qDZHr3<Uf?_g4y*))`-Rs zVy7X(iCgJ<NZaf;p^S*@fdpw}6>oDsuxdW|w?UySrO`HNW(Izgv8K^%V_^m+^8{PZ zp;e<p)w4EcJYYS?N-gm6@W_y8<ln*CFqbUf%No9-VOu(548z|`Vb9!dU$FYk%tJ== zUpR3_B8~&oe$8$K<+?h3OuJH!+Zup`Wv-kPcU;Cbm1Z`gnTMA7T%}5^wv|Lmydu7m zsH_BEfm+4Iq1Op8=z#*}?6EOW_&1>iY{3U>JQ3(`A24!PN#2{&J;V@7wE$mysUa&e zm;A(C<|h`rz+*!HZHrXAYKqyLh_a#2&s1opkuU@4i_G(YL5eD7#p~YUT?t|MK-rwZ z<Zp9LeZ>no?9RuotlKAH42#l?ppD@7(StKI1h@9brlA(8NqR~g8Ofb25R!?>=;mPn z5CS2AO_ITa33inZB5g!sgGiHO<dfqx>choREmEtf&YDniHDFd1DFr$ny)*LxOb3<Z zWa_^4P(Y+(VsW%J??{N^=f0Oul@24kNf$fZHpX)z?3GwXRO2!7Pu>T0p``QjQyV6f zDjOq<ZqtM<@*8?-xAL>NjUnu!U`cv-)95JNU$AB?%!o>3riGQ=fC}PQ$t{Qz!qmZ% zfprFI2L@nCwGxIMEYJfPOlmOrk?f==oyvqFtS5YwdSWV!E-bZ9bc3~?4J_Hp#RB7l z9=r|ipE+;wVKv4~{r~111P2EL0}KxO?<D}jgn%C<Xrh^s|Idd1_c>>1yrh^<9oO|g zwu*_1gC)hPsFP*>r@w!7zl8<|2f0P4p;Y~kt%x9y<Pl;z^;df6P5{Bl#`UhOJdnBG zON;;O@nL*mD_aHm=2^_*2nw)sWBSE#H@!n%54wE_IAGohaqKkI2bsgUNI-wlSUUw< z%BLkhtN)YyAQAB7SMY&%{isPE7KD1)Ys&eU7e(+>rdJD%LYy1mUHk{_ZNLs7DS1Tf zN#h3juMzw+FBi~$w}7;C(o?^nMFOIAxB=g*8QH8nlv&gMX#SFE){_>O2Ymr0Uv5bU zJ)Q2m%^6BtK=&~@3|TTb2DA5#F+e=wv+h%n36;mVB_{_;9JuoeowmtOTykX-V7Y6I zll&?VP8yrVY>1=Y3u1RW1W^$60;Hcu1o99lsKZ{h3Mni9wL1SZo4uv~%$5efjOBl9 z4FrS3W&=+Y_q6{@uKwq>|5wa>aDWmZ5#-7!<^R<Bfbq``P&1Cs`;Tj!ND18ma@#n` zNdIYnyg`9I$+v2zWUYTK+W%~@EscM60DT@M=YMP!14Y#izf{(LWUCYr5s@wBU|?Y2 z=Wl}+yFyjX=A?n99sh9?Y<IJ4)L&a)KYt<rjji@ysMH+@PWqn++#{57Gi)#omZKyI zm7lbBx3DN3vs8p8n8wBu8$3~C<@i}zQu`!G6v$waN%xvJ3BA?vhOVyFa(!dC6KpEi zb7)v4;YLVZs^QS@2|P_9rSTj>%V(Iw)STar*;=`r9jt12h?;$n4;QAVmu5vtMvSj} z@qt#+ufe3)YA_(t?H;giI1wbl>z-P%;B)w}#w+Ax+__PQqL4$}db?S;>EB^wAs-#> z!cTG!kn_-&SI(k@C}NTqM<HP#WeBW`>qqJH#1|n93q-Bja~TmLI@)&|RC9S%p?yMV z8aAags?+oH7`}ZNMbteI>pV=GGmqB|<0k<!TkcQ}P)hDMM~2DENZYzJ2-8c`?)vAU zP^FLqVZ53KoKPEbNe);e8<))ND-X^ih5?a7t8mQYMVH6db<VHH1LobUIXJih0dOEI zJo%$ScmlLC260SbGdaK?<Y9ApR$}{hXKy(D&)+b$GeA3uAZSSL`)a%Up<3YM0u)Jn zkrR8wG(I}6EQ}y|v@oF6EZ7z^dFaQtd?{>bX!!oT&jwB1CS#!JvK>hvCFXOw#g~+i z2z-0mbYA}!zhh%%^)mDJJhND>B?lPl3q=%rJfb2J$a8_~lSyinE0S|Knv;BMW>L)7 zbU68Ok}jwfR2m5MF>{jK=OiPT{8mAxqF1=xp9%Deq@u(z2dChW77%<MRgO}OLRE@E z&+VNEkpMj$t92S!EO_?6IIej)n3NjmI4m{8txO#n^&T=2Mou~M)iBT+!G2&gwkZs7 zNzR|51?#fYFvcMyVUiQ#S4W{?M5EtK%C2wi4LYk)0Fc6dae=t+>^l|_I=(47FA_}Q zUmW-1TJ-1ZRR74$@o)KBG_xMTMTLIY5RBN?mv+>UmUY?OCQs6gn7u-NE3M+ogx)#l zWE=7dpS5;2jMrH#(EIZ@du`skx2752$PEI5lwPj*zMpfI&U(kw{`_xj#BdVxAh0M8 zgpoxw#JGQ@VYaFv=|+^c|GN`d(oaK5Y9QCS%ZZ&JFPAY?^acBzg>SRN7K)n$pVAaR zTv~2PEw9fBMditN;ru;n;%l@>n%L*fJuiHFKJ4&3m6VjM(`8!Hc>lh~VVN~vw#j;m zsDO`;uPMcvC17t9^T!U8Zk^B5d8@mdn^XIK#^vQDl;ivdl{x8zB0cWz?sUBGEOZ6! zgZ+HZ8b^NA8^`*uHQT96SNSU`O<eC!KuubEx9gMIFHl4=U-VN)Q4t`Ta!{dKduO}8 z^LLJbJFQrC1r$ACdEfOgEp$;<qE;Z>4h0XPbRa>+;_+}Df2qRJ&vy4_HRPen>S_i- z!H%Au0)%M#&bGF!lwa;MB2j8r_rE(Y`im|)FGSZ)pd^6m?xKB*yfoj3HR?VYqnFLM zm(4Gq&K-`Ak4wi=K&K*bx7JE3ksbB5U&{4jNdUe@eU{M7Nk;E)20d&X?gE*Q(+W@z z@2&fGB%N&2lIgc*#+D?hX91eXWYxa8#jvAnJ68~x6%`T?PY1*6URZrz*8Y^IW9-c3 zu^u-L=;WbL$0MNL6{5J*bSUVhu#5L;=cV8w{zlNj{kSl-fy2fsImaf3I;N74&&T7c z=<(o_VNfOl0M=A@sjRM)A1@yH^T?L}vTRzn3ZAC``zRpO#bXTisR#LAQp}1`Q>ccq z{_w0OgiFG0V#2}4%pt)R6=Cp_Rg7C9B2b6jJ@e3iJj<c<-z1DIt~ViAS=|gQ|4ohq z_JCS(n+xT{K}}ZtC_}F>oidd<9S=hw0C5}&#*~U{0-!n4mGv84%6A;@%*58J1{dpB zC?%(7!i<nMVb#)dSE;CEb8y&XpGH8mW#5vMlVh0nLsK*590V45nLE8JD^aHxCT)zm z7ze3OWM^k5MM8@?P!NQHu_~II)8b)Q;4%JYXJtLiE3wf~Q#Y*1sy9K=QL=sl3k!=( zW`pYi@>h0R+6bS3*PFt(zcYb&JDE(OqQc~$8wmSs|Jj-2V5q03Ck+kF;v8GRRqXHI zPPQ0%i0FmM$&D`C{R5F0T0HKD)1N<oK0i4Lxfd_tb^g_5J6G}#-bFMyKJK>@O;<(w zQ=3o3$)}wl(B$gss=52+>Cb|?bI)IELsDYAz^C<1b@Fh;d33vs<#+CA<x)3<E%PQX zCwfpsn%p$52vJAW|DKuUFO&$A5z5hp9z{$%*H>&=qkRB44_cHP4>RM#)dRAhv`lE5 zb2G58lA-zY-!%ekFP`x|-V4b(a5r(+9cyQDdqn1E&<usKjjOWCCnjnZX;Lqa$s*lH zg$SMTN+ni5#ib(O>+(+;b}lg-d)A*Dyutg*+R2n4r}R^ND4H>~cZ1XZ#_*@mLDuhG zQnET!PZ1X<!A(;!Mu4~_;VA43OUrAFeZSVjVv>(<TgS%l)9OUDsZGD{trDB`Lj^P- zn^Rgx6Kj8CQd{&YVou7(>89kv?bbpF)}W)IBmlyodq|vrqT$`=h^(JCTYz6d1s;8B zLZrSS&>`lBp-{5K6R$4o`s}^AO8X4~nG-7;K8czOEv^XA10EESwB;n!VO+}QvT@XX zD*A~y7P7X!cK3)v{h8i~eC7LF`(Z)%V~)e?jsGUsdj48jUti%=EnaQDDD82PVn+q6 zN#pYl-75<ou_HF2=<EJVXk#-``FAamb!ZxIN}9)uC-L#y(SuTZKV2W<zWVbmKMaXx z=<}xQ2<eyZd!h-aXu2XbkUPJ}k-C8aVeG}->GBOpzGj&YhS)Q^`2Z9ge1ocr`uh3< zbv!Hr5E6;S#jkF8J1ARza;2;~4cZn+5|8eE=e^+xLXaT#rC=tlks6W+IGD=)Czy$T z(q=PL(A><;tz@PMMG|!#98RHxBuFAh^pjv5=z{`v<m{-}%dr}?65QZ{Ts{g}lp@zT zwv{z?p4ijsjOh?a7!$Qbr_=Aw-jIO=qX$ts3eK*T+i;d@>t;sm?X&YI6pSeS)h=<_ z$3-nQ;KZJzhN!;%nwdpnMX`~V_NKw+zV=^tRkV4s?>}Sx@usaL-l$~}JZK$0RCaV2 zhohSX*gyZA9JGtpz*bHiMXddmP7}Ei!alqB-q!YY(Mer$J^xFVo3J#DF%p`aypj~z z*Xf`b+qjw&)urkAj_2lweF27ria}`x;Q>j+a;5-uNx<*r*$wadmOskw7@d-fvD_dZ zPlpI<{S?K$hPRJjtDB2!%X+v(Slv{ucP1Cxs-rdfI0LQVL&5iVg<eg@MrL}2&39xT z1V)c7fKg5%pX=AZy+*6vPW+0Uu;`T~404)G>pDU)R`A(KN|khKN^u9X)vfdQhF#F3 zO)zKG-1odCu;IzCF%0l()w=Ekk3|sn-?acbj`5({$LY$sWeo)dW%;uSG<9KNX(KlY z?96NA^?Suw-^cu9u}<iBSJRg-&ZilFc!<u2PwR8V0$*QW5Yfp6`DqvkSz6lK9zH$Y zr_MqvpMssZdAS|i9&@B3(fh?2nV1M7E_$8{PS$b~y#jGaam(j!H+&XY$Ui-p+1ZUv zX8&E@bb;lR5;Oef_c@@Om;<yx;M8>`yLhh77G`?6H%3=D*o(>&rH@b{hi;2?M0lM{ zk^&%<q%;tLAV_mCx5;_6O?1-SuCD~+KYlv>+&Yi;*JbTyK}~Fk097#clDrZyv@LFr z!6Rvsrl~2^_Lr8znFizeBNW)2t1ZLV^8Q%G`4$ouQnZbfzR~Ku>D_ZfiZ1>>@a4|M zw5$||1Wb>!si|cNGK8Yy+DYodm1QX>K+(RWNG#pg@ZPXV7UF=z9DfX*mLJ}C9N0qm zHlYbGaXO90{R5)i$1YZ<b0@rIa82_&y3_J<l^%6&9E1yvxsRw*i193Plilen(MW0G z?+pnjpH}2Q1bH}m$tg(b8E9eHOkOYS=$5LHK-Pj<8O1G{Not*hHddxECD4JChP~IR ztyrBOyRhEUdW>`gzg3~<IUJ-Nm(-P&3oyJgF?W27{<I%IFz;~s%UO@K<+KLYF(;@O ztac33W+Zt?R4W!Y<RoUW-uD||1lu&ftG8dSV57AW@6z?$DFGUzL}Ad?dqH{ZBSgTM z#``*H?n%>~Iu-&ws)RZP-_Bp1zhCy>wD9m~VeLY59-96xWiHnn;1l8&Yu~^AZvCzk z#;^qo>FXOBl3^2m`x%p73oYZkzB+exe(@}XT*dApt<1B;Gm=Pd)NMUAS1JeAt$Ww# zHuEbHTDa}&*RNYg9F2`oOuJGo-|u3>+o}Cdmm0u)QAMGrig2j&d=G|6LYzi6Z^wi{ z`mv|W8f_z<OXDK1Az$>xweYL%`cV-Dd`5jG_hN8yapiF|NK{l+@6~%~{kg@_PNfcS zb`!;hftWkgbXbxJGa3ww@W!yuzLbz5Y(X{**ULP`)dsTxC_WT&!e;zh^Ev~)8U0Bu zd=9fuAt?PWj+!nbDZgMY9)!9eP2-}Tysu7EZb1hFD-Z>?i<}jk#?$;L4&WOHNmEFH zBZ!*wF{=WgxAaK)C#5U8_oA4f227N@9d-;SABqgIPsLf>4W_;nsV44;6)k#3+M6xR zbXKlj^J!I=Gk7RHNfSuPh`Eh=CavqU4Kvp2f{wCb!NG1O+s1jU7VwLU3(F;oI6pj@ zClw8|GZl%mz`ak35N$yVSE=RXntRsxX?youBfx#M1mG_<DmdM9xa#q@XtTnXah3f1 z<X%@TlRhWOy#ce|EuKy-o)_-={18VsWZ2M;=5RrHic|p2(~9>jXaKwI>z~?Z7=Pg} zp}OQpY%v7#_V{HJT0kxToKgTNM|W~U<aO~1Qlv=FBZcN05S8{-<H?H&`2RjT3kgZ| zzUaA%$nAkbp<Ns8rvnAQ3ZO<N-|?~CQMl;S<U*76%q!Fqay?l*t_*m#^!Cnlzxh?2 zJog1KAILjk-&oRkAMSr)Rjg~E(P%woXbU-Q=c(1h%}g__`|+#b*WL0vG%G8#Lwww= z<?MIg90_Y8aDegZJ`^RmUT{56&}^p7o_v`bDA*2II$XqaV?-TRXK3B<TCX!`GCkwx zZye?%C5raI+zIY4P-5O)&U%nMFcYQUSHxl$gh{`Ka-LF`;;$1Xubd6LMdRu}Xj+pf zr;#;H>TEF)G<iS>8mHly#U$uLCA(S<pa1~_Igt{vV>w+NTWgR&ad_H}|K<~!%GJZw zE&xWFTYfRux#O62n6Ql%xm-WL*yF(xTaie7t6yD0y&nN9r<q&3#gGNG-Nx;fVQ^^Z z_x}*}j=_<|+xPdx$;7rZvF%Je@x-=mPn=9_dt#@fj&0kvZRhEGfA?R{n|^nyx=wwu z_gQOwc3xoCsuL}waQC09ZX$SaDfMWMv+CJbh0#n+RK_Ve)W7EZvu5X%z`U25YAZT- zac-cfB_wOEwpHI(5ob4QL5DeBJdO^ZIjtF0aD&6W?YG1{aaY1nT5T5GgLx!ZB=7_q zvwKHxdT7?a(mX@|wu8^Le+-G)q-FC^1&zV~sjl*>95=>9Rhh2lH@aBenO=H43pJ)2 zKl<$&{cRkaIHz1lZ_f8(-O`y2?T^0JF*IqCRhBCnW#%;00To@zC*#R^9Ktvw?eZ7f zv;ix24nJ-=ECVg=zR%7G`jTGTj9L9Cx5LU-h@t!Y8ldhVDkAL%2SbUK-4{$QbjO_q zH<NC*o)>=kZ;~?-Sg@Dv|Ktu7-mgQ6ZMHtga=*^Qi3NECyz!{uiah$8Qc?Ulf3Y|> zAXrhgXqw#NrjhB0yuN_q1R72?pD6@5wfp2P15G6;+gbQVwZgR@puh&<bN@krJdAge zo*`xmvK=-0Av?_L5E*=KfWmt(V#`1v60y1+F8g+Cn%2N&=^5!8>K`4U|2Sx6F8@<C zCj@!;UI!63^TQ6|kpEt&UUe<cDeyvg6xH`(AH;3baT<@RREaiQyPQKDc*u^dPXwg1 zap>q6y7RPG-)GeazP839JiO7D<Ei2A6%l$B+2~z&b|FGKP4(~%Rctf=g6A5aL}^Oa z@t>*jb5OZ55FgtqU*s%8>hzH^&g+Y@q35qNDFVCpE7AIT-Ep#`)6C3`sj;A+%?Y#Q zKBx44(z8C|3`L?{_7j^vilmADM%!^xRe%X&OpdLfpp}|?LQcz&3OOYpGCT-V@|olL zda}OGA9ImS&+oYx%>~eJl8C?)7seKV2&<l~?(Ie2dS7h9X42A7UwQ_8AP2?jNay<e zNpETda<j81SkF$n-l3%ZXSzWB=GTdW-}?KgSrjjcjI5m0#AHsV=iL~f>+uz2T}H#l zmf1IBTY}|NZJD;+<0cD4TI>u(BFYdHNX5WEJ&R+>;rA2)%<;74KmYfOTL3)j&vv+p zz1>83;z)hLh;OU`ALf}cOD+yK5Eb2bZ6SggYr-o=SRx@&Z^%N7*8Sk843WjgE+*|U zd`P32#2@Ug$(Ey>+DSm9&;?5ll~u*ZMDSm?`EEd@a25#~d)>4NaVXyecl{AFxsubM z{MS`ROtf!Ze92Ur#61KDnUZv4Ybr|^CQiN6>TwUTf4I-fMP%;Akd6dy4{vy>xs6J* z$bTA;G+_a;&uFz(n;0h#JM&S84j26EsB+(BEu05T{%Ne6sj2F6klRD|3dsI8fa*74 zPf0Ck*VNS2RrNYe(xJ@d@)HDvMUw}51vzfCK3JHCNQ!019WJM(sa7n3hH=!*l_;uc zw^%)ZPOb2omLeWHI_s6&gRC7I8pc8Sck<XjKNyj&YH5?Ts+4qrM&i=aR(6*N+47+Z zDK0*EwmLTS?JSPcM&hR8?(W!)CP;+*VNl}z%3}bn9ew9P%$}zlnDPY+F?{%e-mz7& zzzDX|kdW6bi%svEurw-KaDt%zv<nmExA|j)+wC#8Yq~`a@^9w7n8wzei0FhRcx_wU zO1_OnFduXtNvUox+v(H~hNURpc?oq}m|%`$K@B9`Xd5()l5AScoaj7<jVD!VnKLHw z#b!;cSGQ3X0<x$gYz_jr5HP&?tr^)Fv)Hz)@<)W=8gBQ{_%6%)@nt*8OT=YXqEm@3 z{CezVi2shm7I4`!YIxZOzfVm<8@Ms9N$9ZS{p3b8ut98cNT%l0LaB9{2ujrS%(MCy zoJaYClYm7qQ0KLbONsPu-aAvtyR{4+VTSZ+@Hd;-fm8nIemHdZtWTH<|L3SBPw|DW z^&C8sb5s2ppX4{DKLm^au5C2mGZg5wnwy95*$@%^6=DZ}&mP-+v#d4V{6?>)EaZp} z-uW|Q7zF^+na`YN%0$D+iW8NJlSl~m>*i=OJu@k5{4~J7G@r%YVL#O|SdW1I=%>NA z*5Y;XfS?TVB6Q*eNA4Vls5o!(LiH>~F|gzmuHSsHUrHJyR8kHLkX%YAJbBN#rG?x4 z#A(huMBrdQ)yiMfXvxTWiz3>V**IXw9cOkw4<c((6(GSoW4?W~0t?hpId9L>aH4Uu zjEAN<q0hP)sH`U&9t}{I*2p+akh)s!AKIwOL3`%9Q6hK6EH)OQ!`5&z;l$WVbj*UC z?(IHnyLp|&Z$M%^eg8QvEDkiwFoKSfWjLtpuHkTei=iX=V+g9F8NqdVJdY+4O-<<G zjZj=8=-SE~!q0ACR4S&!(HH9p9f7v)$rmq*GI@}s%rTlC^FJyo1$;|96tQq=JwwM; zwwQuW=nBpibI%lONc`-}92)T&^V8CrLgT$v$Lco}r(PlR!v4)hYAWl|WvK|1sZ!ve zBBy`<@~@P2^3u_Gs2x3FnLYg$^%{NJ;}_eEDQ8ozQ8Z4-Bf*<7hM}S{89~1TOKe2{ zuJ)wnOss$OD0?TzO!&F-_UCiDd0@>f&1`XwDmHz8A5!I}TV{4CoDyINj<N~BT@tuX zc#xWl*cocAIhY#*03JxlEczY@4h9rTKaEH@*Oh{+(_kd%*8is-Y%{?+^xO-@ssbOF zAUj&Q@x6b>)s(sxbH%9q<hRSpB_ui>#NdByAp~GtU{zJWLg+rYwP{8MAOU_s4VBr@ zE7k-lt>t;Wbf;IqvvOgW#EhB3?Ww%(P+B*x3ewoG=q`rkKjTCyg=P-KzdC=9FbN=t z_&ob$rS4H}k$1KukG9$UTW?jBtzC6-1+4)TwBR&jZ+?>b4nen?B<o;NTsbjyoy{}Z zv2twmmy=b8;?`}XMzp=P(2ocSfpyx4=WDm+|LIpG;1ZG174OXb`5bZ$A^+fQ=Z%`7 zF(3-*VL2J5WMWX=(VA-<d2L?pTyN@&W5`Mrk&eQ9;GTtbrwwX-ek%dyGncVZnwF6( zjT;~uu@_P}-D**pNmp3kky9dpc}=nEO3M}I<`UEr)l)(!t>HV3_@}mrQ!B61uc{D~ zB&<phKe=436&Wbat8-Hi$R7tO4=wmuyh5l${`&{h_+e`}z1}=9sh~~e>o3qfm$K@F z;a4bp5BFxzj^wmi`aC^jpZ&izjrT#<5~EXR`os4GsL~Ur@MvrXw#2uk9GWTM@*UI$ ztXy+&nbQkVSNB0rL82q*MX@E2#Yp(MwB{9yx~}bJ)y@;ObF+2c^;a|2&VTh7+C1^? zv0NJcg4m8)+hmiQ5Lo3)**6GX+`!?#+=}#iA)2ZPbAW`c85%W*1QSR3uRg<U2HQXl z1Q5~+&8_e8s|)f-(5JNHkQ|(oJyP+gvYP#Vvl2mKO!-ap0TkC{+gbTn@twKg_truv zl*q}b>C&Cl@jnQIIVgw^LaUEp^B|=U)=9<L4Ecu&m?@{!4~oir_{j4bS39nycR=4f z%YU8+W8SJI?-th7#{q*RZLblQuhhqh5)Ms?<b|Zl(!ivXCP#6A0Nc9yTp%n@ManWX z?7y{eitCvwkKHxe!KDGqigQ-BQ#lR3E3bI@sr#D0v3wTfKH-gQLwUTwHm7z_L~O&; z1~^H2Lc6_5TS4y0IDVt5c+lv*?`S*hh#(5m11$EtX(;-XsH5Lr3==LsKG8=rL1`>l ze1CIO6UgKndJP;0wWx2I^2BxmM=PZp(Y8uEmyFz>7sTWMkY)w?h&UL{waM<|^+@t2 zV3)#x0wGM9sCaJF7M^*{h=St>`KqAyR{bsFV+Qm(*UXs}SXrMn`~0ifs(az$vFx-2 z`>v`r?|!{4rgT-&_`&lzZL-o`#p`(SqDAdjjVg4xzl`5*9-2$LTa5n|O#FOYui(#) znHoBRT~^f!g~OF7AIT?jFwADYFT0nx1cV|3CNo{W?;}+#F1D|%0{bleW@WHtbYPof z-;E%<E9VI#K@6t_0o~R<t0tc!gT0Bl)EIRpkaj4E_cyeDr(IU0?9b<md3{|S8{3>v z(-ZHk3r-me4F&AZuN(9oNbtSCKZQsMn&w00pFQN(-a<Hd6J4F^gy9HEA}YzRJ>=R3 z4PIw6R#1(-xAvwn_dlADCR(_Fm{-utw(|IBiHCh0T5NN>TnrOu%=e%AH=IR^_&6xf zx9I|Tx7*QkSWZ!H2_%NKE1SGpp83T=dfRWU2%8lc-2MY*eotb_Gd3-)2B#j1;8&Q} zbu4RgDX&6n-j6T_!dA2F-qZhDkL2artw*B%cE$*$+L#N4MRMi}b&Ofmud^8+G~YuB z!!oT&e>_2%JKTG1uqk|bO6YA0JguL>ksWP|tkp0ZB2M|?hYY{$i+>X-oyGB~c?WeM z_QZfgw3={R5i{=LdOYZ0Hii!Rm@9prxwR!yBq|x>Js7zesbT099l039F7K5n!HP!( z@wKygLZ_n(JTR=#i?4_#cih*@+!i%2R!$(ZUKumHEkh-bn&=Hxa`|)7&0om6qfcQe z!Y%wG`L>1<xW{?JrpEWa&HAY`F2q+HyLIvObd+$8GP}gr*nzc7;6~2}`L_)I%&E&f zwsjukf#s|3aw)ph#MTI#6G(Cq8&K&wF&Welm(Azw9!<eyZlImrJ%##HIcW@fCLL8q zSY6I3qqMZM4{mIqTOq92V(Vn$BO^c2f~HrCSy1|ByIfP*NOT5+;k<OPCU_oN1Ge|O z9$(Yfg7ceBl~FG*0T`dm8FEq+Ea!3guCEpWEIkaJwk|SNdC)=`@CDQQ?JY45k#Znd zpxMgJB=~%4U~TG=eRPs;boN^f<K5_r0H2jYluw63zM{u@59oR!bc2~~2l##9%gM<B zI$KxFfU;;6K6_Bj|JC>%;K7!+g<g@z|4nYIb;DipXi(Bso_QR3JOryxFJ*a)Du0@F z6(3VrL9DZPZ+JB^jE-cWeLU5_Ap>)gDIg&5IkT=i%E`U)WncueJT3`~T!~XTT8FRu z>9&WZ#mxU**$aRUGTZUx4E@H&uD*$aFlLFr$wGfc_(;TvT#j~eEgeyrQZING^>~i! z<Wx#*k>2RcpNK331qPuoKqKzJhzs_Aqen+Xc%9b*GA4>sQq&N}qZE6|aR2W>r2tP$ z_B#@D1uSx>Paz@&phOm+121y3KnhXT^z@%!>Mf>q+4zOC=^6HVX&nZuNyzvSjf(_` z-^G+YuC3Av#L`kdPR55|kwY!0d_N*|k;fzIxQTQ;x)_r~S*K$BN37PgG_+TW?|&x= zVn<hb#{!)*=OSdgDGEpfz})Fava?aYd?(_wA3`kt_I-m<MIOzEu6Q{?D|zkyr>^(G zGh)?Sq4BD*FUwzBTN^9*s-fl8x?uHGyB>c2pN=LGHNdXQ@vg^tq5}_0AhGtnkD3i3 zz;yO;w^d<G`|r-iS9_ZGXp3R5A)5kiWZ*oydel0z-|7NXx_73$fO_kkO}$<<G6~li zr`4xQJ5XZd(hu`b5drp+d#6wP_epXLNJbdUOjGIe7^1S$3nl%@*e}7R2VX18Ka|~O z87gWfrA(O{zH9j>6}Pi)3z(l(He40=W1xmUHZ9%raXzM&0Ca8KZwa~{WX-K!2Mgeo zXx06%6>}8twraW(T$r9NA}XX{t4_Zi=qDXB7*NbFj|q3v1**>FGx{zXs>n40NhF{E z8S!3o?<bg}hu8{!!I0miwo#%Lh;Zif)IVx1WSa8qVeNgN%3IXifTe2obrB4Cd1pu~ zj!lC#2PTLbh!=BRo2w75i-TRz8m@^}kOpv*ur+3&%%q7-+iBca0hB|nTX%Q4iF#=* z;O%8Ln|BOnB^cj|A1{q<=TRAwyK!ttfsLt-0pvoFrGSi40}@tP!l%F^KGH4Gj+q9n zyl#eDWzRU7Zu#!^+0BsSH<X@cQ65sw!Q9!+CFa{}G7}aArp5nGZ|Gwo!y}*o{BE=n zY+LHhCP3c}0v28j0Iv!17zMLl`$tu5OJ4WbqR}P1*A3Y;@bn$hRw-95?v5k)Zk6|z zk*#Ljgmhnpb2YD6DcHnhU{gk9TF*Wm?Yagyu`)p}5hO+I-Q@ojULMc^w`vD41Fq-c zdh$|(Y$LM&G&I<`o-CL}g%qP=cwLgOGI*sSOi9s8Q>t|^)b4IjBbWd*%#f#k&bu8} zs%O`TqoI%^PwXj|m2~Y+tb$iTRVN+hnUlc#Ugtl8^_>aAHM|bhCmIjI(*$nIHn#RW zuMrS|luNMPzQ3hyo`WjP%PPyavOJn<0oL4a!tBC_(h~l*z#(oYerN_Imob)aniT(M z?4jfU#|!d=$DYn)^0u&2DsX=3E#PozE`K-<)<DYORX8bkhs^0R+PMwM`EB3)MnlPe zKV}VYL@uh&_|{xlp?2BmGJm7>5`d?g*|q1?SY)oCSul;^+C<b+Vj?M}E84VWjkK05 z394n~I}`h2f`1LDFHVFbBz^&Ho?14G4eNx}`2rL=HE%4dM$i899$T5QV^GPuEkdjk zF`DxW0M5(m1_dKt<>E#7|A@EBIZF{QPbA@dmQmF*VJT5?=pecI0v168C<z_zL&p6u zWz;gGe_fkXsX=Z9($0q*q0xQJiN8dmY?#G}nk`qNKvEDOo3Ezpgk9_3Wg}P&UcS+* z$cm&y#mYua=U$h#wMSRYsu2(#5zL(q>U-5<<|b36BSXV_bPx9X8X`kVF*d|`B_`Dp z>_6BPpPlk_9sXyfl^jt9^B~inOEsA9P`ZCRQ>QLjG~_P-x{h_N87FGj!cU<S1zebG z#V)#qj+)}u?!;CO`<4`XuFOAMn-?LJY*VXt{b~5bKXY;xA^-99M#txzke{LuNi6U1 z&b-m7x*Yrv0+S+tH`uw?IXZcu3AaP~=?(&9aCSbNXeU>x-qjs#0B<MdRhL4=+nkh~ z7AG~~?LsFBH?>=-X!&3B5RnHY2c#@As}6O=iE}D>aq)@jEhjqC!*Q`PP1kB|#W@B% zPdqc#w$QQA-2R;>`jk9^z(xW!m!E}c6yQ!G57#Yy-qKI3gIl$`F)f<TW0_-3L~DaY z%MXzNrZg=Q@k{b8-4bc*MtkJ&h|!jZ#^dp*zXIMs+bXupb~<+6RzO+VZ50;-bOAku z$zy6KMtTPA!Akj(dgqs&zGz>AHD@uS_F&&?Hn9r^Ce=i4uJafJQs&2a1;8pJ(<7(F zS9I`nL5bSd+WMifs`at4@CDw<zKyW#*V@e<qPLr*smbBi8<eI+>Ar2kp8^WEn)V+a z2_PVgYRf%+&`4`l&Et*rp<_UDNj~A42Z0Wc#bp(&OD<Bht)M^_Uj1xUq|>1ZWvAlJ zNM>^GDCqQ%yzPNS|4ZGtK-5KLzc3(iDv7qY{MT^HO<l^TO`0i5ToSrn7E5KWnpbbG z(@t%0*a+w7H&Q0hw7zedw=7BA^3h3iT6fIJym;{AV*HYUL-K(2Wmw{yM1rv`CXDu! zZB}cyx9UkredN<ORF<}Y?#|H|a*hpsO_q<Fg_gTnV^|u*r)X)PrpNkh1a56(K#mZB z(tNg-vTLeZnY*1^U1vOF@t_b<bxt}zAJ2Esz!{^n*$mha?e7Z(Y)fv|+A4n=1%v3y z8KcYUWRzF)Px<WKdu)J9562lvUK(UzsOqh=WGbS7$z*hjM6de0!p;$^C7f(Dd9ocT zpIG^b=&Pp4%*lod)ep_&x*eviE8-0!X8bO<y**D$Hd(X8%|gJHMX6O4*%yNVgSX6+ zui=DGIvlP&vG1DKXHDH?DLlL)zi;0j3u<lVlilD^b+7J2HsV(Ei#~kuq{PID%EyUM z$&-N7Me5SH9--4G1`5j>;bUku2rSfIl=;*-IyIUynwhfR;5OOXb@dfGGKQ~Zry5$< z+Pa(jmF4(GxWct=z?Er>Rgu?L@pf-nAuq|FQ7qx<;n5?Ce6aNtQu933L~-iwXQF0p zw(<0HqdI%FvGH=Qq*YpB;^UTl!+exiM%sq5zOI$^>eBl=D2i0O?fhuHXpsQIn7s`b zTnxoF<#O98m6bQyrMqr@xJ8C|tdBN!d0(=n&A5xi{`>_kp_3eyz(H7^-x`BD#M}uM zO<45$*Czn%yfWH`hrM%!*znXQrD{aM9IC6Wc0V^6CR+kZ5(mAMM|_}W^fu=(evl<+ zXH?r;2Cp@kG#zybi~&aa+EJ4n`!R|;!4bjYiVj#2BTK8SH2Gj>(7jO41`>Nd{$2Jj zo*GvsNic(hWPXWE9!~b%$oj>q(X_J#X$&+GKrdep`Pa408{r*WF%|EDx0GTooIni6 zWA8LCJEfSP@*a6(mKBY)s^~R|xBAhKTsu`#ag*Y#R3h)CKm;=$ZWG1C+r)Cl_b&C- zb{DHZvKrUkMGWzzx6mMxR%I|Bwh24k0~29c6AZTl{dkm*i>bWDmmvJQy|jsih=+w~ zsF|i(V2_dai-oRat|DowKr3yVob8{I!jT==tXx#*qxW&H$c)jonO=8{!ol@6i_^xr zs`$KH?ZaYBj>jA;RiRGaqbZp3VzFSi6Isg0i!WWSwtQs>o^{_=S7YDX(<~Zal~hwi z)6hwxcDtDefsCBO<w96-F*~ctjKBrC4S8WTfEv#aN){#`39CP&eqEo+(ZcB1Cm3kA z9gaN`kqf=r(K9?}B>3%q7e0BUd6CZ8cj$S$z&k)VLS#lt(tyH==)U#$Mdn%HePh4m zU@esI+U=UhKq8l*55x#X!r-0n(4D@szW&A}A$<jo9&U+ou{q4h!Cdu5sikH7`1LA+ zM2?2bYUw>fDkSpfqP8N-M{%ZcUA=K#9SU<VX(BdLxU7PLZYzQa4jY3E5#IMAgy<Nh zejiHfqO2P>^IO~V3)TK17)dd=l$3rn(Z_q)(1P=WRA>7xWByv4wFV&?dpq^B{t)8= z;cQdLH|@76j;UYKa$yMgaw^8ei-sJQQ=M4dRYAg^B~~m(&C3}&DTz^<GZCAI6XuV{ z43zH3NQlxIGO*2MyBFo63jG^nBeTNxJ}*>vG<bXG7vDv=GpvkY4=5goOy!UP=?D5L zEd-p|z>DZ_ZKg_^SO<?+$!v3WS*g)BUsvW&jwi>ktqf0CXs6{#?@W`cam2ZHm-Gt{ z91{DRyG7X?!myHaqExOL^%K<`+9f~45X1t2Ze}*iqGR>?+$zp>wW)Xazkye}nE(#W zuZAwP(`R4Hu?y!pEV<~V%qn?Luipl$C5H~SQ+?{DeF$#|Y6S~KI}Rkw@1w3O67?4+ z3KjMliEf3}R7^a#@R22G7rW!n)4m-vv$Qtnx~v>I0k?Cfpdi)|ieS(|S>_@0(|tO9 z%_!fYDQNMMGlW@4CEk`ajf=}aZxJU6OSWLDXXMIqwz<UA%GqipCf;k<?fTjBs4f=k z@iGX4?9}+y&WZUL5M#%0mzSMH@|j|xlBaUe^#}FFRA?8ZcP%tL7KxvC+iN{ep33UV zLc*_=vKu}dQx{%2p+e0^Z@-W#*2)zbInwT@+lTD`5X_LVBeNKnl;2fw{ZY-24`$bt z_4_ym43)<S`F^O21_`ZUb30xB(h;+<dN+?q-Gjy(xWtwdy>Q7Uyyj{fiOQiWuQnM8 zfxDc3ZuW2e_lQnKyXob8Sr993%I#U%XV+u_{H{kj5fj5%mNfi|c58lvUJNq<{~Zoa zK}r$Cn!j9+Z*)3nu&zExzTdGOVN*~@=Mr+9%!+8*Qlg-^)<Mnvc0{53l`%7wI&$I1 z!{t$$zi?qd)-4Xp%@9M&L7c+5-o?7rWm46;nvG@pP*_J{2;{p}+Pl<DS+Z99I^6fx zq2QM0q~?H$K_j#Wyf++-zsg>H|ITRTQ{Yw8)!tIBOH)lmh#B?~pn00a=|ZRwshz{O ziC)1MQVZ-bzT#tRS^hVn7?<v_l(0^zyL9z^OF7-;GUgjSnnoq1WWR6A_9_t989chY zGvR2a-eGyXt_D*4RodOkpxO7sGS{-%6zy{m5=<Vl))fp4KS@iiJ&~+fjLyYOyJRse z=O{ANN>6A7g@`^h4Y!gL7K^BN2J4`-q33pLmokYBoh-{NEZd4@(zKP$!$#wmiw5%% z!iv>I6ny9@spWiHC4Z7q@Bks;YDIS@=HD&*p#^>8s@=x=y#^Vn-K+?GI^O_I2Srt@ zovoS(q%14F26Xrjb?O6|s@y-ntSabRoY1n<6=J@t`sh`o!oCzC#H=+*0A}#m&GDu< zp4f-PxK%x2{ty`E8E$o6Y#EUo&F;?CDc8Z+ar}^=5_H?>TCY1#X+rLRm{5kKQ=8Cw z>3)Slq0NVlSUhg8e0i#DrQ#(!^f+)-&x-5zURr-!c8zuHttkP79#wTzX~)`k*kAZu zA_Z1+3mjOy-Y1d^y<I6Y@dGX_nsB4bS64Sjr9Je1UvCF(jKE{(J*N9@eZ-&O+EPuX zGBTr#nm2KCMC2tAFd=-06JJWo<*euVO$i?pmiBf4*xz(Ce?;VJB;dE;@bwL_c^`j2 z__~7Aa_+l~70J)P*&-grpfCn(ec>&@S_S*0gPLV_3C8Uli~~Y2F*_)@_>l<w*3mVu zjqe<tIkv4xq8~%@W$%SA4qBu%yyvKl>9EZ2kNe(ot2-HK#7H8?*`4Y>V#b^ZL-kD0 zmTFeW>W!eRfwF7B@NuH^r0{|=suv$oX!YpgLtc~eP1$o5r?(l%D^UTjk|M~Memq~X zp$qHyFNB2ai2mc^b!e$*ik0K?>h+#RcY<;GU!UEas=d3$!zOi|cW|7m?5`S|FrjoC zw3A?u0>O7``9+VrStGYB%Vha`j{8$mgUUB<dNG>k+>hwz>*?k<RC+M!~DD>RhOa zL>TD&*>IT0->7pMHZk~SB?mvmFQ~2YHQaWCzdy=2X2tg$Z!-)`l;fM1NE5`C44rx( zn)VBJ(mv^{nNG=v3+k+86A~+KJf!~>h@?s*%BXmzkU6Q{nV>zQkj`SD^^vd<6AP1F z@ViBsP^#;p6Wo_}`US6HnJ57w9&D*kZTWMghRk8xX}KcXq&oYU5Upa-heGkl4AS&O z!*l%&m7o}q1$pm9t&<>vX|u~kH^L)MD#za&H#x^=O(;z6abH1;uursaS};2&qi?FT zhT6jzP`qRVL<z2$*qGo?=d1dsuvM~H;ra0TxpbxLV=r$t*&LM8;<hnb^6CA#r!SMG z>mOLkuM+RK<rD6Fbi$W4R10F5mWJO(YcI{r2sMNZW9G4X)@)dTsNX+ddeP8S78X3Z zPi5!lD@&tXR9$}vC9A5=%+*Cn{}2o^xs4m9J|>$OD^$~t_MeTSrO_(xQJf)Qjmpj` zsIK~vRNhKS#(+3v@;vWWD!=Fk87bA{oE)v!*U2VVow$$_xeS_h8*yeO)L^j&{w_V> z$gaV>)E^vTWF)A9daZLZ$tDopL!_D{{!63EitgMc=2z#JA(FXFsk${r@U(LBfCQ%% zb_ssl6d;Blv(|mLCq;HhEwqJ?WLltlCwo0gyfW$=oMMFJ;)LUH<y6x7_N-!)#5eZF zX@p$9A7|b&m#>5GnZ^?W<-H}zAqv_Ya`MUE!Pu-VWp9;v2Wed2_x_Gj+x)Il`#NCq zYt;G;)k(re&-)S`vjTzq$5G8!@Lj`8F7Dqy5-FikL4yE4;cYOX>nk!a2qI*+-Dt_a z8y@b$=L<#1>773(zX-pl{My_l7wPIe1PAXfhq_J4B?yY3)JlzO!Z$0@5#oDTQK-1d z!oqj^N+@&O>49f6+9Mb7?YtWgjM8{1vZ+yAGs{HuW*)vV@Jae+Hu6NZpvyHSl{xF@ z=BTN*n-dX3Y%~7V*EyQ|=eLCKrDl?p*3=&*aV^|h!zacZt@X<Hn@3h~Gb%BxLJ3j% zuV%n4cH1NwTRXRI_(8%nlMui6P`eA#PcICfD~Z9DPCh{Eo;~r`#a_b3jUpV#Vs*_# z%&)7ZUe0x*qV*7@?so(90X$iG$AcLmNTB9FZ(kz;<vV;YTT>s~(P)uR)Z77CKr%tv z<?6FXJz3K6rO=TCyYasnBgy1x+7ZozKiB--7(2KW{h4VLLzwfN>tiAUC<KgoM!` zGS?TZXaQ}<X>RD|X7l!{^`ZXW#w-%wV-$z=0+1ur_jYgl8rFqR)fLD={O4bF#a`jo zxLhW;?6Tvn*l!0|ZB+O!&(O|}j^h&{rk6=S6Ma9?l?p%ukq}l&S{%E5&O(~{`}o|@ z=aagP2mSkE*GY!<WXD6XP98I@>Psdk>7L-n5_UbO6MOB${nqDS1W@ZW?z4{yn@7fZ zdCcTzSf6&p|2|w(e?CNnd*z<Wam^r)>{WxMsj=i%UcRPUKP%Maz2EaJfS)Y4R&2lA zS12fz64dY*HoeVMoso=%eY1OlEM&<jTCv*;oV5TmyD_d`YfiGUGg1reEwkPmBOZ^) zu{#$b?@eVp(f5Rqh7R~R$~ZKk5DWEl``Kq6X|S}kswznp+sb{rc3d;fRF#p9f_a;| ziyYICjQo`VS4wjG<sm+A?7XDMXKN^C@DC(0BSyniw#NL_JpP7oA}`!p&JZ#GLzt9^ z2n<~A$Ud&kC^2vq*wC=<{FF_fmemTPpGp+$fGp+O%zm4BeUgVv8V+X+T$v2=d(jQ6 z)t8qbZ<@qNT3VXrTyaQfsD8`<)R^hfDBb7h>EYG;n_vIXP#@8hq+ht7D=)*^>-Uge zp~qj0ir~avVnAA!S<6NVF`Y)YQY30+oqMPus3KN}gc?=TpHh@O(x^qS5=CrcSdhe` z$AhLBsH9FNc4^QDRJ+2J11R6w^0F|4hX+DJEuydr{ladi@qUoxyc9b+CZI$zHDI0l zNzm-C$piDvo0tWvTea_YS{}dLH!3Z}ncbvcQYOLB;!49%*zzo5^I0=izhaC7d?d`Z zZgxmf<|&f}sYy_L!x#*l0!fucen|4g!X~KBixo8gB&Uu;k4?fJV>fB*R8Pgh&A{0? zBL}DuLUgsw>(+D@rtLQmFT0tBZUnri9EfcvrO`-Np>4?bd%?}X`TsdS&$E-8Vvw;{ zJ59<$jzWr*R{)L4NL%JrAsqeyl@#O{JMSu%s$!Wai73Rl_RP3(Fl35iqCj8?CY;G& zQWT0OEDLjkFlsn%$U@q+FmDCK9XDvO(@3GERf1%2sDhM82f7T~@Haxv%sC=igMZb) z1v8*TaS_?2D8EL%t@Wbol4Nnw&L9=G;xNW!aUhhUkGi>sk<l*~1Vh*CYy$fIEJHD; z?`tZrlkym15C^Qf-h4w)a71|MGSmr+jg2VI%SpAIzs1D=6SjYa$|DzDH>=OXLBl_= zf#`b&nf~>cy)%Rj(msb~w8L~BE8f()yz^i8>Y&AI@G}ed63put8okV31;)&qU8y4n ztIy{`X+<m%$j2H7jKX8XVI5(J<yI;(BHY1)pexK{=^-}ZL&(%DM)QLg6h3?<QJ4H= zoFm#s!047Y8@XZH0xeuSRUTPrnR5Y&>(O?<zHmyPQ(Km7N3{hC0q}Ytcf>r8h^XYk zH#8%1&2n9ot2iOIltp7hjXD9r!XZcBk=msH-xonPO`r*ZGsar8+v+F&8G&n;MKO;b zWmV;v^xuHD;5+oTgGNTq(Ko}NaZ^mW=MtK|f4A4Ed|M7Y%yTzepk2R$yl_R}8PheA zQy;xv0+uFy&`^e9nJY`_-K<OLJ@kwo3-0WOBcvPLQp={=%`R!oWm_67)j}D(deq5_ zx0v5;PVWZ=H!$)1@e_%IbX8R5Q;EEmt$+x$6uU{iyG9wSAWQeO5KScgT`ZBnEeK}{ za{mJAE7-`%_dtfoDG<e)WY0<I9dazl0mas#Mw!{9Ui0kKDm(s(uDEqHocNc_7dR1V zVNmj`zMiM)XS=7my~Y$BSr3(z)~jYIU6Ml-BKS4e<YCKf;BP6rw2XcKbGgVa1HyDX z!j#jbYaXGRMq#9r684a3wC%V`nie0Cs@k6*IO5?69O|t0aqfP)y;8idllecHlBA<D z7Q?cm7udXFD(+Z}RzVHwgQ*9$$M+Ml=F<B(rG2X0sfw}OTy7HIcd2x_@V1}5aG3iF zUIV5<5|NgUlSX#K>1YBWT>%F&F>;4f4-}lNDMBYy=BO`y7bZRs^0XqTx}QE10WZ_b z>pC>*r1A0Zh~tLi_b1i4gE=6RR`BEvWR>El4xo834)oj~wEW}N1{5{ioOfKE8Re4J zz-<!C$|$5itN|McXOiV>%Ymr7!m&%Nd&^~mmzXVObH&g1(<XE7XZVlM(5<zD*W7Bx zxTJKrSjROzK5`|@v=+|G6caS|`vG_2#9S$xNgeFF?Wj!lg{-683to34t8T@Iii6~V z`cdDa4lF>fhjR+fdPd(fas`4zdGD(o|L&<JOTh#dfN5W?o!a4P`#thm<Yqbda3l9_ z=uV+&o#re#I_E)ED~q?Ke_q?w{8;NMA#=Vj;mnvgiiO28zTm}a_Z_ys?qsg^)CPVf z?(Mi48(&D$ER8je2XpsM&1zgKuW3*<azYW0^WpnDb{D_*<G8YrZyf-jP^5IV)%65Q zw}C=YpxjE!U3D8Ok#F-^K|%sqOZ(Td`tP_AlhsZid|{(}<2XnWVLm>-O~0#--yyq~ zZI^=TzL$v~Z`S3sD+8J}4mY4hczR~FleJ{m9z)QQfE5E5^zA!U(}WzIjS?9^w0Vd{ zB#EC&_GGYwq?BE!Z9Btp>My{Q#7;s_XH!;xEV*mHq*l9chLjS{Z={C!Akk9Zn$;O< zKs3DG2iG7e`<!xu!V+S=vfC#pGvz4zvfA%*o-uo2f3b(uJri{0%dL5rKAgMGVYTAB zjlUCD+MF!n_~@^4n)!P$lj8kUj{WO>s)%)M(2x^7y4Zp!&n*=d$qH_h#dq;Iz(d7X zp@_}3Qgus5n0dZL<M4f&XjtD}^l>)n8pmftvbEecr4yQJ0{<6)u{y1rB(gPtg_GZV zZ-7biy`M%8|8R&0*%D~qdGpHm_2)n2Z*Nb}m)ReY*jZoOjcYcls;Ur8!SUz7rlu~? z7zg`~m&1r;y1Cg|KHgP#zXp71IG*)d=g=7odK#L~vq&bfp?)W3xQn^%?KtvK<g1N( z^OxHS`CLJ$0hKb9ucs0rN-C=QW{;hLqj*s=)Trp_$nfa3Ci|<QHg_hyc8p@$<!$D( z8R8B@A{X0B45V9zDe0fk$FbtqEBui$&L`zJ2f)W7=W{v*ROB4H)5B4EQv7SB<bU*D zjYTD@We-a41+j~EGq_qY+yvM*D6xWCTTwkmzzJ!E<pz3y+}54!2@+81NR^X1z<=?0 zb5Lm#{gslEwj(_Ac;Ii`lNHQIWd5r>#&Ab!zlD(-PNuCuu9J%md;GJEhK&!yaE8-R zLKd&<TYV8<1J)kq3ds;um7BHipF{ga!AFN4APuM7RbFm|jDCYfMm806D5;i<L3{N< zT+kY!z>}k>2<!=7DQg6OLuu)9GP*C467B(}wH&vew<I5+63g1%olAj2rjYx$V!g&V zm3-E4F2~u;P?wsTTWdpMS|9DHpsUTF_45@d0!n&n>e8~Z%hOW?cw|X%&*$qrz@Gzs zG&D4<9`U28AX}$H$ugB{tq#0fp8F9|bu|!R=05}oSjhHZf2A?4nnZF4y$F3VcyM~* zn5D)o+^_V{dVryk4UfqJC)?Va)*yclaS7a6aA{lWA?-xQ7PeiPvU{_&k*|s7?qZ>q zt9NoagOH%H35y2@#?rxq1%gr2j5kR^<e1}7v7W8`?29tXrp)+9n(Zh#(pcedG(*U= z91Q27-F3#GSND*sCM)l1Zt#EP8F;Afu7~e$=K6$23y#sj)we#GdDvwJ2GccCnsMv- zmB2?1=%K@AeW6sOoz1Kb0BeLqNz0dC$z|!ls9;F8E$wHYz%Nq_x72Kb5AChjMP-GL zW+2e@=`yw%-d{OTNv}p~B4o+XFk*Y#pmFa9FAQLhB)`1rDFi!bYCNme;|8sMWPHWu z-5}*~>ub;3(a}+@BAE^`Y(K-g`s3-!7yAH|O`Kx#^2)Ngn=Qgnfcig}gMlnccb07G zAakAbi*Z6vcpNOcfn4Y)vrVRhv8I_#1CDkKAN?yFfa#Q-g}Cf_cl7=FcpK!B5t`fR zPB63iSM2wI2L#DBM*1c;N(8>NVO68rEvdtlY9@U!6S}$d?m$X-=upj+HvGl4PeRTh zU0oc5qgeo4C#5;$uLwAAhXotiMvzu+9bGQRxzC6b5%*ptmwR`&fGnir5M5AE`nIjG z@MCH%O8O{V*nN9<JZGbny)ipj%ClS~R8Hb;z~_Uuz`nUMN@NEO9li4_=&QF!%DplQ zR7DE0F>SY_;IA(J)(7qPD<BY9wotyFwcg(^w#wpF0v%LaQ9;K|VKSNJ$p@n2kJ16= z7Z&m*zl&a;oWLQ+YSbE;dTHUA!<tXz5TfF)EUcs_1p}caT~r`bEMo`++1)SK|Ne!p zUr9>ka$c&&BMX)l5f*@o4wL-f0$msJUyyP%PBjR!3*ZEwS~fgWIpdK`O2y*}+}1-h zT4+r39`o61KBgA-Pb4oERiI}hJq4H&6IwDH9cfhivB-G*JGqxzk(nj?bB2^aR2AQL zyIvQ_H`<z()g#>8P;0HKXI0qSgwVt-%i!ko5iBGC+tl~(V?)NAVko$l*rl#R+V@4q z-FBKZl$?hDai(QSKbe!RzfcAH6DnJwO?(SL&YM6vt|)3eM`{xd{ntxE`W%|HkQo&f z^~t6cTVN_cO@ilEQt>x#2-e}@q=P2zKzwp`a;i{8Wn~mRRp%$*tJPx!?S0*S3xY~M z$A8l2riUp{kk|Y1T-&atqppsOii(PjO_@<r3#WAH42fX5WbP!tAph&`P3U>CsjK7Q z<LMOtRJ}?oI!|$Yy!~x*Z9rVy^5kSFhUkEVOwn*arKTsofd*CLzc17S7R-*O>`Db* z<d~G32|t>GxYSDhfO05?0kOp<MTd#LBiRidyY-J<_sWA68IQ)!dM&;A<~&W?Dg0Iy zuamd{OKfVk-lFC2rSjE3(I|_qk2J27)eg5F@#4V{sNDt%&ao-Fq%k!}e*`b>+8W9H z$Bs?q$@kG;`hK9xn2*awdN-Y3!vLw8r(c~yfXlXd!nfA-Sp3{?tIgX6#$!E`bxSs_ zZiRsbtK9GiB5Ygvl{<@BV?Qny@5ZpUcQr_YH6TBFp1$sO)vKx2e@#h&3ZbPrCar$W zt|`_CmCQ_1l-&)lvx#>p-@-R4BHw43_T<LJ=DzFw@I4L0k6BUVi!HxxpkMnaO+VD# z;Vl(ZPoL5Fut8t(*K8~(E!2`DgLLyBC&+*NH+zBK{$4c)Vzja{mMN5r1(~w$L#R*u z61Sdl{~kk%t1KChPi6Rr>g;kq_+Wwoz66k)T-#O)S)Z@N?dA)&Cr@ASIOcTg;k|Hr zAE-FAUkf|VxfZ_KxSeN9;YAM|Awp$#?uk`E1?@olt4(~AeQgn$ST2mo(TxrpXs!tx z0N)MBnwL6<DM=r8gX49a&89Xfm1cEObu92y+)^Jkl-g-9eGvtXmw^-oK}wP9AaXMM zVtBaXMFal}d#D>73q?%6fmuQxy7Ol6Ovv-JY<YS4>-o};l2SfCu=yROYD_LJ;L^}c z*25Va%nIhDx-Bo*4`iG&SVxWfyT~#OJ(OO~H?KoLiyb`u=0Hwtp?;#3gqKSe>}Cuo zyOZsXgRyLnjmB0sNbiNxrn{Z3&g8nVu`ihGZ=V2Xxid;gjm0B1G?Y}$)g&f#>M`v~ zMc#Sf()IZ}NK^%os_M`?ca;8SDzIN`6+}Zht`2#NBwDYl&e9|Kt<6~1cX0JRDnI5l zb<|CpurX5dTd{6rUkU+{yZ5AO)1(Hf5yvs@=7NfCZRUL1{5^@_Vb1rq1r&-WfBr>8 zxIA@ykP#95Y<qfp%J&nVfLQ>|^#&!Cm`HE(VnJ4ZcNl^NI~XocD$1X_^=G#}>M=1x z_L}YHr%UNQZMI`t(~TD&TOz4%@o<FN!Fm);r)YQd#Q;undiQn#O-3e~_gj7zQpat2 z0wG@g&Mt5p2TSO;Jx8kBLe&i}siC;_RrkRPjtC1);&%jQt;*6Erjfz^zY2fOebxn; z1;OuXX}S^%3kSC9m`jA`Zq`q9p(9U>Zb+J>4VYbg2HW4vtHvTSj4r89c%D*PjtKZ2 zRCx2TlTo4(DmvHZ^>9*ZU-MJ#gsw3~xS98&C}%ByvQ#1jo9b*J$$LL;edDQaK?#Qk zEnaf5pIp5Tmymnk^0cPTxdms@+?{xZ-^oaf7{YE7bPnm#GFKxB+^n}XaMuX-BE-Px z1(j~LPc`u}54or|E0loH$Xl}Q*ter8EhAQp=cPA<BEN!y+2G;wu&v(E6d$?1Y0HJK zhQj8Za--q9pPTCC>j=KVexxR)g@J%6?wLtl0yn;*`b+<0B(sZ+!3iwm;q<fJ_hyrD zg<s`eXXe&P?3>d0g`vXn>o%sE%HDpqkeNDKjP7nXpAZR;W5eZ1ey1cu{qH^m2SdTh z%;q>OQ!7F$&=>5Jqx3x>#|aK_9+9-N31&)KEVl*K1urqpv-N4yjeF#x#G2YD_^9t= z#@f<tR+zUpLevAoNav##6NAHXH$u{@=FINaQ8^*H{haLJ2<?Pc0-;(yhU&wHHqL2$ zLkIj9)6|~(4CLKO76asGM`FPvzTPJ0Ep?it@NNSLGWE-qs6Bb^`_=6N_Nr0zzx3;s zwO?{3rZ4zcu<lzF)Z=z|(|TL{Hx1#FXaX@Daj+4kvM_UeqWL)msF}y@zB{G0L`)HJ zpH^uI!-i^;_vZf3(WQd~mOT>K!H3kSI~nA0i3nNgCTUx;st%jiI%BseJA6Qb|CN^3 zbyc3Oj9#E8cz2VR;+!Yg4Q;9TE8II8?_Ql;RH<X8dp#l%)!)$|UpjO>&}f{0?%VJA zqw?7Ov>PEwtEFUP6*CdNpQFh^5hyDVbY_kt{q-eoITjWBo7n0MY=X-W1d)l(R~fZV zQcl&XnpKq^Q_EiCG!ytY0an<^Nui+>Si`-$`z)G<xMK(L%2G?K1uQuyEF7wMlN?$L ztx13_v;^MOGjm8%7u8aS|E99Q0=i*QkJ&O#+ZHsS@C`jsZNy;j@oyEYMrH67Es$aA zO06yqI~6ZNytyTbB4bRSY$Kg-=l1mJgs$}-;#oSK9KwvaU;L$(OpnLH3H?MgmKI7l z(gzTOMdwq8^z*XUD%kQVx6y-tJ9BoI!@=HS!gJ+_;Iq)XR~f6ZQ~mfWBk4Y$lQgzJ zyGd1vSiq{o{5*Yby=XEHX9~&xz~v%lD}kBM|Kv6#s0{OCssAD_<OlU^3e?ciC|N>a zK|T1vP8ubLT2rZlpH<~mja%6Sp>$ghMwq<(uNt5_Ic#1@xpqJK|LzNN$$vm~Yi3O! zVOz_6)9b!CddcQIqCT{U?VGJl;0YhZd7ARqpS^Wt)DE$fmCr7_%LWd%Id?9ME@js+ z?Sa9CxN#_+{>j2kGpoh?6wbRqY~xReq*YZdBoyc{xjYs~;!}ghRc$X0s1vxo%BqQt zv%{EAbUR}W-z%yRMrz25!Izb>;u0HxKxfrq^M>J&8A9xYJfY&iYXcARM2jE|`}(Zn zy6YoHMfoId?+A40lcR%mM{>n_kVrvLU_lTDOGFfAUKxBKc_QJXK`2XiG0VM=Qk9Uh zeWh*vS(IDBita>N-Tz|&7}oN*LM*4AHZfXrrJ>>=G1cb%iT=W1Rk`fv`>*{LM688@ z)Kc{tVQdgGaogQT&8+rHRITEoGaB^dbk{)SG`<>sDEI@E*RW+8liK8_s?b?^o=xBI ziky<_HYh<6so%h3t;VwZjiq`rL9grsq@=RxL_u>aN7VU-PnH^OOiHMx>MxFq!kpuI zEpY?levPJ=f%11)?h7CTCN>%@>X{IWMLJgY`+z3?qAA9*E8*#G*BSnk15>ZV(9dT! z{%N~p@#5ivl2MU@7I_pR9Ew;+jq?<wqQ?9IjRm7p8<qv&8Ut1g=qiYs5W{!cv0#E{ z<#31jm0BMQ9tclPC@w};`oG;PD-Vf+L{fq#(^CuZS%mhg$22C|t7bOiDz{mEoWbYB z@kR*!6;GbwS>~9Tasb3{^K-xTNn$(bH2;Z-IdQt#D3~b7Lhlt;=kb=jX|aGaZlpfE z#;s2vMA5F?*E#KhE>b^hcM6A7!pFn(cYs`hnL_!$sx>Z$Pn93_YTyH9jJUxv55L$= zktTk1b|W(>yR3SLoO=CSxh34b$_~mv*H6$iJG9bY`d9>s46)M5lEucX^Tn*E-|?f4 zV(>FtD>r;n7%}Vmy0`!ROpod!jR-28)|fF_!Ve;obj8mj2^RII*ZUuZtUDR?xXGnN z)cayoF9cbE+@MbDLE{$ZDt2PtoWrGHkbM*PM&lJLlQ82W+11$v>4W%t6Q^a{kUFG$ zOJJ>m&Ac0KW$~CNPUaDBeMmGM2>gfN08`v6pEWuHQ_Ia)P23-pvgz-LsqEeO{xc&T z=OY>D>_uPx1#plF)suS<Kze&GJeNG$Ov6t!QCE8+@EcfHz64D#VCBtEz=`v(t9kC; zrq+B;x;}r%lX3<ZB?GVQaqE~^ubx-Iu#Bu=xae?8g!)i8+CU5QIk@fKvD~UhG`Biw z%&aO<S;PvSuD_7%r~L1(rX&Q<GMvLfMUrg|jP(xaS+{7Uaow{&+cG<LBqB+YL-9s~ z1w$jltPxIMf4m<-p1fy|x=IcbZD4QB9<dVkO2_3eg|*-GE#k<SP}8=!?q5N@<}}KQ zAV%9P;j)fSX%4$-_v$ya8+tbV*?s*hs^t3>(8C}|>Kvwuf%)U>+y};hU?KfWeq~d= z!(B7MRO*s2(@s>D3od{nF79V@oRekhy3j=oeCmFJ4mA%f4Fv`1JtXY52*+~s6Pc!3 zz1P}8b%U$h;a4W1XGf__Tg685Cdnd*((~#K`q_Hxd=lunJKphI)aEnbFP={Gb^T1h zmtLH;%iNpa%0dL(5#h}A#Pr6S&<;ar8t1IX$<Vo+X1PWr(Izbk9pA7&dhzk{M_){$ zPt+hnC@3}%J^L%MO}`bH1P&d!duc=p67Wkbl4dphTK5d|-<SX*bF)QT1zFuHZX3vS ziC577%Gin5oZhT$X*Y`wBt@SpbLHsTLVkdj3Jo1%;Rvu6QwcT{X+RYzh=NU@ZpwQA z$6y{n)v_+L$}`*!GI;)B##S<W`GLgs$P`CM3A<ey3zmv)Jz7hMoySVMs+We2Ejxau z8leCH46|Dk3q20zJhC~CLH}rHX>dne!TK?T=5rPYqo1jfV&Hoeok-=B(7Ov^xJ&iS z3?58V@Vy=uH8SRbq+b3Z{Q(oJV^=*?@1>>yYtXoHpQ*FR#V2ERTkxy1;L&sIC%JfS zogv}vH2K~QFKc<F>gfGI6mL3*3EQbk()b#Sf%l$&MfCy<L|(R9*9P1O(SS%dG46C+ zB`aSgDQLoOnCm~!wCNkSwM1f0O3S1my-V~lrwUd%^q^_B<B*fh@IKk=dzX@rk~Iat zuZ)l%W%0xhlMhIb$et-jKjeP<Lwd1ub9-EG_vGc}B_$<+hQ5M=*SY+jXc(o%#n6IE z;y*oN2zX`}7K-Lh)G9UdRA@6ZGZk_L8B)h8%F6?L&`KALu@+8Th<IIwcN{2w{9w>- ztOQ}5K$tk38ZdFIa&Fl@a3QgcsVpA!N^g7ntDU}3uo=)u!`r(%h`iycX4q{LlkvPE zA6X(XbTImt`(<55;u2g#OOlNA7+GzU_sg8{$Em0&l&+`aq3z{Vrcnl24ngg<v)t%6 zLs2r{NPKEskF*Isj6RXwhbsby0XHeKzYDRRi$6Q(4wDWBs!8~%JdQ?TNr+f`x@(wW zAFpEU{R&RD!JQwolhqmJ=<;FsY~FGl+zGaMu*K>6%YumUa(p9H=d9o-%JbmMCv}H# zSS=;S+DyUeZ5P~-2sbawt)JGQKN!RCJ8-DPA@a&;GD)+wDsF>zPQyXdnK=rjN^4O( zUNqKT^XKu)>une{exivs-1D8a5^DXZr(X(0#F_Y*ymfKR^v;|JQTCx@L&W?4nEJ=? zN}lhH7ml;z9osf0wry)-+nLz5lL;pFOl;c|I}_VBp8fru|8uVMes^EpwX17Yt#z;a z!*hlm8ont4Ek<r;9?m*Ia~Ui!XLHur(F89^X_<cb6XjQnJKsjw*c!Aow&&O{s}{_X zV_+4xM#2Wk0nd!d1>rrzB+H==yFdt$Gzn>W^%!T>{(X0;f7L_>`$$fuR^Pj3_LNRy z@xbJXTSgbVMgp`uM$OQv%iBBsEdy}TQRv_5fs+_qfJ8TR-{3gKc^E7FJxZ*@KsXYK zC{qRkh82j->qH>neSbT_3`;0_z{|@koDQOHo$ie!aB*;$o0~5#FB`gyQGR@UT#IbX zvNei_?yuCD(xut_Yj=l)gfytp3hv*#$>DbHx8n=}FI4sD_y8HjmGk`|dEe=E1o=O4 z?}HGO*-1&@%{h_ozna)g-+%=n4dIW+v9$pJxwWDX%ULO)iL5l4wwkXF6XSGuBmdFw zJE>n4K3mgol<0S;lVrOx#?lcgS&`Iy4^TI=&fb1eA3HhT_Iqp;_m8GJOgY0x16Unx zGteaG9ffQt5gng<8=HiTQVw(mcVp2L?rc3(Cki83mhWO+g={boFFC3Dj6}FKGU@nt z3vITz?z}&Hmj$*3?eFr0@VRLU(K+);xiB9@B<zex++57tahu489#$<eZcHdd0n8>M z`tuU`yc3-lZkEZK-G6AHi79-g5}MXYz=W1~t=wO+jxzIjR$R6yDJ|mOuSV81u_-QW zPeYs>6+~_!YksxYGa#Xbwb*nJcXprtZvTUV|KAl~wPiPI^mmV3PgmUYO$^J3iZ~RZ z$Xr_M2haE8@7M{=4h%AjbUIK(PChYe_X5aV-I7YaP7cn0aM>M4Cj|Uzrw$&DGC6v@ zJ1#%tN(A%1?z#_a3{^@kvPL`?zmC!+sMlfh_<TR3>+jVFA%Tbv2ANTet1HnVUW;xb zkptIANJvN1znB>qpcv&96=7-y%vo?q;O<|1f%yGFCLzx|wCZG}G`7~(DpiZXNNaYy zcQXE!A`Av(dhQ3Z0)y>#2?>eqAfr)&5o<{7N*IE`aB+Z0c1e2(83a&SiXU1`vPN?B z^71k#C#S-a7e#E>%#1WYg&*aN=#Eb8!m9VA4-3ujoD*oAn}dIfg;YfSd~oBe#W3FC zZ+%m@IXn{I&ARV8VQQ&~;i~vf{T=&rDA%gmX%HIn@j~q58Cf>Qk38>hKO;rx5~(!c z2!c2hy0tI~8aX&lvR+MIjX&?-ya24aBx|C>pT9lryk8%WYDab#Li(;gSv(W#e$`j^ z-|{xqgdvLvN@lF<m+6vajY(lFM4in#Cz)OIY&;gbt_0UF&j_y{*1XChk_ULp%Q1K* z5Jv!5uYV#sAF)TE2b12tg3hQzoe%+0%M7HoUdG`v<X=Zo@mfl58y(#^*^tZFC4mWj zigP=6OK~N%t5oiO2}LYA%V*bt@cuJ_1Fq(YKsue*;h%PPFz?Gp32rO@EO`lxLZmN3 zTm=A2b65Q~o-)yb>(y{e`^qtxr=_y#)}S;Wkz19(c^1=K%O3{0*@|mSUmHWUbQwRn zIBwermTS9=y=<*?O6#=z)Q;1RWsFiNL&}qneh=~D06z_^@%Q11c>Kx}2_33bGqJH+ zWMh^saks#^z}cX68VvX@1IH%a5D2DcVQwl`ff>?7a`i-W9lKv4_lR{)F~_aFxoCBl z<l_vBlmg%NTOcW6Fv`h!5#Mj{tI6{LzxhU4EI3thSR_flk?FYbK?KcAFGWi2dtd>3 z-S~$U0w2H(88ooSA?lZ}YDN{?y}V>P8lH9rY7o7@Cz*%j+gs68lm~&-K}9#ghUEyD zs3hqNV;avx<TDJv31s1)weC^utT0TCGL!d_rFq0`;~9zZzegarlYae$fyi8(l;lL; z4?=Q>^Y5dZOMYKSU}S1+BSf6q)M}78XhJ6Np8R)18XuOJ9kZ}%yTC(lm8LUH51lY6 zwB2}B35$uL<mdDTAvNq;7>9c^`pn8M$_f@)1}%$ikZEkW{XzWj8L0;Rt(qzcv*~t% z=$-UM>X+$|yc_*$7o>rcDZvuakwLe!&fM2wXo5{_OPq-Ghf&tc#u1%nm5Z6#=SG_T z{#(II)PQdRhHjE^^_g=ZS?MvRHzof2Y0#SWerCpmo9xogBja>Sr++16MOG7hh-_V{ za!5*(KS<fn{{8EQl1G^gTD}9{C!C*PPO3VDacOAPqD%G<5@72F)9pvA-8_qrTOpXN zCL{KnN4v;B>z-D-a8KAa>3niqZuM$y#P=}&yb`deHv|vZ%K7<uNKODQ2?mu2gbLv{ zibIHAg$+pr+M<Y&lW3x(_#pY{_;#SuE*X%4r!pMj4W=)s;04ZudJ$DQe;w)!=U(cc zvxs~&7y!hw*JqV33mHQj0YHTtR-$(Ado%+h1wJ!RxI0SPChRnLZ(lHqahR({>%0lL zU23OhN93vS2g}IID-b*r&y6dw-dCdj3d`KEYuGQ)57j{jDaii*sj`i_rZ`2{dn3+8 z86poZ2mqT#lZ#JI8X%UM#?JaSSgWmQj)7I8JBJcaACgyh6`FK~8kK5-<5DiIuC{IN zFO|<HMb?B<5+o+YrEw7ARDpAFT5MO-v_!PRWL5s&NN0&DVe4ep+0r$=ym;tf<+<F{ z@waK9v+Q|OmYMMolZFQd`dkpt|7s}1>ly6_=jv!Ln%y?4>5OV-7>t1r%p06UJhd*& z0EGrTifje#c<PvSXu*CpofeuFz{Kc7!gwrff&BVPKl4rk3?_N-9yKuk>Y&Izral%3 z793cijdv)S{R$gU+jeV}eIZ}=Zvsw3hl(a($rHujMncd9fa8hb3SV1e3~Nv#Qo}q_ z4Aa-)qmU#rWWu$*R=EGuZhm^J`l7a@_Nm5a2GX_Eu&a?>r_Amg|NX(|K`XrssnwU| z8|2V}e;L<S;lKHnmI|_v0Xu`g!NhrI?x|(?D2?}q-6V^}DuEumH6xb}#7=$Ew3~dX zh74m)#!%@6N%;xbPVnX0_2S+6q)}Z?Mj;0W2Tsy<ur`T6-yo>qi$D?)g$0v3;Wi9k zI1lq!Npgq2fP8*9Ry+#^K>8>SVupZ_5V0O*-!aI|uP<>244@3_-tjL#QW(;^p%=Da zY=JQShf_?u+-VcYH*`fTvUi3f7gAZg34TQo<2hY|BgKl62nUYIzo{rqVPS5_Y%nbC zV&#%_&~{*tVK9`CA2N5ly(FwQ_GTXPi+goF81lGpJUEWC6Op(R#h2R+-o1?y9|O5T zC`2^3ped3VI}4HFZ!w<1?}%6xvh?Ru%8GmMFuXZ_3R7h(b+Xj~L^LU-gl}1%598wM ztY!OMOY=b$lvA#M3;O={5~S|_^XSoQ)L|AZ)VaH@85;kCvPL}7TBHJ>$_VZs4jUAM zlm}*x%t&UyZ?}*9kY(Spjjkbyu5GPv_lBQ=SpjJR)1=?$Q*c+YsI?Ie3G?AmWBR1y zX-^;jcIUpzT?IKI#0EH(=hbir7x&0q2;QbujoXcha#p6zz>lz0E@W5o?t=y&5uL#+ z;r=6>U$(3)^3BMUkRwQ$GXk4_L3-v2`|ndEUI9t0!*c%A5*`pq^)Ko4jFC~BAr(#9 zZGt^&LR03A1Yjq&Ao~h+mN2C;dh5a=P#QSb?6ZVfcA_ye_da_@&46cakc`!Z`#FPW z-^3#~f8RQ!wvsI}h!|oe4TH_lEvy@vKvO*`Y<f2^Y!La0X!Ej_5S`qcjPX5QXTjR8 ziI<)Yam<@i6pQj~4zaSElv$eUk*52{{(G|iUFj{N_}6FE2x-Loetjoj`jLR^Ha~MC z$<`wD*r<uQ|6G70-}hX5QQjXV%7_GpL8&SPiSji1eVtRyYPl#~-`~RAhor?B^!p8P z*m?sVFaIG-WaYEVWiWkjb=vvQ3G^>yOc9QujjgRn90@C=j`!UZmq?s2;f|WVey7iq z1CT-+S7JL9dYc?t>igrxdW-!!=kW{#{DRwbo`5p&_n$xEp}=fL5kEOu+1^5-=WWR6 z-dCDD2-Zv^Xl0Nqpzt9%ZF05>U?rSC^D3I}2>n8|4jXEc{D>MK(T1`)apys%S~kCv z8_LDuJRlzKHuIWRZ72%kURLqZ9pfik%;aip>^MiUXg>8Vmq>ac2ko)(XDOnM<2Vf_ z<v7%}`@pTB=9`2bdh7KA8dLek-KVi?1p4n>0e`&|ZtGGShgy8tHdva*ZMR+J6OmR4 zj>kTxtgR5ReC^$~cx*JpYkP`#f**Y-1>#_IL<re)-JU_3I3O3`yvbXXaaNXnnMN&3 z9EzM=?PTL&Bq;G*PIhro1DsG({Y!E4kl@%^<=0*S(Bvje^Y{i<`o$mC(ere|)om#2 z$D5NPNl7|8k3<krC|ppB>aZHyR~R;!-7GN<9FR~Ieun2nCSmcUez(`)=Awzi(`X?a z((yHL>cGQ?G;5!`&&K_g$H2Ewf`igGA1L10ih#vLq7&4UNlO_*gVypRT6{8S)rI67 zF@PrFslS$E07JTQP!jCUziR68v417Je4Nd~d}i+&J`>6G7coGz9NEfU)>(icfic*$ z^jqFhLQ8IY0dEI`MnJvk_U!kE1V0m80|+iyoh=GHRvM<sP#9M!8Itl;15S15Zxk~B z)W@+)&4<57B83LRcPH#EM$51f)?hgwqnsEj)2rI(!x6AsC0>cJs;&uArv(h!6KL94 zNZ^4(0~2is;U^;7BLsiylscm!U|L_DYE?vN8CxjRkrX9<E4^7%CtCF{{xbm2`i6*> zV2_bm(0(<3dMl3*9wXIEXniuUglheVaNR|L*>hnRf0=1YuG=cbOWb#ov)ZFfwItR1 zA+k)ZE$p&!2tfH!hdp8BRCD&wAyJt8BSW}uZJ~-di7J4stX&zQ?=10VMTp@Mwr50e z@`eQdK~xZ!IrhTS)1X&uwsLah`uhC?yaYY^XyZB~QFuELs3b3*(--%zZTK;*o=EbZ zTtv_X9RMb3CEaldhs*K!G;#2_LcmmQA2tu3EYJI*U5JQbG`jcJcjv?O*B}BR7wpTZ zGGA@t>*F;old<uU$N%QF0E*_moUBkM4(|ZP(LmsItTtl)fC$GeVii1Df!8AkndHYC zztj~0^V9R=5v^Cf$?P6CAB(nPnHv9U+MURqXbz_7E<5`^;pHYUoR2jZ1KKl_bq!X3 zw-AZ)u9auSErV*-fZ4JIg%|>ylt?v2HJ!?+PhXS}yVb1Ru>5V8UdTe)4J<{*^~w)l zbT70$BFEuvhJw)Oyib4~bC(JrA1j=gAk$Ym0fSn_A_J>K92+cJbys?7_Z+yWFb;!? zSR-w9S>Vw5t-uZ#L~RiQ0Am{aOUg;JL}Ny5bS#Zw76L#YhmqRqw{*}4?Q55KG1BwP z)DLE)Tp^)UV`c$H<vdGs+2(Vc54?7N#4(*?|6t!_u|cG6Kehh7Qav)*b@+E-^JbK} zBszM84?lV^_(F08;<Ax;^N@swfUkZxipENfj+}4Ik0l6^+OE(B%)(EmB8Wl!<%F=J z!Aa*|Soofj;alH@g8mw#z_o5p`Z5Fs9t$SsyX7N4=Z)*?yxRDC@pUH;W+WfKiu)Li zyGULg9n;*Ijq7{_YNK@so1%3b-S<qQ2jL&!OVpO13NzcGG!&?a&59;?3)rZqD3ilb zz2(KsE+jwoN|MSfmIg-BLBKSTJ%7a5NTMnc-1hn%@hUYxn!NW(^52~0Sz%MrtPCLy zJ!@m@=P*+4Q$7<?=iycRp~@?58-*hJk<=a%&*6$HE#%{rAO`Hii-0l58!)yYi4Zak za*5;sAwNEZKbngac2%O`wEf{M5c}(f`L34++kx}b#6L5L3@1$W*NImf8RcIU*ziiR zFkZsOva-To8w4UyT6BMltyEIyQ8Qn9C67_78D#t8k^u+I+D9!c(>Lw`q>bTDc~6rr zWOV?DJyNh)0^~THE7}`s01#@I06bSVDKI*HAYXKKcn6Kkobp&5Sjg}jjrgd}9on+$ zD3Eo`8E41N9DB!?1P)_^vkrxY^_RwUqYY~bTPBq$u+{|M*rZ!hx&6^u87Mu&Dy-i- zGw^K2z}(pRzPq%loo*)Sg<t?qUE1`K_2gE|^DCte26qbawsJSDjYG~wPwzE>(DCfl zt<iJENXUY}YZcd=piVkEKk8=|6;Bt((AMYWQ(x@$VeCvTydb>0bYFfUR%Hn%nec9R zXaOdvLok>~Ac-(gS{#Z6MSda6D5p0LrIQMCE!9~y=L8#40PH)7u@87Gmd!j`>-)T8 z?dULYUC?;d$gCjK*rqW@wUqJ^0G8%>j&<oZp!H>%RfYQ&Q9J&!X}xtO#qFMa@Y5FC zZ@1aJWwYJwq~5ohbQeBzN=dsZvhJsscj)!;EV(M#59b_3OEpt+d5&J#t>CxIj;Ean ze-4uY)_3F0kLv<4zGg>T8=GMB;92uP4NnS=wSZH>(UOLuy&%}nqM{-J!Kk+d5bXdB z3IG8G4tgIWUBDoCL5cN?K6kl=ZtaMMkuGwTBb=Po9{l(qgM9T1L%O<2RWUHhLW~ik z^P{}wbL73ZKpoOurj1}JfcW3x%Lm^kOcj3_%f+bzreS-)z_1*4aq;ni%hC~KLsU!{ zi^b0+0;xR?0ntd)j$#Vo_s6TPrzj=6p}OydmGEK@507gXk26GY-}Mp&Y6l~TUbimz znuTsh#Xdg#0MXZTzbN>957II`uRg&9rL&A9h=cZau+EuqGLZ>T@jHjq#77wWBRP#Y zeuAj06^k~?k46mEU9jWbston-<V}Pxl%SahfJl-6Taz^ljAJNPQ$q3q)cHxX#y|8_ zl%Xq1M_Xurr^1>VkO>_`f(!}_WpRVSlGObc2_N%<19h0%Y6e3y$Q~`@IxB5~2v6ov zI}gpdHB52`u#~9_t{U>Zr2hQ$bdxo9ZF;|zE&WHu|MKUk#nEe1U_j6BRHF*v>a)My zRtV-W$-K^L$TYx24gp$673~`3?B^ztC@p=|@0~m54EpMi50sD73{T(FZ=l59U?`aQ z+wYdm>Z+=@PZa;ul#~$kX(tLi#gWbCi@0#tqw#V2KkcWL^JPj`8+C*u`8?;4dLYo| z@%0WNpNAF4&uqulpXuq%K2LdETN`jcBM53bF57c*HhQ|A-&fn)B{UYZIqhLUV$!MH z7ij1Xp&wu2_>PZ1_!D;u5kcVit!_U(N$*1>u1zCOy=MD$|NGIT*vUyoy+2}-0CX%P z3by~}JD`EGFrFM(Ex_>^JH|`#2Vc?9hBY^#BSfQy0ugzqZROhotzKA~(bfW0aKpGE z2IEtQVP0FuG|(Th>YXY@XW?aYDI17$(OpEPbF_n8pYe!xVb5wyoa|}!QitvMPWk-v z98Yg>+3V%ezvm4tV3cH}m7!>ZK)jGEW-BXT^i8`v7&yq5F9N-zRjN~=Ghv*s_Y|R# zQL>Ns_ksVQH!#>mL0Y!e`FTc}>tGp3M1EfX^Og38Gk6395}e7YDHS@*k0pN=t{kL~ zSKFGvwi7qqP<B0rlanl@xVRpl-KdQX{T$ExmL37W!^tda|2yu#v(2m34-b}%SQ8)} zZUo`FXK`6s&vQRQK){K_ujw~}KbF5HCMKSKY%q5{@Ad|M+@Gi^fnYHR*qY5vxnJw^ zO3RCjpAXCd;4dIHhgm(EU9L4B4ZMkS=61G+I9d>Bb&2GF5M%Oew6v~(u_q(i!ahrn zlK;#Mq&QhT%~XVC<uFp0A!8=hvI7Ef6uj+RkoTjPg)|b1VokL}y>dAcl{n)4RPPyf zMC($~1e}mh0K$;zPtSaYx4CbQK@Da273)hhOQ~#hZFVz^RcHH#e5XFoNYgQIew;m} z<qUi2dRSYo(`y=4@JKKmja<$c#@ANStJV-9r242gjc}By5(V#Ys9OS0zNl$XxgmB= z@@H)Rn^!J8&QK`GPEbtTL#RQn^{$mwRcAJ~X>=T;>-FXnBpp?MD!ccS-9Z$TpNlpU zHt;38+d&~595Et-6XWCA**~2!OUJBy1D5}QuqDQ4OL|V9Bb!}5*vvYGnpK?kn{Ee~ z8C>j~V$qS;9h*KVP~Vv{=+i1exRgIS4OkGvhlizAHQl;3%jk;S4gPRue!(-24OMB% zh}k-L6j-3S3WS7u=f84$!Le8q`)C}GMxPk#Wnmm7!}$-(p?mX&Js%?tFohRFE=`~% zP8^HH3*&~&gM1q`SxfvjK@d7q9QQj-#jaZ}3C>l{jwFvw_U_~AjXQ(?>JHw0Q(@wu zP3C^Rtj&P0ns4Hq$v7b7OSq_qB<#<)`K?+bKC@n}gWqfvI^of&&);8Hp4|Kl^z|uO z9A}Lt*6t0802RS&USA*KKecIW_t=fh+TPDq>Zkpnq8IGrmfIa8OvvWh11-kwchH;P zgdI6{o*bS}R`-cSqy{(MfX#kDB-|BEOweoo(ND|GO-cUw`nL78l)o&Ny)Y8xC1VNB z%8tav6sDS$lS43xfh?n4rAn8<Xo%c27abk8^>XRy336oa4fs3)QFo`J!_o@E5aXmb z(8&WmZoIobIAMj-&GV!7lFLBRLbw`ebl_55zw;4i11@h<B2#rv<g|MRT#S|8B?tD3 z13R8m9_oaeBNj_124B!a#fwp5txBx6;4!A^3)5z`4UR+UctUt^aG@;Pa9MDdDh{yh zTYX6;P1J5FZj3w;Wk8~F;kx%;|EpCdugiap)(7>Xr6g(o4mB-xdc8X}Xv<YsyVD-m z7KCZ$rImLpl|wunF|O^ru=%py_tJVk9hl6}fl+kd7N*!pQ#bEGmdL=(;^O`2Xm~51 zUF^%`WVGiZA?QlRQ>5PVNlJXq&aY3O1paqrYwME&u?Rt*r+<my?E*yn_GeZTqoctz z%p4pSA5UA%40Hy{&0uq#pWT8*vjA%%45<sqN07xl8#Nc#SaK-b)SUT5i(|mRo_kNr zSr`4ER@I_e-iUCDZ)HD-^_j3W%a%aSzfS`KoY{;5<mfI#-<ry7ZEX?YV>bQYF2Fkk zCNBkl6&2|OS<Y0>_=X2B<dP(61Elvn)F)z5x2jd#gMgA8_{q=O02&>trPo4}I;)aA z6`da_ig)Cqg>`GR-u@8N$85B-<5mpJ_hNCXmrg!oBrFqCfji6PFz@r55Li1d!ASjo z5RWoL<U|`qoqj!WzU$}tprrWfB*(X(p$+{_+30rlPf&u{mVo@#XxB@-5+3@>bk%IC zu0Yh#+N-T&udkl9I{&RTAi&kOYSF^gnZON)R4ry-58^T<xiBTd-aPeX^Ql{CNH7TA zXoQH369-X8XO)FI<hS?t@3{!rOu@fr&vOXQ=lR@EF$mD?0T`^fqW#0e;pyq=r>8#; zCbWu6OFcY1np;{JEA>GE56Rfb#btc8!TsO&f7zUdDe39{$1{De4enVwI=a6v2T-1j zJjvwy&o}BIQ(gAL<20*~pd24g7r8cnd^7W7Dr<0=B}<vzzjNm9FuB>Kl#m$0YA}BS zWf$0oJj5HuIzjSIP=W2YUd!j)1QWhmlY8{yud2S8Y6mVvikrkOMO`T<Ds0fo0s1H8 zUk#M-z~~*cx7BDz2+6>tZGRP2j1ajF4auCbjX(%H!YvUlGmZXy*0zbi>k#O6f*N|> zuGD~c3vmW7wsKr1{ks}R9zB92MkEdnF0!p|9kaN4MnX-%>{SQ(qm6|x5QdP%2;6^C ze+E-2=L+A+yO0hB_uHA*<;hXlF8lm$=K>seLBbw?(lJ;fOk6^9Ld#{6vo_jKlF8?~ zy?3~94gv$mb^5%9GFlTIFKgbE;v#(S9~oNvp{*p0Q2aC59zNzs|K1B=0y;3!TzPq9 z)VgT8D(O;&>i`TFE-xDn$Te_(BGvK6e^QoIusD9}tAb76zfoX=P(CJ8V*p0`UvGm) zJ{t+Tb<*IVg1}a=BE(ccRU8~4NXb?af|_X*3TIhy7G7VVa6goVoRhft^{SA8zxmsv zz0h+Xir#d~=eea&+t-AlPJ_1wOJTyE3XynNf!1-Z?3id31~DT68!2=M1bwVgQEu@D zDy4R%5$_=QaWyBOAf_&(M<_}QUQie)ERq1AP<wG=oEM9K9p|_GbH4~!SL5r8rO3o{ z76*c#qfW7(b-QWAdHA-+pKmzSEMBTtrhX&ZFK96B(^PqBO@*v9S@P>~YU)&|t6MgV zBk8~SmD<~enD%Qn%`rE>oT+8x&`D&9ItMw?EEvN?zd6DD0JII-6}NS?QxeVs3k8vn z>0h&}ZfkV(S^B(l{y!(|$(50?q$lq)|4|fzz~<goW3*3j?f74uHOWUG2qi$SYIkHn z4dG+%#jc2f<Tc-9nQ=5KBF(Dsf$ni7e<_35#?{g)>&(oG&%RR?qz@1hzS)>fjKJIZ zBE6DQ&Xro&bM&McMPUwnn0P~qGS;SAx6LH=Njd?w6X8r15VR&c00d+|?JA0~GC@Gq zfl;SI?0K%%AF!~$ZlMU4&sqt7My(r}O3LzNVxS>XVg6*0wqQY=>q~E$(R)se`QOL_ zp~zx{73+!6MeH7snNB4*{gT%*0Y-np(TmU<`dG;&H_?+t6yRsE<M?Y+N~Kw2nTGKJ zDv}H^C0Xp$>r{+Pa6n@y68AC1Ii+40@|>+7V^P#fW)m>T@lU>r&thc5*B?H<8zk9J z61r-c4ypgTHE%X?FE7+)V33aE4a%tn!;*dBv8=pd-l^B$$Gp9*!PWB2b-RWeUAzqR zs_dWghA$mP66X<|m&DE9M1K~2L6@Y`<D~FFOgPPxgwnvaE*;Ow4@sGX?1d5BsJDl? zoN#sDD71olNye#P5&--bKgs)JTM56x-%$MTGyoNZOKzl0Tc6^F^v^?c&0J_NRC@4) z1Ps9gnl}7oFw#VODN&5x4dMv5O`T|n|Hy9+LWw>umY0$!a8zN=)V_Z&1VW1+!_puX z{F`Z!WL9-TodhnNt*+jwWfR*bA8ZS%!MTDmV%lANO7|=A#NB04oLqj6^8wjXe(5If zkTJ@R6>ep*GW_R)@xj;Z^cDB8#?d?iTV_sm>bir{$w@P@(5!QJNzX$kCVX7AuQz-P z3AcBMEOE00?t&xeBuGXw>sgRIqZaKiNCEqQhf)AGc$aQch8g#|wdu)?dvyO)=pb<v z7=W}HCGI|MwrfKjRP*xx3ti;GO4r{xfhD=7)5LQ~B}ut)r<Si#?ewOPZpZM`lM&+p z-V~DbJf>8>e46-xOkX~4NkchS@%m}%gOW5Fb;G{<71qg4AjLaw8(R|#A<R13ex%iu z(Ld7@w~LC5DOs5<J=yEM|5Ep>+dH5#h)%{5>)@2RV-l=sx|!2fObnz_IpQH!w;#kH zLFoWoax+O!#CkXNai5{}JCBQX!u{W|)PNi;tb`#ZUK-Z20o`aWUv2E0eEaI8TX5%o zIDuqFACkL*G2S;3w*A?Appn~Z;~IY#_bgm)v09e<_@>D6b2wFD%FVkX#}L@vALlwK zu?jXbZr}cOK4!d76X>4x<@eTY2g`Z~Y<>A?#m5^B7l5rZH>t-&oMn)lI;Hv09uKdb zXhtsI%xFpWaoUibjH0Z8|809ySW}ZgL_*`rYi_&<3ODKbjC>&V9P7_TS#zjCA5>|R z;NWDb|DCG?Nm!JC(yZkVbR`pK+2iep{CyOai=loY?-*i{jLqeToMD5sx>@=s7d)(C z`4rM-z<iuAwh}Ub_WS%^-#-&rS?x*crGa4a*`snxN~GK8Pz6@sD+0<LJlJCVm<lD# z^Ui#uo*u?ruP}d*PrUsx6g{Gtb<EvwzvzeJC#<qacBzO+;dr`^`)3fNshKvyB(0UI zWv;bo*3x`WEU*NFz@GcvLnY0PKsVAf{0<?R3<8=o6g4P`LKL*p0-+B1AXq%Dm*`*x z4AP||ElQY$^{UC}Y5yFz(AgdsUEDzkX`CH21kV%v70<^lp>s8VU#KPHdznf(+AHIC z?Vw0R-l0jnm6v;0Pid}x@a(}8I*9U@$zPSZ^SgTH&%F(<8OLbTH~dQ0H}#L+4f7Ku z&`xMTr}`w4tt<vuxQ}}f9X&2M<64{}mSte;{Q1Z<x0?C4Dd=?Qb~Ki9QI4QXPys#S zd;VLkDs5<}0=42$Ja{zHOc;&y|Gw`RP|w8gHK32F=N-6hwHyKj`@PWy$Dhgxt9fm; zpdY*FZ5E(dtW2TBVl(^hm!qFWrSLO}z?ehsH#i|U!vN(Od2IlLDrM;+bSj-?UL07y zm5cv%Y|A5Tuqm@?v(plIO#fTf!>t_sKD;K(S&>>>?GD7FwUJM0U3S0|`_tJ)UeDzt z2NI1dJjf)h{`m8a>yLq2W@@z>jdsrH_DGgb5Ixs);VeyQ@@KIzfHVh#4DG+n(tkr= z8rqv2te(k}rK*fZ!w==_PqZr%L&a2$08=HZ;-c}YN=AwZfu<4B`-WMep)zIC9<|r2 znv)p+m*-z3#XBM}@ZrNetWL<8qH&*vZh*ob5eE<HArIC;mg$F&?~(uEl~1lh{d2BE zX^*WMrr(`xn1KW|@^&^FAeoNV-x*`*=I*o1!z=H{#GS$KnJW$ZmDX*9r|O==HHkD0 z9gBvgvjbnjZa0zF)VW+yl0r6K=tODY|E)op5D?I`l9}n&Crz;Kx~0G<s4Py184T4r zd5Lrh!N0@F)@3*qfK>UUdO)=@F9f%0MY7wlurSanjmRqDhQ`AnF)h_nXOnkv7cK`t z*bzMWkyAhp=E_!b;%uyROtyg`fnRCv5;A!e&U7I0^|Bp&qS6y#>o4!rG}<6^P}Y>U zHXkF4U5tm}NU-zQ?YV%Ex9-)7M!L|3=8{&&r)OsFECh#pUOdB+m1F3{{NMyq^XTR7 zgOPD5=RDFKOcVfepuUU(0`5)g0+RoWjM5}f!bBFo8@b8ac)3and`RLMawl~UELq77 z5=-zFy~b`_AdIaRo$(6#JA3E-LQPT&J_iYPprAIZ{;uFq!zzYqA`O?+Prj}pS3yuc zj$v{@aSIuyA;{w3y7vLFQdIlS?U@u$Q&jF8p@2NK+gIK?*KTz1J(33)UVj(MN{W45 za?O{mPp4AIQEGm{pU<?Z%&=u>X)cd(JzRJEbt=KVuNPGq6IIZWIQ%heQJQ=k$r1{T z2MgVZj{zHb@e_35q|VsG3giFZyb1>!n1LUv{)<D>yyhi(dH*_wilJs(h#OXD#)BpB z*FQ&P|5&n6r-|+N@VXBgdeL*8Kkh%?5fXQBU;MSUS~k3pN$N>eO3Rpeg@^A;aAHXj z#-xE-a(!%++%-n0WM#E%Ig)14i+iEoo6g>N&WGyCk&oJ&?IS)*6NmqPGiYyHbBnFR zFzIRMkL)~9w|(}pBG>%F0P|IH9%NqocZudy4gE>N*lA2_8%Yosqc5AX6b>b=4VC|K zaQ9NJ*|B^^8OmauorK{lNG3ers93gm*3?is0EYm};Z<>!Ydz#^E=VRw@2<(h-9l%o zS^rap>U0C??_mp%6um?W&#*jL4txCe5rzyU$!1D;tW4mL7wy|A-)}`vg1NLzFti(a zV>Kvvh#R-<L8;2o;_p{2m#CfG1^#)~aWKaDX`LUnHIET?o8QNiCpwQ#BOcFnyIssh zDqY@KuaYeMCkM_#>ZqNhD~nfBBlpfUZl&RO#*75O&-Yb;;NX&RGgs>4t$MMDSV`QV z%xe%UYM=mH{NBFHOQsU}<rtfryTNU@Yc4ki=_ghVxxqgiisPe~WDsAmk`=~FiBV26 zw|HEiRef2oX`5o95j7vDjn0&EXgJAhY=*ZuyKzMZgl|}9Z<Y@mroJ2l_R1QXE7oIa z((c_Bs%JGi*Y#L-$a5OPk*<ZMBXkoP?$wxgi%9H@n7ItiT!Os5=pDbO*eScdO*=a% zayAHTC!+6$4*b-Ip^_*6A;De;NROewR00?=%9g2EjI3`S{0NRr4I=?g0~$RaCM;rr z5E1<tIsO0JdH~2{-CBCp1I;rZX0W0i)ASWKOa6v)LG^IWfhFDt1`5;P0|$n`VV#ZF z$FqBOK*){V=>HXG!1oBFKGUd@-PMu;D`%_Fd;jP#@3b{Ii&JyKy?SioC(YCU)s(hm zMIePjU+*H=+{(Zlw&omrMv!@Y{p!qDMlzrirqPB<wT`OjurZec3l|R--yw62m$-`# zbw~@Le3xgg;K(yk{XLP=a`cJv_Iv!@8@+3??5@98{Gw%5{W74F`4%|&hT*&xN@poh zBok#x!KScQ0&UIWi#8o5KU4n#ZS3wG&dSsRge5KEfNu0ki#-Y!yjujn;O*ObY^?bd z8K$w)`u)Vof!y8Ts*--aDwl@9X4OeH-}DxqaXJT2wVS>^3hUGDp`z;ezx5Kx0Y%r4 zVg!R8K3kMN;46<~#l{Y*G!@E+eq^onPc`a)n~pD!_iw@YA!$F0Wf$$QxN`{d;ZOZm z8!7h(KnBLIU;_;k1SrK#E2aro=vJk|6CjI$U;z|u+rxHQb}QXXwNG$@X9sB+a<=;j zb%{8eV<Jp#RS>HKAhOI$Z`H_^w#a^&sPkgUr2b#%L4yU4gyl*<0*d0SVh!*ta34C0 zY{bBp=uX6WRZeOIZznJS?)W$WZy#%gwz%K*s}N-EDIOOQzm23I1IGjr3eHaF<Qo(* z$Ha?LuuAC75#=HiSpu233-p~he9nlrFFKmexae*X>}x*1mkYUiLHnG^hN+0XlwKD~ zSJBhQXvdKyADR_EQirneUV+P`{qM0R9ib{+lIa^#{KPF8T>b@?h%_{C+}OyNm`Tt| z>fN(yroe|)BqfY~!~z5BY22JS+VCx=tgd6Dc?jUDMDfS#k!%has;VQ@4!IK$<i1-= zPDK~qhq7$R&eBnt(o=Gwa|G@9hozi#`E|Pr9q{I9F;~tSHVxfeW`{UB&`<9R1+>*u zDPfc;Rcq`$P0Yor>sY8WfSNyWj%tKxiT<BNxHObHJk~k%cpT^v$u3}RzW+2fNdASD zv|vGv+SUf-gQL#cn^aUe6ldbzmd|yC`>iOOx(4NKNB7uCDI2ucnUe_rn`um17V0KH zl;XFcG_^g2oGA7i=FsR5;^kLr!}$TbpR?~bd|kg8KLY&d!p=I}<9yYc2*OvWl!?<C zj=`!)d6&ZANIO^L2POWuivU2Nii6GfNwkfL3jzg;dzEWc?JEB!xb77e*;X=ku^N4b zDhJQ5$j?h{>zMv&{xQK`G{pU!mgYt4jWkPy(t{gl>`=dxTz`I@@^cDf<sP3FGrew9 zu^*v41bzHRz?1Ff8Z1rPgzwuCPDtI;N!jzTEB<(<p5~|;(n4kqaE)`Q;(olaJQ7xg z8zk9n1ioba-y9>0>peX6O@ZMxO;Yp}EP=hwr}J1W+}NP_-oKcwg7<*=3nNB>SA;u` z8h}y6!q6F#E+xeaR9vNswOycL0Ys)_fpS}<%IHE+-tg(&&R(eg)X2DwO0?#9+3r61 zFES_qPUO%J1|g?jy*IHw_wm*Ps)t?krK8XDm7BF){m+IBT5N0<!AtU*SSxvHIr~cE zBF}WSVE1a0^g$6IK%)2P&Z}#`w@O($GMfa54JgKA*U~sZeG%r41J`3)mEpA`kd4eW z1&!k0?=_S9BJue+agz&vfqA9Jc;>@%6;e^Z5*NK_&9ktGABB=^O99)RxT7!#s<Gb` zNYZbt=52Hh!8OLTvB;3`O80~BazF*O&&}L5jWc;Je-o>%%g?eeyTfWdtubc3Q92_( zb`tHh%=-g`RX0-%o%Q=<3mi$(o@Z*5R%>tXs6peRM6$4`!1<GnCfj8YG4*53w#MDf zEj9yh`VKiFWREO>Y3kcFf5^tjM8-2Zs1C*|o=?S$DRqQ;#`ettBD{cEFEg8^*-Uad z#pK>xPsfjHRhAtuoA%{8XCt$QaV;m`(s!U8k+)W)h+;jeY+<7a;>AmFu{iE7<yb21 zwGejbbahf`d>>{siun}{8<?-1H9gEBH0<7;&p@m2dhYkRnU_Z$Fp>NFpE{#1aTpwK zB)pe_W~wancs$d(N%vFNl~SEbEvMJ(eT5XUm)qMT;Z^6GqLPxKmgh9zRcCHek}(TT zZ72^fp_4>(`?b`(8TFV|q{(H7g?onpi&=HA?jScd9x`Kv<}QC0>z};5ykGD8)Mbjf zvI+_U0iW-ndo;Kl$cCB3_KJy#DG5Aa1ZPbhvZBEcAfWXZ7Z+DoSF_BKsK7zEVu6n& z#-t^@1uN;=$AWEBRA%9|gvP5WCV!gQ{+gel40Z050neHO!^vU(p6RM1p~#)(lCyPj zQ^&x!CtjOVrb#=4WRVV0#5NC~+p*U>SvY;A9gOJR!r@8JQ1We9<Q&X^X%abK1U9n* z49r8>$o$mJ)-YJM%`(>a8CAkuh3D>Ou>^YT8#c--_h0lVWEN96V99vC73JUE6bYpR zR^MvVWC#f{kMVVsyF5;bzupi12l|$LZc+v2xf&YEd5l-?*NQbxcko{I{^0}6l?Qju zL|YW(z3c$e*FM`X--i$s!O@9{^{%%N5bZllUNYTy>&ISPSXxZ1<Oe_zfY}`ai_+Z4 z?&<E%$;H*e&Q(%e9QuuDsJP_Nx6~L-bP~<r$4(ds86EKT6ab5&UuOamoI^hoB}2=E z1UNb}Z_uT|yN;KZQbhtq)<Lx`&8jdV8WSHMKB!plE`#8;tEUzaJN@aY=nD}MF;Kg3 zTDTlgY2C7pifUpLI;hsNjsy!aad3No?{?3`T`bPa$0wrZOn;+l7N@U_#xDV{igPOm z%oG9)rUM}?f2$&cFK0b|vSJ}0DSeagDFs$(9+m3%VBkmevrzzDMI$RGka+wp6Q;e9 zCkE%GOo&E+??3w$+LO=vgGdfTfnn2ZXXg%vt~I&U@CsG5&S>C>ch!tjIhxg|L8hRR z43VxH&9iq{0dJO-MwmV+07{x|LFyyKay516R94&K5Z1H1&)q!>zMfw00f=%)8Np$v z7zR{y>;m4oOdfLCule2|qsp&-y_{C*wKy=+j0T+78E$!Q^?>G_2`94#s;ghd#0%(a zeSCW0;^f@l-#@)?hejAj^k#%GQrt!$=!+clbLvCQBN&;0d^0C?7<|SI#&RB}h0v_R zw1xWhws9m+g&rOGI5~+C!I&^|S0?l!W8!QSV)%KR0kZxv@IEO4rPe{v_qy`(V>!cD zG^=$`!BQ%F3P$(GL7J8U2<^R{IP=c$vJcQC?%fi;yKFlmiu?qggIY(f#h>2ZkAzU7 z?#EK7w*tQWN>t&-_OW}Ohd_wvAmUwA@9k_5=V?jLaR;mKvggZR90Xfl28sB>P4ewL zH@(W#7r3EQF=9wqq)>aYTJQYgJr!F-1MK28S^F3J@#xQQ^!3!hcnNokO_K?Rj>jEt z9&--~>@ftWZ7}?BpnJ5kG)w5aNMCg%1YmJSyV~h$>U{G5@s1{o7}@+Vl_{rUHaC?B z*1;m|aA^#lYMpWvB6V%qOMR>xm^3uZ%t}IljPSGk%4U2n{Jd3Y6t&*^v{CY@I@t5v zKiHp!V3|(@2JbB{E?sW4?S-Ihjvx}X&=4OU{I5iC@UUzcVl`^SB8o?zhM)2v$7#pF z%q-+~z1?N_z6%=UlLAs|nm!>zAEbCtK@H&acWLosXH2N`?eb5T&(qpP$k^D}w9wN= zRn4c`&(}RqERxJ4oqWOp72J!<%gr{|BP6a3ut&^j3W^A;ZiQ^lpc7EV9<eW-K_~bz z3}0Xe)CDAU_5~V!W+tZYr%fMFCz$_T_EvsXAk*iCW58P<ioestq87+mi&zDejDK4( zO$g%kKjPcc>G2nMmGSfYxILKA)6?ri1Ia)HL9}%XOUs^*$93R0GLSkXqW0HymEp%} zI-_3V8i?F{cXvk|@a}BpJwoW2zg&<bB4T0{6qQZF5`h}o7OLFe%(A#*@7bgSYgzq+ zGh*FJeDRtPT2@ahefu~bHNnCO9|cVm17EkOnsj;HPyU+6TwIY`M+}~S<g|f-z6X=L z#k76~T`&O_jugH;x#5KBqN3TK`6%Ns5k1|zTPU=hJHFuS{uQ2p2b~J*hJ;icvo<SH zv-AcM*1R+0I6i*1ba(7QpwnGw$R_PTi?06e{iEylootv!R=>Zkl#DcoXivA^twOQP zmHQm+EM@a3!|;=j`TE7{1;n{m_SEuB9NM8>_}FK#D^{I(4S~c9Ok6kG{Mu-k4yF{% zyc;D)5z{y2%zh+ZK~Y#Z%Jaec_G1R@CmI)lY+EQKcoe=p)J!DW6Mhn^8^>&XB5`nk z!M_5AXfTpc5>w!6!EE16{xDoK%o>a{nYtvX3vbV*?HZ#)b#{t`C>1e+NXY-)l=uzj z9hFcVwFO8)#t}(Csvb(+PVkz!aDz<19hgXiIX6-?JS+)d1fknWNyT3ifoN&7sEiU0 za1agQV5K41mA5Gmc*s8cV;q7I+L}X0SIPC&l+aObh#$|G?a$&_^`qK;EwLJ?cWBrm zxbu|HeZS&3epxnFm<lQ>PlJj_m}za==dC1s(lg%TT`T?~GnM;#=@`0=KXeZNN@Q!X zX6cz1c1HL@58G%)WBPt{mm0bfPSeQUPW|eXOUf3~Y#n`52m$WCsjKRGe`WL(&Y!UE zq>l&da4US97I>RXE}4L_gREr@8S|>md_I;dM(+9Xf&dR+Sy?5(V%-0de=_po>(GJ# zF&x}4TGXek(k)yxyJDwVHqn${9ed7+*pD-f%#MqLO|um#YSOb+1R^eFfMB$hLs&5q zg-*wu$FQnLLBc3lQl#HODj+Qb{}*!xSYcn-DB?WP<@-e~L+GM`QgLN*;NNXzF*De7 zni8;<MheuQ9JfQ^8dfDHmV2uWmLOenG*a9Qt;8-UbbUR||N3-uQxPjd=lV|>paE6S z*shn3jlaO1<)ocKQMpQKwaZ;%s^L7W5p&DruYlf2Ws0en)+DP{Pif?wdLL8=hV}u4 zJ9Ub+Q7B)9%PhY8>c|k0RBVU)kYu`q*1|hW!y_q0IiatXo4`0YD=pM)U4O#$&6*2l zTv#%&?k7a46m-08KpwGOK2zo}4!wTpM`>LIaLcj0^}kh{rhlO4HSM1<EP5cm>qk}1 zrg}g#a{n((`iz99tcdUv1ZI9uqpu)6p-<;gsEBZwCS7+OuOWKouK~Mefwsr6|GyW& zhW}gHd#4>pOHttYIw!|W#P77d{`E*W`8*=W*w2!<ZxIbv>;$UJZgl(kSX);t5ch)h z^*O;qyYU6w#2EIXZy+9qB|<)D55nz)6QHo8@RrPcAzH-yg-8~mT1bmP)Lj4>kda64 zg)j0oqm4jW4=~f8ENN&#uQUs4|ACfa=R+ZJ|G6)J=i2i958)jcEV7C7n%WA=@CU7m z=d)42Pzil)Z1Q5W^73L`D!0>2%eOo0)Ts(;DKIOq$RB$?wEo<Jqly13v&N@6uQ-ch z7l`JhER`8<R+_nf4m)7yq~_iiIMLD4t)_mJ^?^3%89%4HdU*Q&VD4&iwr$l&E1CB| zo7FPC^`zO`i+BP-&aw+0!+++XYsk4PqV8BG0#HGo`DO)h*!V%4xg&=1!8{q%YxzN$ zv442jUpXT1w5g}8%-xTJNOiA_KEzz;>EY($|M%?S{_(MwSGu>Xi=f!a*xbQ@h(gG* z^Yf=>89f7yu+4TB4h?`b*5)qa7q=$3a3hiLwTRPyWMz@+i0{6Mqar-6PEN}84-gZD zKr}LD7GI|ZPz1;~FBon!9ti&rvdkv@!$fJDV_6@PEeZpOxMSQ0nB$-6!FTMuS+)EH zJqDE(dVKY`?iittr&M5tcTOZyw_~31z1ea)k2>#%38*U+h-s2Q)~Hm+><nryJr1Qo z!?AmAqN4pz2)9F0OCf~Gd^O);%Oe!u2uEGghK1b`UTb~b%z<#ogpZMpC}|$c@npmm z)nHA%6H0tC2+o<$*sj^KeaO=}1`lK5U|<j|0+y4AlldYI8%R|`qHmDZQQ#Y2FH+Im zsGs-6%+_~y<%8lABSpf}<5*`LX!5Ov?SzGazshjL8Yd0TzTo}c_k6p*E_5QjW~J76 z@2z9A<L=M2s4B(D?RP{OmCV)<Ad_z6s2O{SrJ2d**8D#;wFYjX9uhuk*byx^cTbRn z%3=KxCOmw<xEK^7mo4yHFPfFeVz%ABOo9h7t)Y6S`dR5hb^|Llw6wU`*?Wx;rPn}# z9fg&-*w;ynAc6_J6m~q~5`2uv1IGV(Ct`&wk|q%;xR@02KEl}wN<KkU3C9PS6lPFU z6^A(tzYd6&Ze+xV@tMu#2y-A-Y803smX?;5Y{S~9l;k2K19wc!i!V)Um)MVvuep=( zJGPcLM86jel@OIYj!Y(j1h@<sj8e%`j%AzTB|dUICh7%ZoC^K=x1tNDZ6wxT7<2un zfSZ*LeXSK^icYQSN5Z0|Er5l14ham!EQHN|mP%~VlFas}kYen3s0Y|}Z?fAr#e!o( zx1<o7;89@_BN>0ZY8eTvl>hEgU3#2<&{!JkxikSfJzZQ7k5TS8{D_zNM=|~8Yqq+r z9y_kgUFibjw1Xbs*Ky#BK*6I});fU@E-YE!C)}Rt+kgn|uFN0yXjERYvq!D?^&a4V zJLTx7SHt!G6e46$Yt*OJeQPeB>=s?(N^7CaiDs?~$PWUQ>*K2zZALU^B;E>9r(kYI z6Uz;1IpF6aQ%DmL_NRqjXA?;Lp<-feN-3O!dp9J(=xr~)e^jz4ZVw*Er6ehME0_iK z*TJvHb6s(7#=3@!KITbrri98&O(UF8O&xKnm*SDfej{<}=+f*^tN8h{q;Pz$9jhDW z>#Zx1QkY<h2@}sR=_VzSFRq}H)qRd-M>q)6FH?}M36`;L>e_1b<~MwHehel<mCN6G z$0C)nx^pY|vN_g}%SlE(xPWcvq#$F#zA%)_^wgWrH;J5RH&>T*2^QQk?Q<#8=btkQ zC-F!gh~!}GDkL?FYJg7Q(taV9;ueT23mB@4DNIhD<bK6x!wuOf%*sM#=9eV)Jt}6E zcG=T>oj-F|-dXRqU*8FVHMToE)N7{0kS#AZQ6IdDbQ`r#1p*93ar%P$XlV8$a2lNc zz)^&5hXUaWLnK%bk}*V#h{%8uXrhe}Fu!)$O_L42uWu&oHIsFa<q7|ejUvJNC;N@T z(_TBXT1%>E-<gIqwNhpl@x1gp5B%2!f%hPRW^zuk4z%;%BF1jbo@)<W!k4b1|9%D* zWaFmc5It~Ve?c6YNlIdy302ASf`e`tsl#NL>GM%fCQ&Sd4UCb3GtMWal^n>_ns74e za`m@`H-hT5%JdK6e`AV{V!_AY3rx6sZjJiw9$p7e2{4AgVY%wL``bbBCsGB>aFE0? zCxw~6Vxbsd)IriHW*h_~0PZ2>oI|pD%SCHN0VYD?jM_We)5$V*v1cGOcbrqhX>olK zQjnXim6~~!?)O6LAs4NJS1A-BVPQG1R~S6QuTF|}6dYSltNU`&8$V7<#w8V8fqBkI zFUyKSV0GMI7C_`8I_Y<S*wD@3I<edDrR_(cIC>ZWhnM`>?P;tiqmT2`5nB>WcdT@) z*CK!}d=VuCC|TGyyucPG-cJ!1Dh|dwqy_yaltF-&9kfAUA%|LG9t}1-AM&&8IA`(@ zq)jk+jX9YfFO1FBOf5gGR+t@o3mt{C?G~<@6mw@5i(<zks~<x^tpaxbO0If{5&S*X zW3qW!9Gr8#lvctWcbKboWUNJde_yZ7F;RVh@S8g=I4COjK;q;s0S;*(_5tYCr)3sR z5pX0IbO(2oQUDuNq)nLZNu1(#QvJ}*`t7GuegYiW|0`wCCRfh04q{Csh(T^I0_TgV zV^Y4q1CemgNBc?t>1n0?wdABBMd~Pey;aIg_si1MgWAOQd)_01T}Yj!%GUH%&~tj| zG!IdgGeX(n;CNYPX6&~<-wt)2<)3AS3g^q=`?^Uhp+98BCkW5d1l8FF4t+I;t<3K% zpb~EkWRvhv?0~<4rf|mhhFr;z|4WCG04Ts9cw}BXn1B1e7!Azcb<Gs!tn0z|!LXus z8tXVKY!dMQ@DA@n3dg8lp85YV^_F2xzF{BmHZ~YNI;0t0qZ>x2lr%^;(nv{+9^H*} zBc-T-#OM&D6p@mUQc+TA-~FHWdEVpwv=7|J;=V7g>pah26@QDyA-!T~sX&YBatT-A zgJvgZYU@VJ3rcjzt0xQvIc%;joGsirX9%%q&6C)lrEl;^b^)}DL(0ofby8jmj2I|a z_!ttEM6$PhxDZzq>?(2oxTcV<EXJzxz{5^CVJTnzYQ5O#R8*2zwDy};MVAJE;D<ev zEZJWZI0O!z4hC;8HcTIw4tiHLtvS6?|1{@y<{OYhp-nNU_GL2o1)q?fXc{gny@H>o zu}h#c-P3H52yAee-=96y)=YPUIt_xQQlM+9^IOUb8@U|m6|*(^Ji5mx@aObCi3Mln zznO^&g-T-?0hX|ecLhSz-OkSLBeR8c#602KOW4m{Fly2r>oppahR_f_+$xS`L~>r* zo8--O<;@Hfn7X9mvTiFFO7K#goMwanj~o%n4&WJ_+CF#c`#ySi(XymeNL#4$Ai07S z_sF1BCc<4UU4-Bbcwhai{cE~04qIcmP+)1~nBvDY3c~&LS~_%mh@$88r-JpSDN7F1 zH0x>@!adJUyNH_c$wTQi1>bsUM|Y?(O#6ff_ac8(O+0k+Y%XrPVToxN$1?Xc=7z_M z?DI7ZohxCfrm&eCD|KwUyFniDV1=8Fc-es<UFz6D%E<o_iyY8M!{WoT_stt}Ury~K z65qd{0qzQ)NO439>56;v&)Srh)2lq8VDmgk@i-MKcyNa6X&d<VcMe3=1K;p8i2%O? zFxn9CnT|R#d%o@=rT>PaqZ`4SIwRuZxBAZ&7dJ9KyOLxo8z?1IdYl?bn(f!CZFS8{ zfAZv(E(wy)o#_5Ra}_<z=2u1j^&kl|$t~?=!8hAa5i=L!DWnYDX;IS*{a+yj(#3$! z7a3rpC*rm>xfF?pk_W1zyZJ{zmbJGlqgRE5LrRvx$<T?JQAF3a_Z`OX)cwPkhxY1) z6*<aKT*$zhagCMlD;dvSxwp)sqe~j?5IUC^l?>j$HiBvz$G1Q_x&~SF#msokm4aqw z`vuPCd#RX!?+>M4Wp?^CD%&2u{k6Ap&)c;MDh4HK5sr?sBuf~ll)%&8ZwG)`;(T5D z0yjp<|Mnf9jUIpQ^?Yo!C7p)cbc<DOVO5PsNCeb$2d{RFF})*PRMI7&s1>P*DgD}J zI%O~85b!Xv$FPK(Bct43*|_1udV^*oL(@%S^V7Fq*AoRZH9HNCs~;ffQ)FCYThDPV zD>Im%P*lPF6Ko6%ZHiqs{Hm^eAH5ykjm;E@UP^#78opBRekrx{GG2O_^Es@2+>q6s z<AY<x1!4D_fvcOZq;8oG4+Z?(SQ{!*>nRU)S6)B){+gC>8@if79UnN8v90uLOdYiy zIxs6~s~eq>20Y<_!Z|@eW%SN3@(ZK(>Ex=;A<DaVt}A+P{ybfM-fqDBPgMC(>h%KA zSr5cQRYPq0!+1lEUr3)!@UX#M^Bl`=gbF3aKWZo|<#{yF!(Y;apf&Ts*)Z5%L1Vmi zsD)35W*0sXT-VY%{C(?|7Q7wqtZ7NQ&_tOhtz{53qa!iBPfhgdSM=j}TNnQ3?hj@m z<Az0F1t?lz<DoR0&T?FiX}Sv>Pgb7h%1G+Msu-X9%RHX{5L?4M3$=4_W~4p6a<*&< zlhXn@#qz5Z5j%y-mRV)zrm-LdL0-=zcwdLDjpxW8-`o*MJ1&ezx>Gzk4$=9Ig?b+c zacQS{ah%kJL!wc-nUfYhGLp{jN*?ybJWQ$MUF$Z@>y5@d;}5=Z{Q!yZLoKnjj@(|# zM9kZP4_~m+J3Y@XyOcZdMn3hz7?IXdgMMe<e?Bd~<*T@q;pv?x+4O(*grvZT&J<5m z<j~jOr87mw?|M1m^O~q{L*tFzqqmF0qm`V}D@;CgJt(#25vW04L3Rn{Gv0&1Zamy| z`yiB4PI}r?Jd_&shF|gTsDdBQ%=VZG!ok}QEzx9v>p)PguIsetVGVJo#Gm~sBI)%% zm0B_YopS&eQ^eljBZ8HRBzROg-b3XkteTAlB)SJ6<5#K4j>*C=Jg_z_CE4vD{l0R; z;QcluwimIt076ZlfI^a#s=0NAy|`&0Aee({(>DA$A=H52J?P2UBDIg->s}o9%CBFD zUAUPnY0q9`t&SQp9E_zrZSr2z8)ts18{2oOB(&q)7zkPHn8+@7uzsA~-TdY#&}v}j zaFCbi*0EI_<$x3d&=X5JP|CrYfW9NWW}G$;^sOC|Yl&0bA|TISPJX>0@KHo@RH5d1 zmf6xTLI{rx2Dz$DqH{!-5RF#cYDPbG#D&XP^&1D35KsJPY{4w74DGVg(F<<qIN$pq z9HTSoR6v*ZGqZndnXm+j#mv><uI~LO^{*SpHzc946djfEC`sQ!aEbH$Zar=OxB9V4 z?`4K;SV9o6=<BB+Pu8}Mq<_6$A}p0f5;_X=jHoLqYVe2==rr<16v^z9TZ-#&=|+On zPki=L<9t=AWW7Gbraj;9`RQdko;gm#=j?BwL<HhY6Oq)iE%ml6Es&9|Crn2=#;kU; zv);OKTvfj6y};S<iwTyPkV*{6kI;3i)P(ejgc7t&)~4w!=gx>o{BhVaXsCm;5~U&6 z{+G*!rNThLoxE2)kfO^U?ZmIoWzsv(OOM)>b@3iE=GuN_PyMhRj^qlIB6M;}5Mz;a zHq<jp(5g2av#u6lEf#c$AK6fj81;(z9ai2lxEL_B-d_+<O4_OT(MC0X8>xXd52B$U z&r=>%fK9~bbLBOPm~v-pv-Z@`?vC1xP&tDa9%7|&G(8dbt<jZ_yzVqgo~b&Y5^9sp zKjI;!1CQ9_%K3)cTPh_B55Gw1exsbrn$h;HGA&7QP!Zl`8FC{W^;I_JruWi2*~d*x z1GU8u&WbV;YiYC3UV5}*WSK`HB`(}=Q0(B2eTK=%nJ;#ku$7-G4Ej0paRG1Av_|(N zK>;SpQdK5>r2|nD0d*|{m8(nM4~Fj9>OXT?1`Wm$|LGqte4w492nDJpvS9RB($g`G zwB3^lQ$N2wNzrR|8nv)zu>J2nK>oBPThHY`ypi8%9qxVq{jW5b`32l=VN7*%f&dK+ zodAaT-Pg|<y-2{)5=8QO9yQZGBpCe=p79H9sk#%?stwh!+dcO;XOao+r{PSh)Fvom z)e|1|)LGoF6t_<W4zGfQp_`<yIDgie@O^<lZ-TEAJs6U5v!S{<dd-fxoH(-1gGJV4 zhkWW*8vHb2_4gW2|9$T|$SGkhvkL)FCLZq8V_2r6MbPnba!4AarW?6z!tqx+<4j#9 z3%i(r*gK?Z#`^aEIF6EJn5E}|)wod)_+Ad;mV_M%^!-`7S1MUdvr1e|nPk%}>6(XX z&?l#i4@-gHG9@W9`#rYkg_EOgOPLaKl|X}l3tqKkn>0rR!J~(;hnlRDKDR8$_pdt* zJyG(r$*WQ~q55BWK5T3=|2&s{cI(aey94i_R7#fe%Dx>h^`mgk2;l)??=mUe5HO1H z5Y@}6H*uL$jBm^or=Ca)J$V#tM9xxS*sj*7eml7S!&f{AYqcTWgIgMM$0+CrKEAwV z6IG>w-fCAsekGjb*n}VA$|D(og8Vqdq#zHq1TCvf2TT|&P&KhSD{31DnEc5w>?gt+ zY(l*8g<DGRROKS34ey2sVp~4#N@45jcU)le4;3aIrsB&@+Zrzh+Qel?xiP8ZGNE%V zC~7tHC(}B43x+t=*FoCeNf}`pb(E?D93oQLSW3qK+))-i80x!=T0R7;z+c~e`mCF^ zjZ{?mN9H?E%<kzGZfO-)U4o4wseYlLswVV0_4^iGnNs!0HxY|^5qFcxFb0l^17m+Q zWD}hulkK3J3h<m5WFA7k7=L#i()Ro>Npk1h`YiFAo9BJUWG#(`xl!#Z$Ipax2_Wcu z+Sh8|@>j&vTdPgfx$73R+3#r|ib!kvIxO$(^VnG33J7VB|5ErV$#i~chidw|^`=`+ zKB)fvk8vZiOQvN2lYK67Qi)iX{wFxkQ4Zz&^e0YSk{#_!ksrpibRP+Dn}Ft2F>#2< zCM|6dCQxS`vW^Ef?m5U6(gJeL@e3MED_Cc}zkUI_WG$?m<&+V$0gbvjvqT<{CQ~v5 zD>aI^In)0EC*S)Ma*Xd`y1xLSSgHdd`u~c|Jz7xa&n+Qy9$!W7h%GbgTT*_S8x9#- z7!&T_f12uqNkwxk-bp_y+!C;ef{-}`m>7F92Oo)^k52gufk)GnIi_o>;F(_&KbrZe zT>S0pGApY3$S;-keuS+iNs-~(eeWgT29k)976uQVv*ejz{b?M1y=4}r8c9I~)Yjv9 zbi$QyOUaZ%Nv1roMTYFe3$W*g%29|byJu+{Q9hv=4mk|B0K?-9o<#YR{lCgvOgIui zfwc@sD)mw5ya;((<7QeDz9Y|hAHWDSU3<ixvlHb%qYqZC<Kkk+pvSm=4?~d?N7H1P zrfaTD<s+paP?a@~l@*AF;SpN6mPMlE=<mS4yZgJFdP<pe93btx^$&j53<X(~Y5&W+ z_@5PqWMD-??HR<?hp2W3@tD_oL(gu04`gIV-K>7Se;aipSw36f`&zRC6dV}1=Ko#* zs}k+mEB1Wjz21YJE^1P390uP1c{{NFF4(7rJXK5P>C>m!0o?D0lIvjKpkHg!uSBrJ z(fe;)itb)s&n*JU|L+0)zaQ?vijdDUOjD0da&-8VnJW<nB_bh_5EGNT`T2^^^aaWs zcZiPm*Z1GQ6$AEC-|zAY2+T}Q($UgN1^=@4_ZLwvp4qFas!~ZL*?RNh1<~~tI^HVR zcQTs;S0()U4xRj(OX<t6zkmPyX^nLu!45>n5V}}F(fCedTbqxkr;d(JtfEr_HnRiu zuC|tgg98#HB1Z7?$M^5NLPCj(YpytE-rnBM&K%y)!ot!SW%q&{pGAy(OA~FiD<or% zI5({Do0b17CMk(S*Y=rEtkY7UNvPm`fq+$lZ3vTG2#m1}YwCzXU0Sr5)*5};*hp7M z61>Jjq<y!i?>Tc0u^|ln{QN|(G~qJ;+av!sjRi+bY1m;2{r}}+kVah&&zocy0&dkj z`&ntcg%1mE$#BgkhUsLo>{J(u?Ek=5G{^wESX^)Atq*hEM;5A(OYO~=2k-H|-u((5 zwCcZ_@o@-SUtcGdbXhKZ`6ICBchtX|!Yp<`17QR7U<x~fuQ0IgygQx`pIAqLLh8@M z<QrHvA&s%RKL_rA3;<ZZS!SlL2d#R+R@u1c?mq<g#|DLBty@GzMV022eC~G??{Snz z|6P^;18Vq7G5p0s!wJL>`cKk)D%2Jh7Y8fUdk66GWPCB&VCt6s<9MNp_t8H<rInSe zmnvL;e!csBkHt0rrC@>7_293Ngo6&2%-_9dAJ{YGK=8rNdHew^9mH$(4KdH{-R~$3 zAr9<e907pt5D@dH-Bv1$$iF3{oyS8I!mmR_-mda(zh-A-+0NuNjj6$bn?6euJUWMN zwLX5Q&J`W{iBPEb7z>vYQcP(uU0q<;C)TS4OT7kbmn%jw@tvDzkN3!q&}eCC+51at zY9c0Ine$grSL-N;yU+nU>*n2h&lhz5o!2W?KF}=x=h@ar?ST1jp<nHnkC$~`{P-Np z&}#9@<!I%#?bVbS;_Uu&)ScOcZ#t7a0gydX@gzyVBJsbssMjyYGA7D)dEur(NLqPj z5fii(79L|Ug?z+NIJDaSyZ#LGQo?$Wy@SS514%~+n@yXQ{8>)JZjRovaz8tr<k~tb znhoFU7-xz#;!s6b<hs#ho?*4zo+G6S9T+8d!>n>FSd3jc%w?f_MQxE6%`ofgm<sIB z3vi<rflZ)@w>{TFw+-Sb6$<NCGXE#VTJ3u#*(H___wKt`!b;#`_1MFPSP?f6_3}Zf z3@SXB4^QxAJ3rx}CzjrU{!5t>rvx}%73KkxtID#?2$$;#-$@ZAXlwbLPfsOC%IhHp zp&9nzY8ZpjgUU%`UMlq)2qATaMZJ-d`zAehGi%&B3|V*NO&WJCM93lw=h@NtD}8U? zP&2Z0HK93&6FG&1NZAF#4vkNXyKtr_$p93V0CBZ&bw{Y~ru5E1HAQI$>5}~kJ}Q3( zO_Et&Zfj&DGD@zuR(=wVlyOWa6%`DcW>5GE9J-20ZN1O6`DiDmdmZcTo;xm^O49Pl z<NEp<(dI9c81wasZi~&cqu1Dfif3~lW3;V;f<9mYb=9yiTzV5hSNjW^Sg4UoakN=X zR`}*vplL0+Xv~KKF#?uk$~tV0{DZHT*Nz$TWyQq=+&FM2Df4!_5n)egg<&QQ?WW?t zEB#Kg5!0@S>%)5CecXQ<q=N_`vlLPfA&q=C2y7&j!Dp(Rt%x9HRv;o?z)D0x6le>+ z&=?mmCVCl*#`mAs3Tc^0`G1?xkGg(Tk=!O!1~TbYn1eu8paACS3D&#ecr&V4@sI2$ z8%q2M_F;z$wOGgvy_p|UxrZ?z3>8NU=d-eS-LL<SOq}HI;b=;L@NA-9!zT<+9uKZT zkX~zDWID#MT8EmU5l9)72n~Q%3~S>ZW<Cmi$l`&^I&wpj8M`&F!+Mq2Dn(7diJ!zr z%|6F9FCA;I%;=UIb-Tm7%pl7dt=eoK%=fW|m5MY?Jo<q=Gngv-c=NAY42GSFQOXw( z0T8F(QoOKAqKS_b98XP2HTa4zGQ>s2K}R9R@+RUYh~L-lm_hX9%eg8ZCgneB*5P{A zOZ95Ubpo$~tgPra7}FRmC+h^4E>+KbwyCK}d2^m)e3jr~;-_Xa{l1qAc4$soioSV^ zxNE!T(4(068iQH)ZbL@voFFr&xKCbgO&JB3U`G7r4(9M<V1Ktc1%uG_i>n=Hjfi<< zr6VW(<L`jW>xy?V0-LhpW#lkW1rEPWuldSnMAf$godbaR1R<l7dK_H@;rZOxvG;y~ zmldQECU(t)oUKabhca_A4floB7H&q%FQSuoM)5C*p6W24pERLr?ZaQS3o_7iaC-lo zN9Jh+0*Gu603jzQ_+vNFw_=8Lui~I6af{yj!lG0<fCx_b%kiH~jtPnTsj`4N0+_SX zM`4?(oa@-KibcH@U`1&>9aQE|2!7SGO-Gpx&6q|KCN+a}5tV9@gZRSpfvY{e-hY?; z5#Jw`56S+^YYV6Hu#{TlF_#P^3tYmM0n9M!PfKN?f7kyWM`DrKsCe(ye>YM8NLNGV z93!vZ+sfS@55N5yDf^AK(8>-x&On2|5xW1}z-PF`cN25}AB8AG@%}R6?;WAbGg_Md zixwZ{b6_+%7!<tbH(n<H&h_{cDRs7#ODbT628a4!#Ki1z{FRvZeDM4d?duLfsHBa* zb%><zmU{yZBV!!+4$o@6q6;ldw)mH>!0BEUT!tm+U7ulhuK%$9LX{gbX3OxN<84ip z4vP*-vk<QY5WqgovXyiUTXwh{`;kPgYShjhOG?YcA((w&sCa*p@gKmf(x1rdSdr^e z-lcY=S0<N4l5oAf6X(vCV246o;US0PlXtQi(9jB-@Dnv%LUNik3qEOuo`YwFKXBe| z#@}H^M@J6~jfC5Op=PC}aJ=WmP%`Gdn-eTvPQ=%EjL_8Ip!{;-oj9%`_V~?5lVs<) zMzLIB;-+;m5L6T*nNnRFJW!bd!+lHJUm-yB7hNAB#`a^bt1|i|bV$a`4Qs@+BtuKM z=r)a869bX26=6YCVR=CTb58jSN+`P+UjT79FqSHpJn54VJ=9a!+y*}gAcfpO{L9gX zejyX<h-I`>b@odk6j84c?IA#cB8c)v!G!vLDSTqIbI5ZR?S)6FxaJw^<2lM~NQ5>^ zbV|}@3#md34l8zs+(WD|T1{q=$jL{gLXi3^`8olYB#rQ`vE|P<w6Abjswi=XWj9jn zKEd%`qHspXVhE{CVn<UdaRQ|I|Dx6sf&k%Pga-;Ni3zjjjG{OCjV}$>YNo1W4giI) z^i~LH^Qk%>MyO1COC=VV#!}<Ug33)1(_u(s0%;cHYPw;4z^(Y<_Ph_m>Eq-JyUw8l znp~I0$$avl>X7e^^~YkgB4UDTM*QLLIttn1qg@dQHf?*X7~Ts(!Q<;zL`Duopuo>U z5Z<~BApxvJ4D-O_Mu<?RI)IW1=jF#$!{_sPYA7sk<^^+_-~c6)P|QbpxUV``CFVHM z^_Yjj{8GI>dyWt{b7+%CkSA-N9pFSe_?k;K*9f^er&`mc3BNQZU|FwvjA$Wn5oYs7 zFrHHofsBkQ=~$}*Q@#*VYuAWz=<~GavV=$}z?90585eV4+E-g=|E_<usr*QFE^3Q7 z@?6fTUObHY`|hh_WJX)A*ntk~;No0d;hQQG13Lchqvejf|Co}MrMUE0@4nvsNtac) zDEWHJDJ{L5ts{s(LAog6)4vx`e#5`#rMcgK^J}=1i0b}V@Bq$ZDQH%4aj~%3urbb$ zzXmwN&j<y9?T9m+@U3Abqe5y>$ZD+_rPb&5l{76@8#p4A|4(&+V>its@HsUae3CTE zF0~muHdMtqM$*ZCetoa_%oWJ|`RQ)dAj)OLPPkahMf1scp_Cn05RqSDj>N-~`*$U8 z4&S){8}$laz4Li@l<*~qm0?_1RCMYk<PHDi%R-IV?EA0t59^ZoV<(EUKBOp4mHZXF z{}B4O?5jLYzL4z)1b%>amXEA@z^bvGJ}FT`YWe(-2*Yp%5nx-u2@sz^`s4}>MZJ12 zo}7TXOh;06;sfsXO1AN1MdY>wZZkV*71R|t#)>l2_mx%Gw?fWk8Yr437z~}4n&+K6 zPl{a2D%b7Q_!gauObN1oW~I0^v1)+=RC40S6M*Sh+MUBrlUZ5H<ONS?bGZO16|0$Z zMc$aYE3Z)T7#epK6C;{D-oR{=j;)(gIur$8aAREU^76|kzXdX^&DrNr;=g<V>tpwr zmSTxvcjhNjuRus@0jUX&;BcNf&-VNxq4>z;x+L^+o!Fyr{s$6IFo;@wHi9iWjJnqx zc*xPt9(wvXQ)-VtoQPA!b;Ll}%GJI{San8|;L6DViQl!OLi2zA+fDO#jvKXgbs~$G z?}Y|NLTc-7zfp12$dC9aV*8tZYXX(Lr1&u-OPPlvL$upCBO2NeI-c<!5VIy^*5Xo| zbuufO2(cv^wf{UA9Zykh@~tgnu4I46Y|%q2<bb|;6o5=;i6sVq2wX!YrKE;M{~3xW zREi}GM>262&d;O|hQEKYS39*iX~hjwoagunWT_&87B3NvNk!d<$22Bnk+~X!W|Y=C zaWVtKdBQ(nt9!-GMaxra&%APT5z2(w(xh!}T<7XiF$j^{Lh_J(MNqGr`E*Ql&gO}W zumlIIbELzc%CrMoHHXafp}7wgkq1=l2F6ZPxjebaU4^e27`9a&beztoVSVs3(~SWA zgn>yXJm)Z7Zu~-oordZ;-e?^e4mW{%krEXInHbt2cRb@fFYwpQ08sFnxV>WJFNp|X zG~9orM!~m=4^rV_oZ|;ppuPp`#sRU>F^HPgst6V~TME;0tZ@>7wQ+tMN}?Pe8eS8g ziY7AOn<>FFZF(JwWx2X>R4j?zo0q*XG9*C8qFQ43LzL5{STD~n8zu>p9$8WnJyi~v z1KYWV10Urt0aW5Y(9E-QYR9k`7{9DevLnh8VX-hZBxB0fl_nGRBJ3rpRh~Jp;3g9B z5~&n7F6zA$gkqLu=#|;Hr3(?-dThO;x@;Ou{`!FPWM0vu^q%Upb+!}gA{!+j<`G2w zdTnQ^?#|4Xz(KkHW6Fws;;}Re*OuC-P~4L`pY<V_=gj$phV`_?(6hZ(d{bx3irtwq z_78<x3<|@3d5-=NNS;auSAIqkhSr*KP)R4UsW3!p6pNAYdxJvhod{4aMobd|L1Y4o zaS*SX3#2?+^Hu5}Hg-!KZDr3kN)%XF;NTUEE!y2&Gg-MxjvLSde7)M~PLX4c&^fqt z^W`Su&;T|F6~sWC>?-9(^0e^{A4ufX%LD@5SY~M4vuW0;1@PC2W%Omoz<ty&4<=?M zD3trP!W!$#BemOtfBhS*V+kzhH?KOWN>Q%TTsL7s0UW4P=UH~h!C(Jw1PDst#9%N0 zh0)Nny+>Yunu-*o%0{<KskeOrde|oLAD&1t!d|Y0%TW(=f-M+IN&JTs(tP?Z_OMN3 za$II`*mdKVS3YzKa<4<{TGw{#n#t@gil;z~NTpBmUV?k{#hX*lH+0q=j~XBJ`#x1k zK}A+eqgSbHyaf{h%rS(zX0-W`1^wfeMlPbRE}Jc7v%ls#*5At_D&6$m=iOkT%Jn)? zd&$3&fA~on<HeSwFvX}RoG!-O>DmAK_uXv3-z3o{pOBCo90IN0+s0i?(}CPz>#a1C zN>CTu)Wc>dw?NN1m6!jS*;;jNAl~_lbJt}XBCWXrk(!CO852-_Th!>R{4^eJ{L}uR zBTHTGYr<3<8O0|`LAX_pW9eLs<Ta#Yfi8tOzcdKUbprX!X{C}6>ksPf{iFyCPO)Am zvHi?BdUWxf(t-QtK5_O>$Q)xj%wLFI`{NRF<8(iAnO2QlShw??GjSwtD0U#WGs+a| zjjvjWcl0)r*lR~_v~wXO?PI}XHZ>X=<L!POaR>9<7#*R$xJs>vM*0L0HPMTVVLb#i zM9;lL$WSqSMQ;3~UCi`Oh9U`lVAe99ub%px`RVt2#&R9JuF6gpR3j>n$a{IIP`1<1 z7-o2yj{r2UumSejM{$S5MzV3uU&ur{z=1Tl!p-5rREXECxS2poR0d-U@)zwI?Jl+- z-527d^uVnllCO?558=ii;vmb7=GH7%@=8@E;-7w~OV3FOL#+v4lgaePWWrC6he;oi zt0tJwt26m8lfTd4;T=^Ef~G_?8Q@M$V@f^r=fs{^LlG+YBg)cWPLmy~^Nou>BEUS~ zDD>r`L7561PTyh<dgFiFi&yIO4y6z2e#_+YNSnX<`D#|V2pVu$AADEdRTJ*M)M~co z8%?#HK=5BdtwxN%htYi`%xxe9!hoF2hX5hA&769zaT|^BKr3hg-J9h|XuKiL(3#p? z_FVkQ<4h1ap8gZR38r(DPbQ|V?+<;QVmT>IjCx(CXy>tJX<OMw^j>^!7Rh7WJ#GP< z??7R81Tm<E*!}eRDe;2C@fRaGg4hdv>oImsu=FXIub_MaDlqy*ZPG-O60oN}Fs!1g zUlPl?n0{g^9tPfIodIc)p`uwm5-Whf6&xiF=gt_1M-beYNy7EvbT*=-z?*m$K{MRX zeBNEO_DVLik>T?Oo=vElOd%<52!QRIvIkLC1y!be+yov8qyn<JSjSo$stOFzi@{6; z9B)~v6D;Rs$|$f|I#Qz4aqm+<ji7OuIc=GvS#!X+x-S9tacCxT$c*YXt!BH@Mm7n} zV?Z?#(W0+#2l|Z}-CHp!P2xw<BkoIX|Fp`hKN~P*S9ySu#FYG$B}>Q?s{?ZP7@X%c z0&*tE{dr;ITrOWV5w(EJV|Oa|{YIgObc^stp)?{}umHZl2JoQ78b`2Tc;Mmes)Vy~ z{i0C^9#dDjVEEN36?1rds(vvkpb+zS;Z4%ZswFY2kIDU}&7ZbDU!0ss(B8sU+oJ9s zyf)25e`L5s?Bwbh;FWPxZE^4`vS7NUfuz8g0qA{3)Gd3&B-ZaKRda&TeAPGrf9)w$ z%r(|*1~Q2MpqE$M42p4GFqKIhT)%zh?s?8fImT$iBcZ9)H7Mrul=swRl1UvO)1&e< zOqb4W)qS#~VbjdqXL$~xat(3X^0*pgnE56oAZMKXOq?#SwYc0@z&SJr+HNOkCy&|f z8sAk`B$$P>(b*r76+G(+x+V;>?J>c%I5g;JI{Ycjcct@J)kcnsc59wGdpp4fR|HuA zE^tIf15$rFB+q;C5n}W3qK9%I)3CFwo=nO&^c=>o4Pu5F>|r#`5W)lo9-`eEPdsoo zn+z%bq{=i+wu5h)GVdK6;l;BD5+5}i3lZ&LA^oOSl7z1;Y%IuhQm|m;0wzeT-(J>- zJ;3eGe69loFrPP&L<z4qD11c!A)tWervnow(;rv%s>msEw3kBl*$RJ3MImty9fi*_ zt!c?vY$*ZLhKh)h^Tekr%Bb8#@UWGd{RF*dM)?{HqKD2GDb>dTHKP1kR10zV;}Kxw z5~Z$toe|)r;{*W00uhYyW7X#7K6LC;O3dWZjn%F+hAWHbf$Rz$gtAEphWU8))N&Il zN{x%Ua_KIRT9g8n#x)iGC+LCP{GdF`3fu$0#gM;BI7ZVswAmL&y=mC?SVI^YYd%3! zbSf}Mf+7c(AU22lJhsTdapgj}`*GiPc5y@SNf#YI;@YGI8YoVC|BOtImSI(msl<h% zF|E|7HLW5L40jrzh*jgAuT14r)h#w2v1I*heghq2!bY;}XEiT|jtf=>cP}yaAZ=i# zI61thNrYEB#5lJO_o!+Lu3SG&OjTp-5l+xX-=pf>NOG+LD9jt@0r=Mhb(SLQDIfH# zDg!^h9ky-KcWxBHEPz-ZDG1K(RlPQTisu!F)$RZ;P`#DL+<-Iy5Sj5z1SAg|ZdUaO zq_}h+WZr9}L~TUR!YWl*fJNxF#qK$9B_QsbgB+B@-Zwf?yX)ATHwJVZcOQcoQ5$!w zn(XX$qvd52s^gq5n{y(g%`*+t!4FkVGcf$Llm(SEw%|c2$8H<4$5g`OxM;MO4+-WZ zQWD=3`)}er?r_-cbbsVJUHIV|+4W9+=Y7A(cjiw45%>qf6^JQFK<vPCnLONRFwJ+z zx3Z{(+1YLi+}yu!2kr!7;{*0L@I}?}v6>g#e?MJ?Sa#;C$B*LyZ?7>8ibG=@LsEQE zN=;G!fLB+cs(#MAk})V27AOiJK$Pf#5aV_W6asZ_bgqhBi0u`=xvd$fxv7a771<F| zbRRy{IJ7Pr97XXx7Y?FLVBdLZZfL$QcE1dRE7P=c63#o5mkDbdVJKO6K)&<8=ZLci zaoLvONsbW?=Z@$(XBfaUo2lTQww#qw;JKJ0Po>C37f_JxlwwTT#+-a4CeE&NKJ<h- zVUJ+iiXSKWp%Ivch9F@+LtIO<DV1rP0Yd;6XEWl_<Uc&4-eGdklAPsxLiVeJ>R8T> z0YI4#%-g0K9YbLvRB3zcdQECFyukGEZIaJ<Ix+kWQuctfFuzy>L`6q;Bme2>k`nOX zfQ-9N6v5_)7M1`+$j_ZnoIyp41JMmc!DSWjW3gHQ(Q-8$h66{x2J^$!V&Hm4%Q31o z0Cem@riWmVGm4r`HW#SX?WApw)|4ireiH3O2HaVpH0c(CB12xX2vMRi-_k*u#YO<< zt42UBg=qRz6|Ebmgzy(brj4qzHOC9()OiBc)R@Y%k7x(>z$U;6drfluuJ$!b9iaP( zZ&;6CvYVR=**NfB9Uo+Wa#uCIiE=P-22Z)_5CW_<iKTfV6-oeNxMpBKV>?JIlS>YW zWO6_)Qf+}~lFsr|_8VZ`$2>}Mn9_p@O2aChnph7q+%Qz`yjCqBH|_xx-pwv+33#f{ zKcb|Cq{=fTKq;T*P@^m}DS_PZFJwgIRZY5VQlWDU-ca4p%LPNlU-O|uDo-2u*9u8X z$*c0=5KiMFpXgHqfQa&W;i8ynuNv@DM_6-&v9-C0Ez`)-V_eN{P6g&BPGa)O@)RcK z_N#%0-LG8=jxM}p2muuuDEQ*y!2sMFMt)^;ny2gY{%jikj}$mwB7@e>N}ZGnUjP}- z-Tb4A#~7Sh7&kfm02)B<%50u!L)<WKBV=ys1OHhH7s*T(4p!TtE=ttT36O*S4N?|K zbs*&SGP5ftet;X`|BY44FP3|rZ$V>9OvsqnRQEIP{4Srt5qVpPrY#ar>7N>3^qa*X z!zUs}1QiRv`60)5SRn68yiRTY%Pfp+Y;<(t=GPaT%V$MdZQiAd#e)4ZF!;lvMRp^7 z_4UhR<f7g2yQ4R&I3SSUK{c>iJ&p4<6<<Cc1VTe?gH^RbLiXvHswa1bgQR|Tsrq!; zJD8ZfR<DL|CQKuX25$ZwrJ)aECBpxFsqchh)p3M186mA5Y~(MbLFh?`*OFq}bvOI) z**##F#(rt!dY#sgfX$@762^-R`PQ8|*Zts4e6IqeS-9xSTK9VBJm~WJ`ts09ONmUn zr;}PrGVS`yA^l`7q6Ya{FQb`reLinN=@h1&usfpiD~qO21efjadFc5UxDG%@xktrj zKJS#8P4#!mu11JuWLO{(h@1Iv=c-bY1EDVQTrfyff}xM-OtVAi(7QwYu=ilT<1^j! zZ(L4*NOXj{j~ds*bXwQ*cjm`|M*MeGulBhjsQ*Zk<|!s43K`|NsEN`TI{1Zk5AO?S zRod`$wgf)a?>OR;xq!)vw4mBe>gHU5xvKS^!`_%w46PBL(B?BaAcd~gLgkZXK}rB& z61kE1G-OI0U}G<Aonp?I9<~yykm!lO=0O`~9$QW#-gPce7%R~yO77RkA-6<IEwCr# zbJ&(eZ^tI`0?A8KXzr4_^hn5;9k9vbYzHQw1eB1<7EZ<BQjPPo)7sDvv3s|hxH|+_ z;J_h#DisaXoM7cGfde2<Q;b@4@d<-pHGno=6RcH9bx9SSh5y(oc>>1wX-jC2-VmXl zCGN}#A>U_};Mi5oX@rQgQAdT6P+%C?c>HUopp;jq$vuxWj46+t+30vg-)D(bw4zkD zb=#F|d1(J0$W0A{)lzj}EHYPByfqo126NS#e#!*!B+2sQLffseMu5cr7XdS9z^)ne zBN`<3;3PP{{+n?Q>5&nnLdLoi07jzWF=_SKQ({zO@NJTsp0oY?Oi5$L=_?N?(MN8; zshTbr0H#E>;k-fGVJZP=+&|D2r!;lpU2@T4Ax0-u0R}T#pe%?`UjWO@)oHOl8mfke zXV^_-69i`e<YpS;5WH6JFk~fH{i2zi%1u6s1U8U7WpzGJORjF#OsuL>?$U+z8Hs8u zBj9@0$;qIKhT9sVj}WRVIA!cm4%xMO-N%&P_RD$KkF@=&>y<z^g%;{j9I>iL&L4^L z_L6j9W?db_{<11S<j)0b13<fx*UKLQ`j}E>qlZkVwJ2QiT?&A&YAOi8Vv7f=trK(Z zB*p0Lj8Qb<tKQbu88eUpK>C2>I$9H;el~=I9vq0Pg!q*Z9!)8I%9ACenay(~dN4?< z701RJ*6$}nNM0io?$Fse?Xb&Q%VDAYVbp4>ni4>k3E`;3cr0-P59+x|sVb$FeHvMY zV$NP60ePDg>eUVGCY*#XRgO-|4VgH!3GgX%32K%33=HXY$!p@d<l^IgRP}Wx)~NF- z1*<nhw2jk74ogDxVzsPk_OL&1mC^LZ#1bG8aTUhi#RspkF>IbGk&Lb@DDXJ9VlEkZ zKmM20Xhhk?vSrN4|HFnbJuyjQfHUG^OTYmzS7#IY2RjV4-JkA`rDb`~=Wx<T7Jp2% zA|Xz(($mu>->$m<=$n8&bd7Sl64$-e5`yo`148sodE`__+w0U@3ld`}EXC5=g55ID zn)7vFFt8jUu>VI)mH&XJBT0*C+8fLI3u64h)+2Ync_~a=ms|ehHn;r4)I*Cy+sCBu z?r<s6c%r7i#@Zo#GQ}SRnylM2{dfdO88tP0T>m#faLUa#J@BRd;Ju6&UusPHgC}%j z>U7pgi%O1v?AI4!-!TRKCU)U_-)XKCXDJf(`#&`H(NmwSR;ynwDX(9SrM_MvgFs2~ z=R8VVKU32XpbSZ7yek2;Q`|@mbGMXKnM;bC=e3@`imAH!Y4UgHi{{@Lbr&C1eJ&C( zdHvz4fzc3!&IBZw3$=(40Mw$T-*tlw{L^cyAgH{%`@aLOWKxdc0@Hd81>sK?O%Kfs zQ4%m^1U4sR(Uy*1?RzVOI>(T04~!(+WgIc4xtZK*-s|!Gjpgs2;td(w#=w=9udP4B z&eC8*8~hQC&wP+5>X$#DtblBVmbdRIH@O5v#V`?tsBDo=Y7JtX*5$_3Q<3|X2Q_Wq zmuurP`7!zPfq|G$JfeHeV?pb9PGS@gy9Opal1Mqq3X|Z(8_F&UorT)Cb`yx5DvBC4 z@Z3Cwgfl*JTX|$L1}C!{hX{}R>5<(_CbB*qd{(IS3Wz1l5)gbd;`?%ILhYBXVKjA* z)k%+DflN6pNX>#yUOC6D@g19=)>#u2OY-4WB}c-J-N0^--{r!vd76DS$B!REpW&yL zS<SR^#+9Y$3h{)RQ4HCDs`pLLETQ&%i;ArQ@g@3VJs&J|g8rvEgo6J6FXt{`OADrz zlC8`h!vFp-Hw-(FU#to%+^muLzw0m;N)<Qqai8nUvUTvt?3Riy{bkXWsx;sp2(T#g zt3bO>-Tz&Rs%K$|n|lQR)VKRB&OmRkV?ulO7gg*xL1-sVG^a9mO6O{HsVW&53{*m5 z*KHUCqzru)Unpxmss88Q-m#UWo*f_i1u+-u0A+J8f2KDZYBbOO`khl)1>3w&_bRo) z{0jI?{xU_IZ!$;*#7#?pu^{>UvW>qaYoP=U3hbfmkyHvwb^UGH+DxrNx!D8@bpTMJ z9HCN6+fDKHgqvr<%}1_nmU}kj$R*exa;0Epaf>8)0k&ukVFNuszO&`TcgYS&{?%mC zyv%<sqEwn8oeq^nVM_UDE8}_L`Vvo;F^7F-=;d^?ZyaZ<tI<~Ps(out$C;z1d7{iT zquDoM1mw2wf&})3nlnpHV?e%D=TvNOcBn$L3}xB0bN{enA;oBYg{zT^$8U?F^^q!& z(2K+Ra(}l<g4n{Kqm|L<pDpU{e^rbPcR&52ggcbLHd*jSRsyxJoOf8sE@}hF?}|iS zjitu2Nf>2Dl+Fs$mRf1JQJsJzq#o2>7Ma8SzmGx>i~wP?gd`)b1AA4@e+jr%UQeE3 zvxVyG>nRjuWlK7oE=v8jCnhw-ji)I-Yb6j^m>IP`D0@3mrN(cB*+V(n`H>sdvPZq6 z1U?a(RwB9BrVTsx5#&EL5mBT5j{xN2!YSDh{R14JngQbWu_rWa>e5Ef97d$@Ti#t^ zV~_k-3b~O5Q|ozBQf)SUVRIEqrAo6A0rq6-hd8qX*Wa5vJH@_@YN0N3J_V>UQ&W$* zKjp1ipf;PjHCBF7#;ITY!<-Oe;4}FH8}KS%6u`kJ`8)+s7<IK<Fq}xbZ>?eEEyy3) z6E_NMCt_ryTmgm=e?HsXv=hHc<q}X;pZbp9)@3$o%I1CYDl`o`4d5t!rRFg+JQ2|h zoyeUxwa-fY-y4Os*g7Nm2jIFDu_CG-=<N;P9!bK62mF}I<2(5`hjs1Uj12)`18Jjo z$K@9x!py>7e0mP+=tNzRq}kcozCu`eEH<~+|3j&?Qz+I$LH^mFOiKH%eT3fvmRzHt z``ckr{K;~AJRyyP{R`Uxrjk=EamIP*d)>p5<mBYd+CoOk!|wgEAB**~pZHi<WfA}% ze;mwLw<|TQW33Ce5*e=KJ(pf!1+&7M8LxvsKMrOs5bD3&K0jRiyB^GZI%GX8kP1#D zqQiEpe_zci4992yy}Ng*4}gERd!GK7nws+Nzj9ei;>K~>`5^myt>>wyk`l0y>=esC z8hbe+nsg_Lg<JHB#?Mf7YO1Ojyonzt<IIV*fiL{YZ@xoW$O@UhiZk<gv)#tAtEU!9 z`Y@HVG7FVPootU1EpF1eXm4Rf>R66iMop$v`M)&m4EE1Y3)O`3OyTOdIPi!zfP8B) zL9&XJM$mdQpe_$f69I0()AL-B(V1eL2TTv~(uiavd^eG9`0@Bqs2w`_qk)9+$#<$u z7^4b{kWfY&?J^!IQ>r<c_CVy_f3^_kS9V*8r1IymnbTr%&totf|2LhKj(~l=V!wXt zZ|_wU%*$AuDFO18s%^ce1m%C?@F^~JWR#SYGUQI7N!4=a{7&O|Hjf|o1s)oe&Yn{e zX)hdBh;lz5nB=Cq8^YRA%ymp%v@UP6{(O9zze4aodtwsQZbrXI#2K4<K<hg{KaXWo zftJkm7S$yGVF#`%fC_@ex`*%n-QQ@W(T@fy(6T$B?A8_s9q%0V`7Jx4JNi^aM$!11 zfAv47$0w2{jb|XtX?Y2`w@-G)x2XPOs|iyG6R^^%7RiN*xUfZ)hiv29$3ExY<%8#C zXM;AeGy^<Jk{?IoiuWDGV03#sh!t12L*2u1ELBOYv9a+o5udcrj35&mY3mawhxuy& z2!VvM29pnpePfb9?~aEk>H_|rVi#4^X->Is#tPQx{f2u1%ibCD#hE0Byn|f>AH#+0 zW6d%xx%!8Mz?)cHo)napoP0eh6bV#8Z8#pN9J&MBf77)MUMIp+8D*)@7{5<wL|h)* zFDt;KxhB$GqScXEK$nQ$8+|3^bNFKsW#Ok<y5WM$?8e7fn-HGq@^T^>yYrk;JmzpD zB{`i;AU6*wEdsSagez+}Wu}=tNS^?>+`i<k^xy_il31`rBs?h#`_8kA^X>g^bGiI8 z30q11$m3Y%xzyoX_f>m8bcsv!tVMUqq@MiY>RTa4Ex6tbtdUQ(B{<~%YTx1ox_|z( zSt4-@H;f{LldmW$ypRUx|GWUO^sp)bsoXKDeUMMH<&&A%z>2Bu#IQ{aV6V8X?bEz( z?S*0=n4qx^dn|O40-uEGirqdtUqXP1f{A4L<;*;$H==2gWOQFfYpP{ki(w-fTw7aP z=E-yTy}Fs|)&z1RBQ>6h#18vP0<`{|w1&pkPYf<ub4lCS*~c@1F((9zaj9rMo7NY* zbvk4KFF_(DxgOeUspKqBjEecPN9zlGZ2g+Wsu0P>Q!}P=UEx|DL5(Mb%R$Xb0^X=n z>mu}(f)QC%lM?YA6L@P)-HxU^nUMcsBf#;(c#bTCWsvE-LS{?;0gNyt5b=ZVWUvk! zp*xn9(M}8Tbxt#Yi795$Y{-2?J$n(3t&>&0OL(uLA3+z(g`S&fQp*p{s;C}t1jg)l zKIrJ^AQC&Kfgpb!;73m~D!?cxs?~wkp(om{KVkuJSlFy&N}vH??ef-{67zaS_pFUv zco07{W;55c0-y_!4*E$e`JHMVXe+N^Z{)I!7DTm`mBl+5vi@@aV<?Jd_99WFL$P?O zfz*I2T!GB=j#r;o$&RUa8Y9XxM##ZovLM=k!0+cH%iF1kBJk-<MO<d5wyS=Uz^!8$ zU3I1*rli^p{|t^q9N=o!ye)1X(70BsUhk{^_A*{?JSm3Rmu!=c1wQ<*W-QJmp{j#K zSQeUeh>}k(La(D*zN|V2JpP_h2;;y37XD8CB6>ln^BljlD+1PvJt8O}m;`nNNZII? zL(m?^@O|*7Qm%%VZWSeJfHr-q9Oa4+*<qS=5GPN)a!-X+_=jZD%5H88O!D5bw&{I+ zF=0Z}?H&!7I9orV;}vRsxe|(YddymRB4<WWVF~zbTQep<j3>rO!~q-q1j6oDNq>M4 z<RCP`sS>Xfhu<<bpbFr|_z?WpNdr)oL-UA~w$gW3Q1Wpyid@W=yGm(F43tX1y0k}t zLYdQGx>%X}J#Md3D#(gFV4MDEs`UEg<a=M7EiQnqWHErt=@P)fO8W>mWRwCodxBC> z`&S^0*ExaRUH5>cr*lbO$nPtS?XB}{bKb{;6|zm7>h++Hk_UkW7gUnOZ$loOf5`jl zkB{95$5{;=MLatT?l5hZjFtdfTV}-wKAyQBi;bdcc&BxH7s9c*MbR&QRSb_gnI$c% zh;Ah+**=jal8q$K;w}W;I$1z-bWe^q@mS<YJA{OJ5W<yc)i|4MdR+i7^s8MGeZcTB zY}<qd5I{HyeHRVxfba;wfj(3&BPPvDIv^>A3+iS@<JHHuHBXKgD{%#k0AAAybLx1A znIhaV2c}<!fKoZBIFk@~`em*Ljj+;Y5}G*i<1ughHep%DN(CTOB`}uIGlK_xZon@X zqX{Fr&p4k4Qa4sH)y!_Q5vfuFoLLBXR31*IOgg5rOmx~8w3iat1w@**a(VSB0aLk= zIR$NM)?JbWsL6D`jHHUo_(DdE%CQCkFXF30V3vONgId}KVv${esu40(RrrT40J&<e zlSVN&K$084WapqHF-MVr<EvK1i!NdGp2y=X$fo(2h3Eu`iF}LSfkHeDKib?-EIA($ zK@3Rxs1a<QPUB`7{BB_1Pfd-L;YkykkiAt6?ltT<Fuq#b>vF@8T*0f~0-nBy=+(qF z>#-;?d@Z~|P2jK!y6oJYnAYEcnD<)cH%c38e{!@&5#OP?!VpN57866RyDJ#Awf;X% ztdFs3=Jy%saj2QG>7vQyjVy7aX=R1l(q&j8?$z6CQ>On8L0NW8@M8nxU!OaT1|oJx z6c*hhR6y^};6~=#*o>MF+#+x6Zan5o94+|YB!8FbE6+)IO^R1&W!^rZw;HKF-yi<C zgByco4v%#@jj~zK?(SK_5LPep#9c;*+c<7(?J<gSaAfmI-}$Gf&xWd$%*VU@c3Q2{ z<vFWmUez`&Ehs_IQ2e>TW>C9#sS@#lZARAnr_Jcb4@O=+$)G^1oyc{A%+wy5XpfY2 z+j>7SvEmo$2Gt*K!e;cuSASUOs4lHrMNB@%wj44LU_$G%h-$dPb5I=@XADReJALnn z(&z8Z?cRYR@_|&8Z==Su&etf9>E1lkd>`N$v92w+cltE82hSxC-nRX5)Z_UW*R*Z0 zigw&U>+Z6>O7-VcmG0o$vAmKe&5HzO&m}MoUUYRSzFz{~bdG&5lpG30=#joNZ=F#M zCt~OPDD3e*m4|m?gh+f|*(CqUa=OKV>3Olp;5UXCVaZs#+ar5arzPXF$B(z}BW}|e zq<L8cTi;Yva4HJb_$aEV#_@%#^i?0sZS`zdDX^=9Ib^tm{G_M%vUkSWj5OTR#*$M- zsfsKjx9sN~$FI8TtR*Ct^72hxT;qvp4?cJP19lwLCX+G~=xT~ZimEm72wyF_Los-T z$xH5&TFPY!0AiZ=V{$DYjW+aDP?N)5c(MZ0S`#>A7}N>I=$USoRV&_cVT_K_Iw1P- zzP8X(;f@N?m)h4+UkbfDWNFSt+3}+)iu2=SegAA8z+%rvJnB8$v;N(OM*OfD^*G<Y zHKd9WJ3f(S7w@}v6-$@dJzw>Cxheg3=4&>M&?83`PPpt};T^W;E`!gvOs2lKxZXKy z$v^n@N+#y;RJY2pg_5wbC}nnm(dAE5PrDA@4`D=wMZxHT2a`y7nH~R2SGo+>Pj4e? zy?mw82d=f9gWinUHB3n-YEv>whi}hF>MZI>=p%v`#|WPV4WH}v4RrP-nVm^Q-TYY} zxJzs4nX<Mt3OPneASw<vS($p+vZYi{4s?X_T%Ed@s2SzDek=Z~{yFXbnWFpuYhtmz zEQTEhBdydxPd+4UytvRJ65m*sba2qV{Jm7VQf%S>ezPI??|vy?OB#WgoD?h1a>d=L z(_q9-Htp)!$)A1$i?=6%^Nx~rm+lc}<@{(8iPVSCTJO=<`N4m_85bUXa#@|Y%q+pv z_$_Z%Y-^uRD!wKmwl}lxs^ifj#ll>e$xyNQQ^Bf-^Qfw?|0EheDPkB$K`KO{x#o>e z*FSd2nr|k>!A{hl&3~#m#01jYVpF?6_zo{kZ$GS0>Fa&)`$2&eLd0!!aLo~t=QkKh z`cQMQ<FIbRrp`Q5#`H*36B_}4$>ezuI<)r$#`I@ph<JkDyJ}k*?3;i2ilpTD#rs70 zmwfCGeErNBs(-#%8PFw>-|};eh}asu+qEc?yk8X(i;VPDp3^LrZWa;Ed0<BT;QJ3g z5fKXSVtR3x)b36(pPMCnQhB5NeABW#G8sF-i&@yb>t@xn@GsSDa&p0EdzU5Nr|v^D z8r4Zf2;r!4k~Sam;XCv8VExNX*1nOO??VRO|2tB^zEt3`uwaV=mXTjCVopCO{lQho z-OK&tz^1R4bUCwTtf4ZT{*zoASILp|k@qo40{sQGhWu^I2h*sYhGOZurh{_kKG*{X z?4?`Lk@U0`pElYR`VQ*uBkZz;eouT~<vRYlpVlr#->?17+|u*zQgZ`mCnqv;buJFZ z{$iox>~8TktG{32fgX+J(S?&tw{7_s6^G<z0h;ESTFxJPqN9rMo=&Jx<P<PZ^EGxb zObh%wnTahgUL!SHd5(F_=kvkA<^N&pE1;_CqOB#QQ&Kvlk?!u0?r!Ps?rsDmq#Nn( zPHB+tlJ0KqfBgEr_cQK*L0rzc`|iDBuDRBvqf|%*4Mj(SF=-@P^QZ8YC<c?2jJ?=> z5K>t1N<C?C>$k9)OB5zu{@Oe!(o>*aWg5%J(R6)(5y0Del;%=-JA2gL$*Y#sYXXk; z4yNmuoTFrTa~(oNO0Bu@@bU7E%uyndPnBSa&ew$P37gOg#7FGaNei9t-2;wul}F5m zpCugN?g$_f(3Js;`S-&5lSB3iQ$|d3!gRf{$A0wi%%Wkp9;P&0|GDCTCfSRj0+ljs z@VbO*Pi&4=njKoz5j0uJ{t<!6%=!j#l!FXmSyPRj3ksO_B?nhpzZ6+gzMlFMdT@L! zFqe-f9gEOPI_8`Z9V1_n*RBJPe!Y~t2EJrW<QRAJD;@y@-FNfC;>P5;!98^!W6_M8 z7YNRnN3q&fL9v7<vFA=1Q}RNQQG9}FPS(zt?+oJTThPcsyVbR9Y4nZ50^gpxVJ4sV zoN145#)lO(+)Z{IELEI=)zZ~nlTEQGiB?eHs)z3<Q$|#OXFA?9rS>hU5Ydmp)71N- zxx8h`fGss5VpulAus&yCLf+%@^OS#Jp6@vX%;!>-;q{L4>lrFlV%1D;albHwUW_a& z)+!$Ke-;g}4XY?9Li;xFE~+jCdMWLVGhXa$`UBAt8jX|EX^S6EWnSjeTh!HWCIWM+ z2Mj{W>~S#&AD1d<O`MrnXZkThMWDSMSbPs&uObJ@MczIz3My}JBldn?qM#GMr3${; z#J!%ke7@<JHTj{FH?W+>0`bf;Wbo=5?R1KlcX7d~AwT4Z6>$F{5RvOslfH7>CG?}Y z7gmgH3M1uZ!HI^yPX{x!STne1A0l_3T?#VAm7O;7+X2QfW)#C-ZF$OEu8#2U`H&mw zs^A+4fI3_-M#yttFpQMqnow-9Z5rHnWCXk>JJc&tR<S%jhW%g5wq)Zeu<Y9#jyn28 zNW4XZa+MS^44W+4ZC*TQo`d+$d=KNx{?+y!K0Y@P&?=d{gB?uHWMs5F<wFw_x;OnB z05~#7i5mX)qjHC!nuTVb@AX3UY;*ItaVQ3cl@V!Bl}tcF*I&>L@MW?crY;M`qP~M} zdim7u)WqN5aA_{r(~_)Nj}Ve$(crAPCL~1c7RPvTEhR|a#7Z^QnrD=<OaX7yeb-x< zR3xU}o1r~LpFdAFa@(eR-!~IGO-}*qR}ntSdwIQeb?XM^qwzztV%u}3L$AW?quOWv z)3EzEUQ?-4>FA?%intg3wbkV5LA_+`Q+`YNYtzBYz-Y~JDS`y-h#WZ)71tWp<wGT- zKz_i-CZHTn#P=@{b)GDe`FniSR#sR%f>1Wy=?Rikg{qN5SO?nnnbj8q)4UDsaBE6X zWPk96KG&Eru(M;m`$5NqH#2gruo8Yu13~=D`f5yqyHx))4vA+B<@E+BcU(tUDSpDM ziHVZ)eFqk|`Op4#Pbs;AMGMR8ZsJ!)MpM-aiRPDEkF@gra|4dd<~FO1fZ!HeS%r!9 zW?q9OmfdfsFRQlCBqY=|Of)q$TjX-%y6x1bA(A2!>(%Ygqp1TS9wtKDaaNa-9<#bl zD2!fi0#;?**~G^G+6OSf4II>S?~8stT_brl25l6#4?w5n?F9v)?P5)RRTTpp8<JLf zG=@-bx|sV_9HfiRm|k;_(;Q;6llfzYima;?w1q)6hen!gQM>ly8_doy!F30U_<bVb z6osPs`xq&tR_8EBC=>0lDNfMzR9E__!UeGvb;3IW6f8p+#>1vFG#K3LkIakbt7B%n z8Adu;y4!&eaXJQb1z2lbA6jfh1e!VtmO`#3i|13yZoj`gZRiB`7Yqd34G`^gvvcsU zaWIamt6B!i)xa^1>aFuL$y;Nfp`PEo><3M9wK@|1T+kN|>LeHl^NklF{}9}&otRwO zgv%GYBzSIjZBK-IXUEScmhN+ppg|(x?>;gS76yY&x~KTz14lS}w|-!{kRU?`I(wPg z9ww{<b}CnZ5}B8mcivz$FzT2gr*7hW#&3Z`SUpA0O#L7n?Qv3`JaNg=A{8ql3nh1- zTG&oeQD2+Ki=KgxIA4z+n>3PM!w#lrpx8{i+DZV2(}D#a-nqMbfbR=+uCJ4_yby`! zLeo$CtTdbijcqQryp#Uiw_Ux5MV(e?Q@0<;2AoCxhITPaby!hPMpCA90|`lOc{^{+ z2>EOXMt~BfI5b9A82EL-Y44cmV#n4ZK9Z#=rGRY};ZkT-+F;FJ^)FlHe3{v7eo8vJ zhrxBw-ub3*cXu~s==TabHu9tc3xz2Ke$UOfTGNXV`R276u>_o&>+%z$w4wN>ioFZf zMvFVSGK)G!`j6BuYlBE3vt^p~C50`InPhic45$%qDsR5%i;~g2;=R5OjC8+k!_hmN zB-$JJfZuF)T7aeMYq#-C%Ko7iY4Ym~NXI)qLq9|e+BkX6ic9JGi1T}engc0}6Bjrw z>gaMq{TaTa6Bt-VW)m*?k3SO`u)+xVKk8)99#fULa|#ho#*nm_TB6PwYjVELVz0wf z?jvD}e<U!k8lq}rLf}sL;C~djM)4A2C<WXk{F0pU9NdY(olT@v$_oPFP*C4>^}r*S zwWBYND4Tv~(*a<Oxu$KIfTi}Q)8$Yke6RDIP@C2pm%L7c>B6ydVHc*hms@V_;!-MW z%lQf_8hUQmTUxYPaBy${(0zx#A6psHTv!GXXoQlPRVCVE$;x;5)uV@IzD&6=|D8ba z;C!z-mhGmeMO8cn1_mqWsT1UOTs??}w)JjndkhnPvsvP5sn*O@SQ67g2OiuC*O8-g zYkX#5*D5aTn7rk7roXnJKHVPFp7!h4qGh*YPaPqcOXPb7+YU{+g;(Vdzwxhssztou z{tc)?(F1$ZF5{WJo|qpspSxb}YKP(16*9iZj3K{wJ7<2Leu8d>gM;(AQ0wIKrQ37u zd|E}5<70IhO{uxOL{9pM3=eONI#XV2yWC-<wb|bj&c4E6JS<aR&w4ahfe0VV;q$y- ztdLhImn8|8#%>+pPEJMv%*=d_-`cvtol{d)md<1{?g<Ka1D8wZX!cpvtkJEz&jc#3 z_*`y&I@<uq{Hxe?p^Obr?;e5w<vJlf-En(RuIXlOnVNcdzEZ2G2AkL7`uc#GlBcK< zpU;VblgxcQo!g!Pj^bFc+(dD|7cGH)%ursL2t?;iCkN?YA>ikpA4TCTI?T%^BR210 zwdx_-pRS`dD6+2%24%T(DGkYGvz+|~U#^e2`v|tcrXi^`W7eM9j|J=mvhpcUXJl}h zO-LeCQBxI)CXL0MiH;8jqrUASNvlvcEu^9g_Tr}ba11SaE}8S_n)JDU%Bos@&GRnf z3#mBJ!xbD}$W<Pr!^r36YcB01?Iv;$bWT7>!owhFKdyN~KqDExo$)+tcLcp43q34A zk<#<?)3UH|Q0~vF>B`E=ey?<Wp4WSI?CSD=dA!Y2KF*UcP-n!zMz1qHv$_50XWG%} zbnv|{!Dt{_r{mZ=ubac{oX8ZD*+l3Ws8Q(DFr-RLt$W_RJ`wr}50APfZ~PMb9S-{M z!b}d-Ja?TIC&L<z8C)cKO8av~MiVp#zO7)@5?++9=sj?%!hY9Y4#R=bwE_#LUs7uk zC<3+w=X)1wyPuEw<}HjE7#Byw%!}J~%e$`FQB_U9(y}+UxWthC<Q7H#pPEsOvxIp< zkCDIA%{$r<Me+RFP3s(Y&y-49VfVQH!BvY<NmMKOQPtAMN7qapn_HK*!xUD_7>}na zOXjM#w^zf?jgQ@Qt_;xD;V>E9Hxl=*nJ}qlVWXj$ZqDjRo(E<FXv@nJahlZuQk#4! zoJYHfzeHMS9KM&80aj_*k^h-Lfnrs{&swXouYa(1|02^fDekfx0{TaQG;wuv^Io_a z0}e)~wXRPy)}N7{e$U&;-VvHo#A6!LOcFUIn6%#R0RtzO5>gwzivtyn(7DMCJ33Ny zHG?OC<3jbQ-6}1SL5!}?4OKLdBjKW8Et2skLrJl-a~g2+rUty6k<Gw*k%rqu1cIwu zyLi}9&VFt61J0l7QU73nb@xjsVL2X~SBmT&&kQ{BT6duM<INE=bDM)V6&ywW12kmZ zxHw_O=#SA9?-8qg4IS%`pGOCmn`J1m(i3iuUY>4#dui{R*+XAY?-}UkVXx6@O<PgZ zVcELzY^gc2IlGFlqHsgDLDGrh!ipqI#@LQPaqoFZuR@I88MB*R0f#s)$`yHk{jOi{ z)j~!Ilm9W<S5gN5^nQ0TpwR46{LieRE8I^t&wziq=F^hJo&m0dI|4W#Us=`U(RI~h zOk(O<!WWIXJ+;{x&OwizOLRytuWK&+2BLSz5??wM-dL(c>!4?oO6cx29mhwPuTjBe z&mKr0t7=<Ynpo-WElIdoi)}1nWLpQtt!f(@8Saj^Uq!R)`d(mlsyEr?s1~g^SRd`m z;C4QZ-YqCY+05lu$+Mb7a9M|u^Al(CLSgIM_T#0m?yH!xVd=IlbiqzWM~|XfZ#hTH zY~sph4RP4%^Ct+oo~&pn=_qMyAD?XN03VvF!<yX&y-L=qG}Gc}@6eQ##6`r!n3B!6 z2M~{QcUQD$Ef8U0o9%xl8jq|^j1I5V7*7KQvhlk~lE~|6Q#*`XLI(Z4ar}tS4U}!q zf29d;z(xgm5ahuHd$&>ciO}q3u|5sORcyxVu3-q+tf2XiE52##%YAc7wMCO$;)u~z z3W2Mt*PMRY5Q)+x_SOuSIKM2expjAJkmufF>@W+owP#n$_vMS@?x?X*iqs;JoE`3X z3MVrUPmeoSb0_F{v5|>2(SI1|F{xDVpk-z@dVOw9h>u5v3iRlA5LA69CMvp3@mbZ> z!{brsYELeO!5`nUL$jTWpWhd#a!S7*&v&lhb=(~nj-{pW0j+t%{#LPkuYC>WTB(6t zPW$o;_?~6_=No}EYP7)UitFcAtQ8h#YGm8y9o6A0XUs1ObvEO)vPBbFo%$<H7h43! zBsz(o-vW8NDUn_D%cqIrvYA{JND*kw;DS3I^tb!GH63+@vh<FF+GELuRB|hj{%l#o z3;76Zv^Z^U)nTLu7YB@c%zS)ch?}oJH5m(k*rOhcM;OVkmadvD7!B;DJ;1wR5F6p4 zcGJ+=z)bXif7eEWo}i}{WO|3QCvsaTg)h=A$118!CWZbrgXXQ;Y4V6r!QApI_=q!i zXjP*D%?$?R3K*DX8&an5K2m)g(?3ozKmdz)l0c|9V$0C%yt+^_`y)l9%ct=7kv-p< z2l6!$R}Oo3VRcyruDW>7UYB4D@*35=vlU#&a`)XiB%>k0<V5|*>)M8YnZ_CI@TvqT z+Wf(a_zhu5p_*MKDyW&y9Gq8V={Px_SjB<Y;WW*Lj!AjWz8QnKn|l%~oOk&7*|xl6 zMhoR!A2MA=TO|WpO(25qU#G1TE)d#)e>5B*x%6+!^dG0L6X}Q2Q_<hJtYj)gtJa7m z4ELX3{`Cx-Fh3L_cJxn07~wbm$Jf0%F+H%{h)R+Y|A7X6vpMz!0fx%4V9&FQiu#S3 zrsg9syB`A`{l3PaRQvyPaW7o3P1yB;yrSm-u%xeF+aC`{zI^!-4ttUQFr{Qce)HeF z;x|<U(qC2>7}ioGc)$nyaPO8TT~FVB9oroU4d}DKqqimGr0em{CnftYD);+oE6`xX zL8qjVvNW)JY&9P8mGfG(m|lT3epS<PUxWUJHvVLzK>B9}1M87VHc`05!p8FW@E({V zvDnko1Bf^*8#KaF9Qzw4QU9)l{5LSd7Xxl9l-Owm5(fw8<>dva-cpU{(@{1p6r>F= z9N|i{g=+N>1;6_bB=G0!p&{OQ+KTLK`|0VvR}CZZ77?m<f6535{%~;VJ<*BBg(Le9 zi)|+l>XJ)oB&+GBvgdVhHX_lm*4BM}Ko6H?@k=uc`g&O-CQjzs?mv+rQy{!9e+>aW zL-R8;RYvm0#ycI}Pvcqqzqu734AcUeo135R&h|bUr0|a-2|tgHj6{ZoO&(}|$)hoh zZT>z!#(Ob`+nmO3`SJo`Xb6u?F#7lmWbxU=B)RVRQZa<wuS=!D(6_jxkJvrE%zO{` zqvC|$BH((V5N~hoW0E;(la$WrZ!T_F#YCG)Fpn1<FHagRE**NIkZ&($XLk3p9_~q7 zhtLyK_Tjcw*;&bJ#XJ|0SIQ%(ga3Wg{Ewpi++Zr1H|ANnk%)sv>|cYDv5w%lK}&8e zcD}L?u7%gWE{A^A;RZN_glT!3Ud>ds`2$;ar<TKVwe7ElUmt`Dny+flzEWj!IRo<& zEPm!oGVq4>+TWjV0@Hvf$jLJ~>;tmk;x8{RnN7!|-ZMirOgK7i0L5|0$nbDtFI&Gc z8@4RBm->B1BXORK{8&|Z@LMh=(Si3Vx>tv4CdS4-w~KL41lVqOLebswRi>_E!OseE zzJpHH=g)=F)W7<@J!?CkYbxscFsezIubu}0p=-CQZiTFegCd?rt<l1OfRc2_3I-rK z`PYp6Lz^0RFobigXTF@xwU(f7V`gPz9K}AZ1Q4OElasHi>Ni7E&#_2*-fkQdQ<H8L zqt|!VskSGajqF9o7U4rrrKIu-3SYi{#lXYU*3rSo1|-x{^`8L30*X-Pf)(3W$UV6& zJS1e~*vQCUb_v?&h>MF0K%fKCs5GZ^@7ECV;fV{3mX5}EJ@u6DO_X(@H(~ZYGD?9@ zH<r~Y4J;?0?LI{i)6!CMaxxI-G-MOi+437HJM~;2VBMWT1b%C4+u9y{T=Ve^Ts;lQ zC*h7v6n?yo#^Ej`A<e7K@uxMkZBuYZ*XjN<iu~)yfyD|4Y@-$=3bhb}yMd%{AM+VN zyT(UYs0=Jz3x<`xo3%!@(Bi$Kj-O=sGk7^Jmb<@+e|fSL@3La+eH0QBvV#ivcxUlS zQHNs7z#lC}`W+ZJq#EatH*3L)gNP<R>$n^9`0y}0J9}}r#C($y7afgw*ftgta(Kll zC@FcPiaSAMrQH2+w@ga<MqN!&(Q{Mi)&kG3;N>(0jrk&k6m;JjE2fp|IIgK7nRtyc z0!80ebl=Aq@3N_1`rRMpQpu9#(ah_pT(vEd8cPlPe^v#Qej|TW@eZvH7T(@o-d@yW z4g1-?HIdH|E%gV%sIMO5t;ID9)qP4N&!iBDHfkOdv?$}et*g57@?8#sc!3N8ZrLM8 zZ|{_50|=?viPE9hiLNJD(-Lvjb4}-7sS6sERWm(6yq3l)qloNotR8B6zmz|lY@8Y) z8XPgUx8vg7ZghA<ue1}{J~gBx9*Ofmy(p}E7hYPq^i>($E-h`5iXTQqj4;V!5%M0E z&Wpz4+J@~5E8>)oE1GUWy$<nb+3MT!V!2cQcdL4m0t~<Qo$Y}Fqk#P%O&gW6x_FZm zaqXW|@NwQ3`&yngi`z=P$3*j@j5Uuy)P?M^d2KKqh(7vtDtZJkHJ%1Hh=F;>3~NHg z_`aKNll<DjhZMf4G9DLOqSU>R3hE?JV;dOC-9&N2TKlIe)}9Z~Lc)VyAWjVWIz3QH z?c&Ss+IK{tV1<o>wKpc)iW6-+9l}+`VX0D}XD0!`9`N7>QGFjPD#&g0y?846r)ks} zNE0RVz0aBg$-)5G5r>~QZBU(>8pbLrhK}1iqN@yd20IFJBQtR}n%eIf<3Dz~O9>@T zM~qigQAb7gS?XlX*hyb`V<?~`Hg?x`gnGP^Ayx{G=6k*M808RM`B*7~%n6~!`a+6| zA~PQqo~xmnN_KJqE;@Q*Qqq7=4lVj;86wCtAi;Qecz}RH@T08xO8F{~9(qiZkeNAt z*sYK!){P8wpBf!a0n#)fAtASJqt8M@$Ii|NgkJJa`$V-|22SF7kL$U;p~G@nQ`pbh zzM-JoM!%*7pbm(M&zaupj@CMYpk4JwJ?8nvm+pA!fFTX~qwX;l&H!9*cHajn4GnoO zq5UrZL0$JD`VTCI`wVSirfR-JVugWc?Q(>8Ue~^$FOAlgiwDz%k#AAIldepfQnp6j zx@9E8{XvEL9Uh#BbuCj<{HVumBS8J)Y>e@B7BdZE0-6~?s~oLNc>4$+XJ&YYd%Tpg ztf4BqT+VMzuV_=cr-!;!a4&EE9@`lwzLxV4QBx+NsA{%u#~=;0i!f{uS)6bM`HTH4 zr0=46NRV4hXk27!ZWA<Y*C`W^T6Y|)X<QvA|3p+*E}xPCvF}SBNqbwH{b}_9JU=V# z^VQAR{q=P5R9;UgLVv48i<<)BJ8&thu6ZEqPPmQ@pJLH68KM^STIG0CZfuO>>#K?| zw`|n}eUp<@CAh(uoD@){G}P{`uDk!O?}+?|LIXghR_>M=Chh-E6w)Xp0F$E?o`(xJ zEhw~rNR~wV)<1?=UQM~LI!Ll{CD$a*g_aRH>|@X*M9>G(e<}bi$Y2`63*HM-<3QLj zi4SAf=e5r1*g|SL1XzcE_gbT{fT~M#hD5%=|BNpF_)eP;lIs`o=LTY_5oN&>JYk<y z472$nazK>#|AuP;0%<5_{KUV?YM<NMfYDsl$UdLeJMLC$MXZ4t=ALY7ek?Ve!5%+& z*^lLa@<`J+7oDP*Y>u{ztSHL-9ew^0pZrnPG@~G%G53}$EC3zE`fijp1ANhOu2v@_ zUP8jN+}y3_=QdPQu^s|gyRCbBmlj+OeF5*Y?VztJUyonRShQUz<YMKqsPg~MZC-zX zToQ)KadCgZT65cG)%FDh5w57bm7W(Pbn?uBDE|HtkE062|9U+*ujlJ{z9Cp(Yz)vI zuYfZG0)~QEn_bUYq^}9ZkX0>byb5ER2<IisO+PEKKFzQa&MKVo!236UKja(~xl-y6 zFm>p_uM8J=qEp-$*eouJ8>^M)SHY~1<}A|Ok-)tFcwu7EnF=PQ$mYrG{{4OR=P|T* z%I@|PfO@AHFK#6cu8O2vMZASB{?~DC+jdru(Uer?!m9-RKPWN(>y=HOz|<w6{8pyo zb-6ZERz|O@%E@_MJOk<Xg(-{v{H6wrGhndD4e+A4oX!Dut}|DL*wN^e6<ekDji`y( zKo}~iP3sGiwkaX476CrK<F6mYKijrrn(<1KGBSWPJ*&4K0N%=`k2;<mW_yAodwYfE z%QUvPx4A9n#`1xL?_hf{=HTVpoI-9qg>}S=$&ul%EX@|sUa~SUxOjB7q}OhGgP6wW zEoN;!2GBzR0;|jE0wWLWO_G+0Fo?}|$!TMw`@E`3%eIx5kFTP*Y9EkMq@wz`S=PXZ z`URQqdU5~}*Y#xSYf4I;{-#jPa+86ufFD1!S5baG0v?Oo$gYh2#f9sNPw3QoN~X_a z<OE+o$bDYtDqcyEPrZSbi|h8srtlOa<JIoidXxQf4E{18$t6rrA(NR~7|RCz=@iHV z|Jr{4d}(@xsdG%83c4357vnY_o&mVGYmBBgUI2QYKv*gI77uA~GDmoNdb*^<QZ}6< zGydoN^1<kp$6iJwyX{(HO3FR3a^BAup&W;*zRiaTvHVdmvG*1?hrFIHYHDgPH<ciK z0s;cOcff2ydh`00Jn<Mj9=k5X`U*g=ZeReam|V@?{({@K6Bu+)vS<JMDDZfbuNpGa zd!MZyS|A-&49LOUJ?-~?MPV+TeOxTc0tml+A6P#3UmB;TrgA_7x?<w71R0L6JPLLg z9XE4Y#>U3m1JUcXp03JH=%+IGn*iIz!C|~Yr*^=kR`64!)gm4`UYpZ_=SZ@z{`DQj zMbxOY$-wLO87Lt!y=>Zi-R;V`bWF^d@8wtCc^FLY@lrkWuschGnn)Jk<|gV&on1l0 zn=xKrMU(X6KlUjF$wp>*MB=A^P0k9K|E(Jgm8+<zhPUv`c*e5*k*K)1+&7j*t+DcF zmkfQ>oE(GuyQGg#L_D<V7)~aUi#D#u%dOCzi(CDX>e6likNqt?Ry8r=io<5lTMD_% z(J@|I7VJ+_pQ6N!GQI;;=vviM<L4k)hPq#gpIdtw9e$a8MRrs*+TK2GKYOiR)YzNY zqf@U!F}b(p_sl6PYx4#9RJEo%a=Ns>C_hp)w0EDe`@RMMNB(rVHJL>`A1RH?`Vr9J zbJDa{RZUtnWKXq#ovYO2H|YZ8!_02fO6^5!TuWq&_yLSCUozpx_37J4MJ1)X(^Y2A zTk8Z0IZpR25y9@@_;|z1%Zr=YgPXnaOd2{m7VIcylW|SEJFg^}Etq!a+gg8rL4aI~ z&uG|WD*OIs02v{E9kcTJcM69rkZlfGV*aTe8eKJl^53{<9@qaCMQAi~&*z>rvj5u` zdy}DfPw^O>emE|dWb^jpHC?45*{2<P-;3=%Uk89A?dbo0f`AyelQ>S+s9;D{VImcD zP{pl|@xw81ste)gp6_$Q#ztJ4ZRQK1@1rZZH4;7(y3G&~FB-k%;NbH6cjRJDd(pCk zG&ZUg&vu}W7pc=$_75M>lYVH?^;{)~1#$ZFolSrq2!Sjo06df1wW4E<&*FOjV;Z|& zQyjnV>uS}?z@vvMCC)Pf`>K<YvQv~;DH$D=l1pHg-t^<3m>X@TupVK<=*1;FPWam+ zcz8r;#IG5(Tu3Ft7#Rd?Hau4jbn!DsH<=o4RP}%vOo0Ud@4?j+&E{1SBNinl7L4V6 zg;e4KorkXTKJ*!NzIJvR#uVlXjMe(XYkb6<+o=9k3GxH8qloP9QKbXmdi741RqnHn zMx!T}LNgphA7_(V5?M(7;7!26OYkt0q7ewuY6k<{)S)4&qxnj*HeC>FY`?IC+i)*p zb(6o7eeZhwmSJeGO$qDQq$G}G7hP0Eg_^>&M=nPlcXv)p9Q%t++#m{v2&m?bJ6t9w zY|rT^eD;pq+?w3n9*bIB1Spp=Vv_d?f;IB&;}kKEsHEbMkdV7OJH1boACC~mcDdA? z-GmY+<0kRB8!|reZx7W~HCyfI0%wZ{0!)*Cg0eVK@!x~i0s;01y^{N~9~RmmaQ^Mv zx7CSsz=yLNwP!Dj9Gnl#SRfT5VJ4UT7UD6Ql9m=4>Xd2K6^^21$=LAl=L)J*4stD3 zRb?asrVr%gg(WRqe8j}g_vgP7eTo)KcCHVqCgp#8;Pz5gQ86{mYBwEv=aB#AmvOCz z#_*47%oX+-_9z_IPkB>9l#3E3U4H%#SeL_}qyU4|OGj@-A#6Yi+wZrHh{KZN$)TmW zBzY+#{pmBw{>3(h!`1lS&Q5g)stddAt5@KZ-Th=vPfz5_6EX}UB5SI2ZwN9nENsY& z;#-srDCq4CL5QIM9T)X)*zYk?(^ncxY8+r@!(CJ<s2IP57k^PuFs-f0$$)@H4A^0V zF7Z4=MH|-@_lAHcqE~x|@CgZ(ao;%^o@fGa09_fV#f+(`+Z*F#i8cS?Eds8@8`kxl z6f6yAhNomjg2hdT(qWhf#YVX5tk6e(%68ZgVE69kBD&q@3q>;+1J2}-3T3KK9WPI+ z73zErGy8y?SdWeAX+Z)?3BzZ^sh{^AtgCKHAI5!%#GPq_sNYp)CrVrP*UppBV`R7G z9-8t$9Xz{VQAnrXAJWj*>WulmK6Gl=2l_C<*SW(T+Q9=IGZ54g(Qqx9jm0sa2Fsz2 zzle$&*Z1$9-*q0p4|ikrCt>-aXF%rr`!xlkWOq|hQkx=wMy9SsSNV5?RN#W+lLX<@ z78r)B$p$fK(}HVvc%@dxPUQCARVl}^2B^i%#xw#yZM1p0wbqj0BIes1Z{4kSrVc@G z*5meVb=aWkv=&e0CW}Dnk@Z)2hYGoZ37}p!G&oc#e+MWVNH9(m#8yf#vn*2DD)X`v zONo5`L*SalKTMKMX66E%&E1HDbl@sK(7`xy15H`2I*^KQ{yxya`(ZXgm$#}&SRXEx zY~qbyv;Xr_YU}?Ksn)^z-ySn96(W4FyCdo1QK>ln_`CV*KS;+PH*^{fV2XE%QhSLJ z2ibr+k%0SVPzFSCKFR<3%WGQ5Ae38LceYYQDH5qqp8&x_UtnOYSK3+A60P*0EzQ`i z5>_eoXoD*I<6^vm6J=xDcC!TB7^=Qx%Hr*fWD3t7MKaBsO1ztojYu|TRvXQ$2Ci>O zBl=Cu34U7a^~q=%O~1BR=(B}J>sMsu3oy*f-L-9Uyq~|eEgACrQLytlTO<R$MEARW zf>huT+TZK(J2LU$0Vk*psT!2Zb)<oTprxVlyqQhrv}a^wTr(zgwV@NgqFG<{rTeL; zXYIVg&1Ns6EfIO`IDAO*>}-Ul#gmkEBA#7VcwCNXi=~Qr8~w@M;WnX}tXZm&4iP!( zzB^;R(zbZr$!dDnNGpZ3&F*KeJ;XqX)$z@(D|>sgJL9xT;~M)(#|6Z|e2V4T%DIV; zhL5tu409uY-uxl|%FSj@S+7{e=2RNt5pG5<$th>M=~zzvtn=*mAz(P}!+8hu6C2)| zzO9oqE}Ql3zShd*3Bt?vub+U5YqVJJ_k29A7TfiXrKP2?urNlWfu?cLulq@$@hWZ1 z1W6vB&#S55aIw>upWn#Hh%<Hu@o3h^`Af=wH-t7Kum#nGe?Y7|hP4eTKmC*fVKaUH z0R*iEfn#_Q85&u$6!g+Zgq*e_+Vwk@6w<QI2hs*Ie!9$bRMM3f7dM+M5_@n10n}GR z0n~Zpu1-S5ow7=bmgTja4kJfG(V7_puKJ&eC|Gur(uAU0^aJs-yvg2sH(m9{`Ni#F zJzEND_$N0e3z~)@Mvl>U3Z%muW4UB3y<CxjlDrw8wpX(dWbh1UaNT2`ELXa!<wkwH zuBylMj$goqnhXa&6>lJ)J#GeH!Urv8HD}GdUhfkg(K^#16aS*7B|3g+1*ud{gGP=( zT%^uW(gzR06Wln5psFooqo>$#n207t!Paejx{F%Vi8ZgQF+2XWF(80Pq)2Bmf9HkF z?=B<+9-OQD{{8!$oE#u7SZ#B61UwVuy8#X$as-mLUfAZdm>Q|f|1PK#GB}fQou}Q| zPwiB`{q60eqWSGdKSY;|K_g|a{ekIVE#`=4=MF(1%@|0@R&_)O9_MNg@5lbiqlLNx z854U%+gd4$_2sPID;-m**E+uT`~gi3Q@s}cLBq;QmSt6f@zUys^rM9W0e9`KC+1sQ z&JK)ER|lDLQYBK(I6B%QD`AOWEWS|y-7Xz`9YYF3n$c{XEP5&^&>KH~eR;csCAU)$ zi_4LzpNa*{=#1h>_vRpB0Kef#@HKJ!d49d(RJ;1_GlF8Z8y}?)%6Du2W>Pj5a&lUa z%eW!`5CPxzDFs^!R99hStaU(%Hz&ZZr>u-NLsm^iWwuC;Ff%ALR83X21YF7C^a4>) zuu0FrfVjg634iCKe`G|&O1Z8g!GBL@3p%j-bl`07KCCTF?(gm0t(s=`FL(nP0iKH% zmVM81X#Wa7pLuU;E*2mE7ah%`?GxSt+rF-1zpyu})$l4YA!0?2RYTmId{kZ%%JOc0 zuU6*P8RcucW)cXjblCX(#GE`N0$D1WS^!0i^Kd%=aTOvp^cNiOHcloBLZ!ZydK7iN zCBD9uo0U-3Jv)2H%~VC~u;uV(bcKb=tcq{d$xB(lsL#~PVVSdPtM&Ah%z^tsr^f4Y z#1dRWfz9CejDv>*U#*))rRE~-Y6%0^$J4Les<)8`MHdfCj;%ZQN&fZ_z2Lpt-7CLB z%yVhsgSOv7LUz3&_R&5r6}gz9QIVw<WhkH1z|RyWb<g$bA=c8d+b73JH2kkAp2YZF zvVl`><p&T^F<)O0Aef1xC@*hU9m)39A8ak<-Nh5PcvKX;Mhl3iR)C)?{%&?i2+upl zBVq$!HNlj}gs4!!b#QdIDYV8m#?C5K-rV>@U#b>eynlxuC4TWMnA#*^80@{8D!wX& z5D3YXaENA%eP1iybS1Fty4Ag}Oj4M<8fj8)Z|~E`1?=nR)d^qQ@w+v35R?>RDprfx za4vKqf7g|x=$@bVZrJ<CBHGMMu9Ln8v<meo(iNu1B+$e7Iujg|YvI|ihib>chN6=u z%Pk!oy~Z?aq~yR23@of|>>#+FQ=$orn=+4rL(zh4Uunu6iU|NJ&Y!YyK27Y^GyJZ9 zsrIishV<7B0IEO)G-g&?8ynd`YcB~M-`eK>GprA-^z*&lT57I^r+1+4QSW0{q>9NL z7$4{Nv-1>v6aN5O=|Fu9=LU$q5rS4mV<(}o`U6hwsrm|gZ9~eneqK)FX(*9fbpt2G zej98j2+N1(I-f{#dLBKmj{FixpJH_qUuz^!15wn|*4$x3RfsK79<{`TOkb|b4`%|q zg5W?;XuplWB)KR#ft81%DQj(ro%~96-HMVGr;>(NzxK4I<B5M1gh^@YM~elY5ZW-9 zXz39dPuHtfDVr7wE|zau8OlYnQcxCyVoXg>k9TK;=;(bOLYQz&Z_&RE=1&3?Jqdju zAHJZtr?xhp1$}dKb0;SjX3>Z6<v2<O1%(V%qW@X-lN0E=>&NmvfZa)CYJdidGRkEg z`}VYV+>HrqK&VB_adKX)?ZS@r(;yEg@KtLZjU;J)k&iFFlM9jDEby!!5?2muwv1Cn z-lRqh>M>ddrk0(BV5IU}9G<Fzsh;yzPrrqfbex`r6%1-P9}9U(4|ZJ#p3G+D>?k&6 zP*t#<=EEX=%h|XRx9W)=q6+S`?Wh*+`kI3wJo@Rc2eiDWXkAygmztB2;gS)8lnVhf zf&GRAEq20H7We&M^tY9dUh8{6f-Bw5&dHP-o@Yhaay!{V94=gloRx&~#g1nW@@Y~4 z2TMk`&uJRj)tkj&@_zcEDA`)8<6@yu&>rF}-EPlQD`_|Rmoet&a?U!af<waT&aaow zN2G0AO^_OrKQNi(xm^HKFm^+nfn};c1hlG}T2VnkwKj^7+v~G?X6=}?p&?1z7vs=F zQ$<C^#<}cLrb@<CR+-w*t<(^b|Jjs0m|(W0G7T5!>IcqZ-POl5)TJNULOVMZP=81% z^)D7*+#n%dc3xx1=_hzZR~~(B?@%~8(&j0qOM<sx9nTQcP+YhM+3I>-`I0BrbdP5A z8i`Yc_*_Z9`eHzG5X8aemP7TV4bm|k&Q@IOJdiy|71C`|?4ca8J{C^-S8mr!1PQ}9 zYnp!hgs6YKvQ5zUe*e??QOfBH|8ON-Wdy9~=TpkNv7>&udAMH8?wN_q>*NS5^si6v zHOCoGC7lyn{<S&+t*Cab!2%~0;UdR$kl$(rhbPk!FB4P(O7zQXx)l>nDz%sZ9$!qD zt$T=ptI8nhWjh{j{yE(x6e0{86f!Qi^lTUewWe~pC`kn3yF$)1!}4rgIxH)NFPQvk z$ysU<hz-S9{ArHcyWLI6zh`6oUE}p%3j|7wp0Q<GmF9!ILFGlB)^hf^4jUpV3WKjK z^O(XyK&BzE3^Z~WET6|u{_T1N=!FG%g4(4wY*cktLQr4zCTyK*b0zKn{<r^>j${3R z!1v0Smh~U#_uoIsRsrJJ2Dr%oK@9&3-^$(t3My55BkC6affN3@dMRL`tuQd%|9`)m z5C<yH6Uo&x^1tIzX7G>WoBLz(60840Xd8-<OKl^Rp&k!Dy(A}iIFk#-)-LDiW4gZP z1ef{HL|s&CNTKc3)zHeKB}6dSbdY8Ro@&7b4S!!AK&&w#m&9OLak3K3&ClAeWb@RF z9275mV|gqXuvN<+9iAGjb=P#!C}aVZk(}PC4eB?f)7o~h^9eEQb~XgJGEdTY7?C8- zZP>|$lgjh56~_h5PV;Cgt*65}m;Wub{IM7S-R?J^XSx?~=|EHJ1|WUnb$^~M;5&?t zh2^?+U!A)};+toGkt<rE_;~Mzg^8)Etc-xofP%T_vGxVmOw!mmSS*hfC))ax_?Ip@ z{)haAhFyPX!k)23zEAGW(vs_+OM)MvAR%?>qQv=&0y;XZr!iwID$#XO_8DQEY_#T$ z^uI*>yI1{$vd4JJt8<81XzS!evR1B|>+8u2NsIw)`!hlm4E$+B-S~ldKnjNUx8n7f ziIFB1$Vmo76k34`H}(8A*ZNrn%uV!-k3-%RM;^f5JnMKGUu*Ylat)vSS`#5FF^?uH zl0jwWb+4wPqoJvJ$P}yjS>#(EnTE|wfAr&^9Kc2~{;GBrP_9wC8%f_m)aJ26ow@R# z`}-%m%Y>;L+nb);#~{o)kHqrD*f;~}`91(6VlVfqs?MWv=lE}CrY~T_`};*dau9nV zG`@p0AB@RxE^L8+`(RvPe6>5l?{vP=-_z3{c@`cO1;EQok0Fnad*dFg9;=?~FH^tk z$|_kvSGVkVGtjrb?y-F0Ob)cY;|2-~ji>XWp`q29-r3l=x$Ws(Nh}&Rl<Cx7^o4IL zOif*#ej~(0Z2;h$$bLn;>g={MtIb@`_;+ZZMuz#cn`;^x8p_&uYUBvXV7KOGX8~&3 zp6P^%OuAd|Xev9sUNsX*?t~1xj7E!-yTu`lUNrsCx1&*aclXA1M$_YkYSqY{s|kJp zQ{T@P4BVUO)XJMG(`jYn;Gkq??lofo0_rK7MsqK3Dr|z8@eInN%q7x-|3k(AixtbZ zCJ+kE^Xc*fn=X<`WAIt&<O4keoB@F7(EH#@-*h$J42%OyVLdWw&D$wtvS`+IQJu4_ z>^wZ#$?#@-YV3TdUo_-Q%Xrx-p)XTC@(+CCf4ZKos;a81OQ1=jXP-???f|Xj2tzAA z7M7`3do4GV-yk9ayx)iWyx81a64J-(DT{h<k|z;S(LnA<Fd1hQ-f4ggD_4}I+x|E* z099FbT&AYy-M_rN=XLr(&j)H7xAFnK6aZ+oq^zt-%l8rQ!7+d!>V2rZ6a>re)og*m zLeKKy8sML41H%(eS6jEX3{%e@=hAGyNs~J~ANhj#wIcxpP3xwA58wnADF^jkxu0+N z10|QS4b$W0Mt<GC?Ck7?1rD>S5!i#NVdCJw=#Kxfs+rLO%RE7Gl%=x)rU(G5FSTI) z!2S(@md(w~;Y5S6va*7Kft5St0Ky~oMuWVU`$j%KKH{<X-q%xufPB$hrIxq_QmD~s zdUGTueN;?@M#J$7JQh&9Y`%h?c(J?8$HhIwU8peGpUhD$weIc_ZCrOL{XQOmhWzNd zosVl!baSKIL^&9R?@9e}OrN2!pdfY3xoLMGI_rF&6j@wU%;#oSEm&04^X?cJC!dwU z=WW59zN7y4EC6E_6=Tuh_~J#YwMN&ojc(x$=PEwz?P)pQX3vR=d0p=v?sa!^5|Z?p zr<<d@ndx;ZIy#@tB7T2Ho8<=U@$_bZlZu9m1wE9``=kozJDr#YfZKd7x-W;Nv&y~$ zqX#aY-las>Rx<HR{{mTm^M?kBC)tF4(B(bd;sY}EQu6xeK%G3x<FR#g^ZCm6bx`m1 zIr%)yZa38D%yU8@R8tg;30CIk!42P<Ec7?oBBOu;5?RFio3^&YQRnkf^1E-zk@D3U zRMYY~*~EgxLByX)OR<RqWUxj6x-`$j9q3^hfRr{Ac&dE)@#5Ks5sPOJ@otc60?Ry} zICQ?sLOqO$w%ilcb6J~7oCCaVjI206$N0#$1;$xtRD^*a;qd4wp7q|`=}N1LDL`{W zy(N|0=WoUE?Ei!H@TXh569+6gN-m_aVE~yUsm7V>-U@f|X6);Zo)2hwJt;8>(9=3L zksj7?#WRzJ+ZjN5s9l_iK7wVBqg;u8LO$LznN2P=S!d#=V{-vmO@wVAA2G23U~V~~ z`?W%P#AgfFg>ak6Cjd+ZnoA07J70l07z~W$Q9y(>&yDBa+$@{9JG(y|x$@D3S=9Jp z9h$HW80<a{)SJX(uqC;)kClcw-@i9y8i-BXsG4KcON1?zW_j5JJxp0vc5qoO*rgZ% zm4XSrCzh8Ps^>Uh_DLBfJ?K|LEY)a*4tJ4Unm_-fVRebe7osELyW_=n%e~Vtd1CJo ze8y{jko_+d>$kziCH<4^b-QIx{?hEoc{Eq<??Lu~Jc^KgZE$|CFB~n6-L|BZneoNp z@%rFq9ff$s$&P}J$okurIU+Lr*?K3SRAk4{8%Hdh^7gQ3-m%v8OjnOE>+qXJZY>@o z!(^u(2&{mNjC0Sia3ZC<PC7z-t2YGED1+EMBqVreXOEFJ(Ob`sq6c6Yq^7>7wK<*u z!-9YogaPQ%4`5ooI~*RP*d}h6+D%=z%;+3cR8$4FdL;43lR3Q>mKI2OJbrqNjKp79 zLxDLp*vuxh{kPLc`W|Hr*V9j5s1kJ0-r13mGs)bH9!!5HRm7sxL{MU#o!d%{3J-q) z9k)ZL<ISd?uBv1dQCxp6cpuG@NBczt<&y|1f`ni2$3SNNC=I<c*Y?(R@^!@9#=FL& zRn~#Eet7Kks_O2~Xdh4_DBr{jfbFpS9F<5=+B<#BJjD5`$%`{)ZeCa<cc;Y9sGPy& zJnjy%Eht)dl<nRJYDTQ=>_#T|VUmr@z&`9>j8T?VhnD|hHo94bJOi~55F6H%5#!Iq zkO3({lmP2=_c%4hpP==l;maG?xIZ3I=wM{n5n=PuXDAKMb!Y3X$U5wdCboxl<}PU` z=Q+X@vYA2p0%2A{V~|dTEbx75?@^c$g7faNA>LIR7H&Ww@AofGAAw%)KnEo>l`T)F zt#QP7Q>}(V<%`$b*KeurBP(+D;Vhi)Sw8u_i4^j%z_~`a9k{yPVP{iFR(Pv=Z7Y#J zp3C0*!xk@cH@8MYS-3!5JXtNT-nPB{`D?P?;{@T$!ihsR7lMX#*@zjn15RkRKpy5V z5i9xJj=b#EwJ8i3QJ%~;e-YYIu=FjVm)eAY$W5Hj0|)v}-!XXN7A5>3Lz3Rm4TPXs z(=~dUMH-ub5CH%4u_#DXPnRdJPu5QcL;#FY?1?I?`7J{UM{$P5g(7sjZbC|dsZ=2y z0XW^CnZq_xJ>=*qFyZ^J?fnY*7?B5c6SL2HG_~DH<hFD_q@@*ur>SRuK!ZZYF@MzC zR<9*dwxFd1D|vSnJf7;v8p8t6dY&a1k9t}ZA~n7#D7%1oe=d2}^~RnBqqmeN(7>&_ z92E+eci3IG3}Jw-2gXCOW}M&loSkw$%yP0;1f7YQuqUvnhjPX-_@h1Y<Z-i%Z_drB zK1atI^R61>UYs;Eu#qBv3$lb;v}-1I%M8IZ5yH4vDZHA@F{4yxuu0F$W|)~qJ3Mr7 zZ*OShVWG!Cc+^@xejqS4$XV3H`?<WZZ)@r79tmGk5_Nt0_^2-)+M3&=r12gWk_(BT zs`UzPFjj4=9U+RRm~3*+#Bq-^G9vEX@$q#A&v#K$)zI3iLUP6;4J<Wa!T)*?3B%;i z8KXxJlv0p54)FIvLH0$BU176^vMW1P;W3*?7Y5JhO96<W7({OH`>hfK3n=tsch~nX zt4VojNR7D$rFR)T3rn6m_dnO<XUrQ_F`IN>@9$TZn8JGTqchZ^=<6v$%tD9aS12)C zY=Bbx$;k;mK7PtibD$Oqa6kts6!pFB?HSv`F|NO@HCW@4ANMQz$u1B~0D^IG<<-@w zd(1V)ywod&NNu-=IgM+_Tn^|=6Wj(pIT$02{5Q(FqV5LHtfRKe?N52)gwY|w#>^() zSf0nmJayeVyw5U8&pH4FLMXWNUr8S&4!9g(JSxq4`XDOX=bFroro{>SdJJsKUujaR zv+Jy5L9R`Wjb6L7`Nt2vXUH8FMW>HO?K06jSUN$m)tSQ_h9J}ADOBN!2+2PekZ2n= zACnjl$#I;Z4L9bSBCS$#hbba$p}H)lsW{fwTjw3uQM~EN1JG+4w*eItX~-kF$4M<N zcz+=^^f%Afs-5EA!Zk+A<6jX0v?fetg`2Kul)TS=0mSuTV1`vk)kcF1c320%(5?6E zHtEv`A`k$t7e%OTGXxiy^HpOs7{lqftG|<^Wz!9#EB!-RMWsNlAZ=D^(EmkTwIUrP z2Rgp?K*bI!H>zl>v7V>X{u#0gv!bHyP(3x$^<k5eGUcYea{20tA85EtYxGrQG9K<w z)6H}SK}^QU`<*}|)txPS-mLo~GxnZL@aS+pIoy4>v)sG~6mZ7E`}W3!%)?M*L0ecf zbj>rgsWkr_+7EHnkTpEdU#nN9=NN*bqKc>NW?-rtYjdvTxN&@IEY{1X#q%OoDOy}u zoIcDIOhDqobzWawjFd!nx{1nUl8zKe<emt3=<4bDZuP-R2>#%?hKZJ2`XxAte1)5u zaFnYk-5$A?uP*3@(rHOl9M+L1#wJ}GL{KEWRux6ffEk15_2S%LLZH>?vMxn`NFuLr zNlkrnF`g@?g#Yj`86i2=(7al*8q#EVRMc8h*^acz_vNQEz;N+mtZ!Y`kk@zSs0u$V z4CBfr{O->2$?<DqO4^kC{X&cL^6TEkuJ!V$NX4I~`S)i5lXn}-?k11MW}0siQ3w~q zn!S!upoEq7Qwu~;(tp;qILLXoR&t|s2UWx1W~TOURcTTrxi=7=(YkP(s+s!w_J6nv zwL^2N766n|#UO;x$V<Fy*I89Zv5A-jXuhR!FlcSB8Dv!+^dgh^_)gv9^qV*)CmB#? z=T&u~&JV-|OD4Y&(ea&!Y;DnU^^M5!+Xulqz0P6kIWpJnP{=(W*MM&DAzz5F%4Nk7 z^tw5(SmbEScMH?XTv|KZt(Ox(%x8qJRF%28zM|5^VDE_jeE1^}cflBha2W2V`D@5s zypI|r1lc+f@fan|=9I#{yNWN_;x>tH_O-EglsnIRZ0M^F@a~bXCAz4H99-qGzCL`R z$Xs+Y+drIWjz_r=E|Slh2({H0S~9?&657W!<FOLD#T7Z9MT3N`j%i;A77P?qMiD|K z1og%BltG3zYh44ldd}<)bw%3DvfYY`=9W<9_<eY>dinjZ+14|iI?#K8Mmf-Sc{u`l z_KnqYJE>TIUOM<#!=>Xal_ag{U|Lm6GcyHS092;vRrOrk32bRO^7b4J&39@|z5;_* zZV#JLlG-meGc;h+#r|=%pP|9TU}}3~hg1c?fMk&8qHp5ddGKO(JA{f7J!Sxj?e5s( znCIuN<y|m&q6ZtHjWCI3o<`m7DbX%|zewK4_U;fQUyTkUA#=W#6!ONe%RP{7y7V25 zi$8w`7z9Bh;dSX1cLTL6QmC*>Cn1#6#oD5pns-;k29rc4iQO>5UIewp#XmOfi1-Ik zNYWU`<(p*9nU=IGhDS_r$NIe(2HDHbc$^lrxG(mHT?fb0*4^*r<%}S}L;4ennj#`w zV`p9Q`@NW_#(P5<5f7&BR6WF^V;;S`D=Gp?4$%m`Q`9v4u}RTabJ*7eph}nDc6(oL zTc;S-*<YDZPPElrhTP2?A#IgdR&>XSCdQgeaU&7*3G@w&rA1gCfuEe1MR96<XTe*P z|7Vxrk;Yxp=5Y>)e{w{95D?if;&-=Nx_&Q}ycRg&pKj!z%W<Y5_L8%oGq=`G(b_8f z#gnc)Q(3_;af?UXIUG8oiUy_GEd9=`)i#~IvZJ@rgD@a$%9E~?`r;hoZqM!qQET>o zOAABX3CsW@p#6$D#V>p?md=Ux33*B%68X!ZydMmd;p2Nq<eU%41UxXSZWsMnY5skJ z7V+-veOd}KGQA-;Cr`K0EmmwknH=YjmJzd<ho?OfLarJQn>!<nFeon)Id7y2rTbTM zP@K0bns4tPohkSpf1F-OzdU0x_)T(uKfMn@Twpc7h<~o$mbF=RusXrH7R;!*CWxZ2 zTC66;fy0pd)^=Bb#fV7d5e=oO1(?J9Co}JtZz}DG(Fvz5%PBKXeHso)Q>d<BxPJ#W z@1=<9k*7_|-dm}YfL%wyLxF>C^!P=c_TlxKkmKpiuayl=ii0=r8N3ZKv?=_h$x$N3 z?mDk?8*SdzX_GfzwJnE?WDyqf+j{WC{pHMSGT#iMnB+v(r-YnO^&;~-0NDy4>6$9R z0V9(V?DF%&J8U@cdzsJ2&u=lX^mx#Ns}cTwAHm+mMZI~O^BD=vimvdrFB$5z6C_C4 z9THM#@J(5)ig8p7{Jo5T0uPW^I8J<a;$GzV_nFvG;Kxi{2=dH@5<z^ENfIA&OH{ox zlWRX7GkLI4OOyH^6L-<&EpAI`3jGz-^$Pf*_89nmV-y|eL@n+&-dvp3AE--2{MY*Y zVS}*2z|n(9-|%LrWEyd$I<W;h|8q|Nw7#E-z~FiioZ!+#{`$xNc%TC!0l^fcVGa4v z0lp%Q4bg9L36Ko^%Cbu`d4IWXKgb{$c@#K<;S1?|3sFk+i?>3Y<Nmt|eol~+=53{e zMSXk8@f8<V41WdTaRN9n3qR$dOXD)$Koax0tglV2=J;;u)-q^Qwu&#l;t~5zjPI`# zshqfY#;8j(|8p5dGT>IItMSHc>otkr3g=YO!88N6|3gp!Xnep&;;zu+TpCf0|2x>v z&v^6JVCl<<2C-of3=_jRQPJIk#lMRUSZ5Ayuoz+<Zmhz8p7WP45|~57{sxn?Y7^a0 zo6<mp7h}T9gd_34mu69LLx?nJUlKc7%=K(-dU3JY^iH>ol#F|F67GLo2e{M^8*nOd zn41T36^tHw%*aJ6wo;Xgr(QY!m#f_b3K|+3(CHZnIV6>AoI7BY0}{w2gx|T6#f!2X zi$;9$FJVGOn*V1V{t7ttIbeX0DM%KOcLrESeSk3T$k}rx|1to1Op)HucU~LHx!%op zz6zL{&X-oU;UR^}a6+es{@4V@g#rZK8$e__^*}O7K)0c;P5^}jq7H!EKw!07WILPW zf4^0KuVq}<|6}j1qpIruZ(&8^(1(!j25AtGjzcLaA>Go_NOzZjAl)D!jevBAC?Fsq zDcvQF<Xt@9_&xZ%|GszJJH}-=91M?VpS|~4d#$<VXU_Q<!j@qX5B)X!2pssQ$0ILc zeQoV6pX)WBz|L%9TOE_CX^~4iu2tUhq9T6?cS`8M$;k=%^>k~^r)pfc2t5$sSndgm z;Cdb`4e><<?(d|Iv$EbVob4B6xG$F9tnRT3oyRk(=&-tQ=S$=6@H7`DnTEk~iY#k1 zkDW=Z@{$OD5yHRG<xV<e`}5pFab)D7IeP`23w*A>=4Uf?`9IivuNu52dd#BZD*wzX zVq~EtmHT(mlNU<?FQiHab2LHl>+tC4S-{QS%^O0YO^}Uxv%;1E4Dcsq=>l6@TOM9Q zpQA||cTNN>t+w>emJYRnN@qXXr`mcFU%Wl$n(<}#_nGgP2N6EjHWukXcjx>=w-zwQ zEZUz`RaI@KstvE3K!;$~?VTO3jRD@*dy`xMd8XPl5TX+oiWKo-tkV1!9sC<3%uvRN zCn^VCU;heO^VyFuGwn~>uPOKP+FzXBt24HDbUXz9X2r~_?e8Jv*Q>QH$B+X8iTh&g zP{{W6(a2<?zOdye0}Qo1Lx7hgY?NHc9Vj_LM@G$U^x%%0<B?1!XJ>c8-wFj)`KID} z8D=|EkD=aL%qJ7M(Nt^e!|xOUl?>d*TH4x~H=X3Kt#9Cs34DI{nO%C{Y^v>*0%r5) zjN9)F&#Lb;`y?#1f1W)mO^iPU?DExOATpp5*0(n;ZG;I?f)q0Z7hX34iTB%{o}O=I zyI{l_ac7auMzBFG+6}TmgL6x$uIRI8Pj`4c*m1>!i#CgFg)V=+Xgz;i*a0v(Se}Xj z(><;&qN1tkr)B+mIyM(VHO_X?;Wg*_i&$-JZ0kp9$S5=GiFl~@^<n1c8E)xl62=2} z<=F-5kOGC&O^12{vZgi_zmT~9j^w|7Stjzsg{K_quXo2d_Y{x3n!mWY1Z{;~ejn6Q zLdsjOgA?_=Hw5OcPq-9sX3~@q5BgVL=L)QUDxG%s@KRU%4LGChIZI2+Uq6h7C8={f zQ}C*$Nh>vQfA61idqtv-(vt`si+D+jz%%w@ewo}lWz%54xw+bE-Pzne6Pdg4=$C!a z?Rsuk_Qx~~rXMsZ3Ir?!4!kJKT<PeR!*2puvD1~t7f}&54WQZfux(Xt^U~HS{UE1h zRnZR}kDV|a`Do$uT(gYd2&)*)Ck<O)pGGKu`?4Ab8fK$HW8S}i540#OjI8??50ab_ zuf(ZiW!lJ-l(<?N+%FwlFLrJ&2XQ`cZmkVulA|CzXz&fRi4>EaQax^dx3@D@Z@fLh z)NhA_5`<V4fAQx8<O~0eB4#R&6KecJ*QYZo0<ToN@nmE+(|gykFFYLON10}$*<J>w z2)ij^DG#;tf=qQ;+3w&?=3Yyc(eLceklhxKjR9b?cu}UoZd6fOncdWMS=K0m`s#_> z*Ld1zyE83UOZoZv^@pqVX$tW0t;fC8U@UzGCK@)c-+i}YbI=#_nD?i-3!fkv=IUb> zMn<tFyF;W@(A}O4jE^&Ts_I3;1bgE`<gN?2%oOt1g{Wq3|6SSs^^3$aQ5Y3tyVA0t z<>M9{f;a2>7%;iL{m(ajD^3Uj?Y6>~ES{xg4a3Yhb7j0DoPL*QZ@WeYFO9awSw!5n zIhHwuDf}MdLWDw=O7@dpGLUpzzwhBcF&bg`TsUm1M#XV;fpcbdL@S9{B~_LOy9d|O zocjnG?kecECTz?;KeC6JF<iy_J|!vnKRm*}_romkzuxXS@gE$_1dJzW1r9NP!!?M3 z4h3QgpF#b-p@ABTssX*xijYON@933SBh|8TbWgT1WEx?={otYm&+m1f`DX{O@?%Fj zRQTtL+c#T;DpZ*)F<g^k!1<y*o){m;8WSP;BC~DBZkl}OmaqrH<7N(GS|nudJ#ooi zBczmL8dZAfKZR!b@X4T(hitb!R}_2~RiG9_XmoJkLLE0xl-eJ|IF0wdyt10ym#~b) zHHJk&>OfgqqRN)8Q2pn@T?2Vgqea#-GrIepYt(}~%uK&bO%ErS?yi@9*YKBq2GX_U z#^>L%N1rFXtoA{W`{D@~x`8gM!Iqy+hM)3Vy(;L=d5gk@ZlR(0`P(VMu+2nWl)8)u zq9vYMBd03dUANiM;29tx_bp9v!&*891_qMu;brdf2^m_MR!5cod&~Bp`K=co;HKpH zplsU*xCYOj_2DR9S|6=*EERvYN6E~V?#I6KNQioa|9x;#k%^GhQ&UiYRN1~UQoyF` z9&M^z(=LE{`X~tbk29WB@bza$6+$}>xz2I3sU2LCu(p$-d-p|LjyvqmcyP!7SOzZx zPD{?QUT4npEEWjgg%A5jV(ns5*imQY;{Jz+0^?`iA6W^i$KDaiEPCT$p!f6YR3tP^ zikK*c?}MC_dsr0Jf3ENLa{aLAvfw3Rv25Uq$$8+E0vH6a!w4Rkl_E7x2{gcEhklOZ z9YT7vzk`yh#>je<Rs+91*6LiyU^bh&5N6x7A3STLUu~`&TpKq)edn;M$VcaJy3O|s zMxG3L2&0$;va1zwu7s!t><wlkvds{9F8>;qCilA7sby;_#9~}%^Lqe_UWBnlnv8c= z_((ngO|e|sfAP$dh#;b=bvHLRXDgW(K7Ho+1cGS<m%;tw>*k%y2Ta@qXCA3_a1(t0 zx112rj~?U~XlhP^@b?FPZEM1(^~gAMZLGbLfzVo;SuH)i>*PUC)627c?^bfJ$1d9w z9ANdG2F{S9^<iikDCS8b25t*qpDh)aHCX;^a${Hn)whQ2k9{_cJ3P6dv;C*0->a!f zoNItSO6D*QMn(@I7j%XGjwTgQ*Vb+XndE5M+|!33oE<Ts9fZDTTax$-SMN0)egmZs zD@#j$etxA!%{P@FEys#LOIq@pthCL|P0&vOUNRd!<_%~Cs;;a&yppJYy;)rT4FseQ zq&%iqNUJ;e`e;C*08oEEEAL(?%|8C2^8lyqzFXV<RiE>Xf?l`70bXj;>Dk$<#RxVk zz9%*_b=?v84~UBNDvfR<yOorbeoYQ~<C(<x8&_u0f&!rsA5BeM>YXua=Lb(f*(B;K zc**Uzy1Hrx%=0xr^a5zWX`h2PBc=!M=?R`p7{{0>Nyj`NdNyP%<9AShGG&FOd-=oX z>IdLy*M-(Q$pNkbNgG23ibBVu(cVYY{cp9@)Q)dEP$K0JG4y~|s_r0$B1TbBa38)p zQ<Jx)5%M_npd7R_el`7xlK%CA-LlWs@ehD?iHM1#PAINGE43X|wx)Z^iJ6(v{b5+* z+yq)!bUGmagqD`}W}p1V@5BHhXnVG?Haq)1KNJod8!YZCpna2Kw!v*{_G0}j6wZgV zdy`d20~tWU*>W#T%OIB0$&d|3v3>q~&<6vq7qpSTc9?7N0-XaMH8|y_%iQ+XMSBX- z@_)d62%KKZoLUZFXcI*(s4?;HOkf0BaFI|9mccYu^c)PM)$9;5CKi^U=l-Sv3IWyu z6~EVfuFa_Vkwhn8CENO-d<KONXt)URISY|b<q;zi;|F0(7a*-ilB{b4igz*fqb1n6 zP%M;V+n=CutE|zcZUK=-+*90!vMJb^IkZR!bTiNpi;8*0N8-m<jpTs3<Ch~5N6`aK zl*l**hXI$$MCGaxLeR-BQ$p~}%#1#8<weWEy9m|>2%|Z@csw*2B6GdU+v>-kF5tqt z7+#0~he;`IO{MMOaRE@FGPMb%6CIVeOrZHpg<${|hNKe~ZJX%&pl;)9`3%AO_wNx( z#UuB_AC-xk!ZZCS)jkDK3cnJ@jrNG#`;3w(S~*K4Uyfq{roeh<Y=M;q%*0UBGW;NO z4IuptBoU<u4e2F@VfmXX3Fi}au@(yC5$IB7yA2)UFe<6^C?mq+jgNA==&EVm4RQ+4 z&)uDbB+V35osQO(yRIVpd*)^vU2XtShYevaqI6)oRV3|*QO4LYAZ3desxxpbF)Yh6 z!(%-=!jQDXoE6Tdr^L^`iZT51mNq6=Df{Nc_QrJiv)<9u$K%7;PL7gtn6v7X3z0*# z4Q~}vdC;k#Pbj5haYXGP9}P*h@Q3?aFZ)_y^SGocv4n=tyI#a1$@?Q~WKm+luG%rZ zeM7f|PG=raTB?3-y?fQFw^tob(>IK~PQ@%{WH`IDZ$Lt-o)v*6HDoQr;g+pP_q+n% zEi1qOsLLS+@-XD3x2G6SNGE{O1N{V%r%U8FXAyvn=*HKNWS9YcwyjCtwDyE2<G4S3 zi}oEO2RDponlSO+5y4ZbP)t%WWmHaMoXN;gTFby?uFnU_pL4jE1M4JFDKW5~1Y&gY zV`m!=`@$L{6GSO}_B`eSLM7KUG6!8o<ax!)jn*mrV|t@=l-Qo09VE7*eTW<mzPaAJ zxsJX$i_Vwf8CXEMGgs?Cz=paB>Mr;i3O6JPxhL*x$ckqo`QvHV95h`b4`XR1#{a%^ z(nkcQZ}AZi-i4w;Er@w-0y_d({kxv3YBCTCi70bA;VwoMo|df4L!DX!V<a#YO+<At zNckw?2Kx2N@u}}|%td151gc8-L5QzG>mX^7?*hAZ52&xu9-~g0s*X-gg}dpqn?t+x z+&A9G<eC>{&%_k8>dJ`mpEu^Dfj%oaeGG^=Kx3q;uAZYtfEZ?%qs-QWPQ?)J1d;4D zRW<n(_c#6>kWB0}@ZTWOXtH6<G(R8|)h~O=F1s5HTPiW`2bX1ojsy4Tg4=OZhAcpv z3~G~yJr|TKiazH!)l{p4U3rW{GNw-$7D~1-MJ|8EfhipyWEq8yiq&UBo!}oA1%ykq zer>&)ZVmF}JfUj#@lZnLf91VIJSXZR4;cmpPf9+EPlO>9`60~^9wS`xaV*KpZ!RqX z-bGLgg5A&!JN9cRsR~da+2Fo0^d`6pEz4LZ3TI8{BS{y@CL2bJw16Q3_a569yj*^B zg)YQl)kv)_U#Z@2A{SL60nUZ^9oR(=Kj(CA0<jHdb2wQA5NV|G+2<HC-`t@@+LW-A z{Ih$N-gP^e(A@L-{_i@Z;QITjlzX~nRZQJM8CAQIe5gc-ZxP**vq5;EIi<Nss89*< zInsUB{^RF5I%r`*88Hk<j0E38a6|c*rBM#3x!j)_k-ij%9P3LHKv%NxAR?ixxpui~ z>uF)_9--TYVmm`xYv!0#;p=N*;zQyl!6#fO9M?n==*kpyxIsp+&i)>Xk`SZVo$~9= za&`<TA=)r1Tu<dh7g})z#&hU`a5eN(8<hINGL?EoSVuO^Fj{r{dQ*6(GFDO>stk+* zmoX+HCdm9Ju<zqgCrjyGKDz@I*C>XzP@Rque;wQ+m^be!1d-a(CJ?$E{QTMJ+0!j( zOjE?Nun!Y+qSuo>>Ll;HH(6X12F;&&iQ7&Bvw*6$qlgQgMIhj1syPvPU~v(O({w7Y zLV2PkSK#i$Zw<+gK^9m1o2z602Kp$pZ!2QelO=%bKD%-ptf~G4EMMo>7ki(%!@OJf zdWi8Qb1P}&Lt|e?Mal2%+<GBpx2Iev8L|<F%?9@l-6A5`@<_<6unhu-V6op(&OLJu z*ObyH7}W;)@61RP;s)!~fqtUDu1<d}O`9LC4RUGyI&bj;Sv*D=0=BzNZn3?DDgsj_ zn>|2uR;qh=MSh8j=tSs%mHp7lb1GIwH8X-7ZqP<RXvBgKyOa2}5u%I|JO$QUZp@jw zS6dA=RyV7**F?#2G=&h{RZ2ffN?(pRiRT!ocUVe4ESg&igqk1!7gCR!TB&aB_1Fu2 zKUXVj^ETX2Fzbk1Cd~PO;6QptZWDI;Y2R3e`&FDRS0|}S+RLB|PElFe=Jxja61jex z^s=Cv+rxJcLB!9g-_^yO`xbgM{mr+=-3-uZozB_X#s)O!d9Mu|xj<f?BN_24tUlo4 zROI?>Fk!~+#mkonRB_ay(Q1Ei1~I-(@Cwpv@UMIeBf6^tkT9%WZ@ex$o8H8DCZX=% z()D0Z2w6v0#pqf%0gw6@<LEb}TqiJ<k!bQOWkp4{oCpDz?Y4s2{bQcS0Y4*hk8NdJ zds%7e3!n<Zyt<y>RdliaS&f|en9p;0y$E!Uub;8G5^<3qfDbfBOu}`{qPtZ#{A+*s z-@oU%{o)a_?F%hyaysepsA)o5$Hta`t18y6VcXjZ&`v!_<GVidejzQtf5lRt!G2Rl zA|1I$Y;IcagZ3YB^tO`?2vwIp7lUN=H-5I&wJx3q`=4JoK6r{vD;Bz_C|l`b^y-zS z+aycv&&VfN!@l0{*!})-5~87^D7sS0WU7SKnfcu|&Bx;(R0@n<bswzQwqA{^z0f^Q z7rB`O?pp;SDRb`o(uAm8IoXs^t!I(H)1JH#InTYh{5_bN0gT=66ZH-jq{+pfi>ne5 za(DY@2V^TjR}A!2<X(n9R3#b<DpcmVJ2(Bs=cLi!-va4nK3$mIyy(QKL?=75iJAIJ zN*e55=aJ{<xeGGOY&VPLJ#XaI1CoSH*S_f%|I`k16C0-HCW?|v<HI_ytbf0f?xH>) zu^fTV_7hN~j?WGO(tRONIUbj(uc}dPdb|l38Bj|{6jDxPx$qy{({8p@vn{tB>4{3J zaM}<$<>uxFW^ew%rS{K_!#?p}m1exm29QhN)caEnCv|4~uw9p+!uVDrodupmebwA& z|NGPK3(pxV%1446OdVF$aAf<^(jQ7x{?TC+C`);*U0Z<=3KY?ray)?pTTM;v$~ZuY zC@H1&#}8m^ZEWE0>tiZwI$-iFF}gTW{Q9&H$kkegbES6XT5sZ_Yip{jOLS`ysnPZ{ znq0T*>@#<-t1Bwj0k}n3jTGZm?Si9(<EYt4sx$`uj1fp7?*NV3ZZVSYHf$9iUeWqO zMK+s4E(q`N&XVBE4G)7iw49?1+!(@K4WWuGhf#3+%sgb)VgDRi<WtjPiyV}T;W1aj zl|+JjC4RGOd)@H*d*1tJdv3??5-B-vR&K7XG%e<k5Qpt?skkcn<NdKC<6Uc?Uv)k# z__}&C7<Vt&{F93lf;d(*J!WiMRf0cjLpZHg%x6u{Z{Z<fnlkrJll@pjn5z@$Rs`=0 zLe2ySepgY1+?x;lOq#*_8-%!Gq=ugo`cM!+toFa~4PIwp3NoU0ph{b|SS<L@G`Wpr zULWyGA*-uLHmr<mV;$jKMh0s32G!Dkeg?$}<nYA&t{s!ORAMfC2ja-U?vdy3ogi@R z?vaT2NBUC8(Lf#>)ogOW0`reHObGU$<;5j9+ly)Ftjj)&&_KTP=9o4p)R$s=6Fp5E zn$Od>4&~nLr^>qb;r<}3gXPW+0_@CXaQqH0F$`0*K3o3z&;ylE)IEfSitLx|cV=>c zR$?XryyTh|u_nFy$$(iT1{-NZi9Y{3qy?BrD5?NmVEV~tqQQ{DSJOozd+5pPPtst1 zO`1E0Fro6p%6Q3*6I&rKAx{B?fSB9qsHi9$I=S0y#_1g?^wQGOz2?)5xxLoSqGCG5 z4D0dV?MUPx$DkO*iu4fMR%?|4iQIInPpdseOUyp#CucL)(%jHMv@(yH+M^8iv>MIs z&g=wDVE<TLR5Ud`{h~}~a(dbW>~9s)`Nx<ib=vzs31^m9R6zBsxN++LQwICjO?V;$ zieV&9?hW!lL3^sp`XjORawQCho`?y<9Sz$aq=o_ElWwcG*Ry94!});i2SVeePt!IL zFe(|K0#u|p*{xf`FmEc=t_8YU7a#?FmcZ;6>A9Bf5-E{0ybd~@@VuI;1`&(RV81_< zT&!N|&m3qpQ)h1s_<7CJ_qc+O@aX>jlq?g0Tds5H+ORWQYfGL-+7b)`!!p&?)v4A) z@Uj}|M(07xx6f}#t*R!6{6@^jLGoR!p1S($$(M<we0Eu2=bE~*vI3!`YI6lr*@lLO z@87?JpbQcr_fbB06ev~Ir%!m6IN2h3)I6bW2J-Uqva(;moIdi3ed`VxbKeb8(N`$g z^0w+dphI?dccrp04Mlr-Iw8@Y9lIaCpr{K;BCDq24<*`IKS6#R>P$lw4w*HtovuV4 z4(-<fJ?$#OU|e1J7$F1lBnXc~t%J9_=BnO2gjKE@jrQH$-%vche|XHN_J{$ZLfdos z0gHgwhJ?x$B-n*~KXh+iZC45dJ=~s(iM4~oiR{xzCmFupCnZbelnB`~6=6f<3igqd zuousbaTt3jO{sV)@tr9PD)B#ytG#~xn&>6b?@D~8%G}%s$%7XkIl>q9?>scUP`K{y zZdVTB=pH3#1`;>fTe$m?yArKRY3b`bVdiA&k!`b6HCC~lX5UD1OENOZx=x1#s#{vf z!p<U`;jyu_(w;cd1;ZJU_JOG07%=HFvaw-40=z{MF`^HaR-kX-r9`<Z8VrWQX}nDN zMqV{rN;f8gk3=ipf9PJvo!ju_nJ9AbQ4DHg8tuKH@&DjuYLXx12)OYBMY#Cx=X=<) zV&%%z1fdw2wR7GAf#eM!aPw>l=+XUY0@xeD5KCm!7p85w1UqvvQB0gTR^0~Nk2?Um zNT6oZB(i7+CtFm6^%&sTOU4Hnk$e6Ulj8{AFw5fNh_oBdkxUB4WenvPCow`KqW+#V zp%9OG73Qw&V8nm7kVugV`4>;u#DjU1foaSLsrs_CX8eo@o-!pU+beWREMR4A%@`*K zV^|Qvm6l?DFUKv(0nv;pKgJIirr?<ZmmG^TIaH4;O^L~ag$%P*ETKjSF>X*U$%4`t z;rKc9id*NTGQg;f0<Oj=P_ilD;qR~jnu>q|-O?3=N6eRoW;ABU*sUX2!lMnQJIb6q zhhiX0;r;y~k68e#4kf6~*WozkxUKW?yC1EmvA>i0`$HEaN{2O1P$YMNIQ{px{v{Ux za!9cxsfm*CKOqcf7$8{b+yY+R!KAnJC^vQ%g&eiD`dxevW}IXgIP<us_D}yV8vnP# zBuWJ4(|jzR;yXO+?Xl#jz{kt}G|>KYLZ^rVv-Su?K&80D>|Ujb<<<o2osmu9w*cPC zr(>eYdS^}sA8m5WkD|FK@uw7YCpwLG@&9q`|6NyR(3jU!!E!qj+hxF4SFn8_F|RaV zbMu_SR8dVGQ~5A4V^N;`@LyjP2!>_B+juq9aX&e-y_3x+(#Y}4=qic%BLqVXzGS+$ zq8QL2qXmr$6*93x#v%#l#4zuBxGMLh0}eFsMHW;;a<m-hM{~pQScp1&zWWYwM_8(q zypE7OI*;oC8J}wq^Gc<1yv>9S6ZH~2#h2fnFfF0yQ}nyySNehT6Nkr*@E88|5eBJo zwF*L$pc6doe05Pn;EkBD`fz-;w6fu;B#Vh~GBV7AIJd>rLP3Egzv1c5&wae6BnQg- zlgJD`Rf;Ln5>E$(C)25wFBl=T4tS|g!P46apXagJm#gb|Rzk!y^4^2?-P(>@p-z{> z?N?%80ICBIsunUxsC|V)5;R~$xx-tsl}tP#+^t`qNhF~EPgT{A<aV4$e9Ql^MhD#y zBHVe!?~;KwRosp1;n$pp(OJb+sAm0l=EYYP{)~z7UE{>W0<FQ3JJC=6;Ezs6l*?fJ zQy|^fl&VePcJts+UhY5{9Mt*6`HIHxJ{Y1Hp}_+G9xMhBh`CnXQWoCEyZnIK{0|u@ zsb-_liEmV#8KR><{4M-ATapWiLKm>r3uXnxDvi1V+j6=U<cFCDY)Kj{()MNAYWq3= zMd#5){A<|rk;7FgCE}rH4#rZuL~b%f`ZS1^AZX!|qs7TUjZC#UI+LDx!^OxwkIGcE z$O^<m4xBpC<et%^!3qqwJXKIlmpsVSkTCSai$G}nHTgc9;gS5y!V^KlR(3hDr2Iqb z#A0N4IIz8VEg{hfl=dJde*N{9{$h{Wn1A~`p!!7)lLu)!Zb@(f0fC2-^#GFeM3c9I zcD$gaBuG=$aU6JDROEfO5HekB3sego`JKb3Tk`-fsF!LzL<x$~(AAy&`STjk_UE20 zkIL$drm8K%g4PZi@b<UnUj5iBdMk+gSl)hlQqY)rn=ti?z6@_i2n+SKjHAOQneukG zbND;9SCc-kzl(1muVZ3)#>>1l%e=m#VJ(2n*M{#dX^qlHT`;Gxv`(dzx2}J!QG$M5 zVPn*8^uBUW>^pu}?un2xq-qapBAS4&2ktB!1WHws17@%YIB<Z}W`Lxi9$>GcS7nMm z9{Ul{<iI9$aRmwpw|M12?q7HX@i?>@)DOl%>WQXFFmp*si6=m4pn>eyzP|T)Jfl%P znAYbm(=V|DIUbTfJTfqepi$h<eywTqF2seRIU*j`BBerW`N}3;Uc#!BOsJD&6q{vT z^}`g*L-nZ%erif8JdFpx0ir7-IXZ4&y_?(n!A-A=6R}nn&=dAYnJ%CtK51PD!|P~i zx%aWuUIMyK(fTVuDAcjNw-pE-9nV|-oJ1jo8#xC9iHzT=*QWNz*w{iFKsn6%Ei-%- zH8l@!VGa))8<5SA3b|L;*1Bwteh3N*0z5xMjQ=n0g_gZfeU8O%56j$%_<n!^y-e>r z;heX{&`={|9FAAz&h!dF?QKia)6QgcT0iF7M@#Zszl;h7x$w;FY?P?}vuCKwX_Gzv zXa0IwrYP!A%0TbVk8X#iU?HP|`n=*Hrt$+2=m<kg2A27}!~i*@b2WctBajHjBk_8X z?L3xh!hD(ghN@eqA67yT7RE@}jM2Erg6Ao}QB_7B)nBt8A2&}JE0y7psbm@p7xF|g z^J2(4He8_{`jz>)0ntI)L8y$H*GZVkK$;*Uf$LYF<dHKmt+z%2Dxw@R-4>HxQ~cSe zPg{RH#Y0z*TnnI(-<U~U8Ek1u#LA94RMo5O_owfwFdG@<Ykz@{jK1C2iYCx~pNU;R z8fikrfD>WC*SyYH1`<iki-9E=i3rOY+&ZR9Kwe3x3p)mdFu7z-1aXWj5e|3gSE!gs z-V&RFeW7$0W|TZN>6R(gI3<QMz5|hmy|TEP5zNR)asu_;#PG0R&bK;iJ5|L-ymj#> z4$S8+kldp8vpMrjWZQ;W3|e*c<72ch5|z8lK2-8V^!lO@5NwA#cX|b6_u}s5C?@Jg z)jWFbBuQL75Kc=J>FjJ-83UWe9n%O|?84*?&_YTyuiRB&fj|9%)6(Piq2>|BOcy-b zDEF+F!vH6dJtJk}(29SmnF|uDsKrc3s@YJHl>MRL68@JeVpO26)MB&AfO{kCfn}i> z<VP7o^abYtf_7A6B^;LFm12atIVk+_5Q4Ufd-<B=M6xlDh}AEVRFH_o^*3QZf0|~J z<NHR`K^IE>f-5;ek06wW@EUDv*R(JKf-&2SVP8Q+OUpb7<Tpu8;+Tzj8hw+^Lu}4n zS~KOea=i+0N#ifUu7Xbhi!9KfGknsDdw3q;%t)@8N@Juv3Jfu&VI2LTHJr(m)YK^X zNWm^s6%`zw%I{-iK}nR?M&3kS$XDJX!5Bc!ip1FKJNeS|TUv6SxIOMN)p;V7xLYtq zwMNbxgV6$1g(rBr(ADXPX9)BK<5b@&rYI9BRMUfaY?J4WMNua^#}%@X<fQmCR8&Js z3soz(%ns(CG2^OF*Jvok^vok*_+V%&hix+BmCze)g)fjQGKra=CNRB$Q+`moQ^|fV zAd}+?eE<4J$MGmy_%)|eSFS$qx*L)<nR>?XGtEuxaE$Li>)MTb2Gu%zT7ppLhQh+} zf_t$IwU!2TmAI<@$SkwSg@VX4tWLtRQ8Lc#jeb98xO70So!!V0No7T;1nR;|y|=}f z@;$%Q>js0#nc?x|L`thIlv@Q6jh-iCKUO?iW%Q+x*95Q#o<Fg;eVgsJ^>Y_l*Yr;9 zTm}OqDw6~H)dQ3S3luZoY)I&P|9*LdFA2qbLW#xE3$2P2mu1!dgqAAcvg^4RMqg2H z@&(&m68B<008{+<>ikEmR3!m_sPA%#W|f}n0>Tkz5MLnPj$#X2T6D<Q58ee*WYo<* z*J$z_4e%T4kjE8+{K?+S<EbUBfnl1iU%lX3`B3f?@5*`UN_97xoutHe^QUL*3J-Vx z%5e1_b_jV)PQ<b<JmS+im66RXxC`BfL%#`IBVR}hc0O;GETr@re{Uy@`cNP;S%H|r zB-?41i<N<LdXblkAXFk4>kNutDgYbe|Gmq?U%btuRBIDD7+Q@hEtZ7WU$2XN9`V!X z<_dC7c^+cuO@}5^XX|~KIuXboP#Ex&__5_^V#^Wj(ILzeQ%rEXV<r!BN||D2ZN%p8 zkwao4Dm?_6i{QKcVvM*g|D{6WbLIM_R8&+R42R11idXUfO^*K;pryf@-*Ui)+F+pG zU*~1MiU@|lsJ!LdXR?(7G!(yvMdK08v2ec$%7D!bYJ{9{eGSYAJmJEQYQ1U<quX8K zzl&CV#eDr^%sP5;d~=9}K%if30LvZ4g&zqu1$(#p8?5a5TOj%OKQYt*Av<;rbI1J& z-QNNzLx;1#bN8d}AumG!Feb6*9{7jY&IBN_FR(iKyPqHm_e2~ZdTA2*|B;;j8y-<4 zgWUlqmd&4xu;?3T7DYdn^oZPDv<}D{I<U;h1y0|&25>$9?=t@X|2Cp4l84O!PT<83 zGfzpFh}U2SOP#X`BuKp1HjnxN&wzE0p5urh9xbS;`tb}n{bR^uSJ2wnrNajYh+0`~ zJt}WnJiYiFN$3cmB#6$-P7tLS8XY|XxbXHdG<^?s?n`8|wYGL@oUNUGojS4slEZ63 zqaR@YzXd&fByS!*QYR63T~b$f21qbwMn(>dnR>^yeEHNY<np@3aF7CU2D0jJ>7>Xs zz%YRqOVx`th5u$Q`-|?!MrIdJk_p)A6gSub*`Q|MJeOmo@t8I4y*C@Z|G3U!<tt!W z=+*0iauWO?k-27ptbE`>2I|!y$Yl?2i?WiEuk-dR3k$dP><}g<rYy9m2LbK#n`6b< zT&=%?*yf?QjrL1DfZ&>Sne#%zBn$i`$DFzK#~2O+4<e3*o!2@tg$7dkCy&$J+g|2k zf|AOS{5@|-FMxJJSVTmm@+s^F$XH@Hoi|5;PaMdB0ft?Q0sjP;Y@p$rn$p_t*5BsD zfVnFxV~K}CqgWGBr78RQ+>gwoKL^0J<(cO+r2Tr{R-=Z=GA6e&{xscBBQ$wTLbZ)c zh>mMp<@&APUZ3ez!ru1>Yq)5*!cknGy%rPoEEzaIiyIc?&96sRD%d)X6EjF@tDlBx z((k@(KlmQUf=~d-?AfrWmtkSlpntB@a8S`_-XODPDYRZ0#-I=UdDrgEdN={IHno#) z6-CfURn@MfQ7(hryTa{tfvX6Us(bs!LeG{!(R^Fd6c_T?JdfZrl{ausg+0KJC0sf4 z6t4cv44<s50uo4R(O{!ETyDcg;k0=O0eA=mg9OQL3F;3F_(JUy@X!OeXx)AX`#ojA zgJ|IT&6OG$u-nXMF?<s@gS`cN+AxHGr~Sfp7xNz(N>D90(Bj{9dEw3Sk6~k1U|iNf z%2cj4<9hhH9~PwLJPAVU+CfE>TINt1u@i}$@)!Tnvyoarv)kD$+1-r5tC;P9`fa(; z`;cUt=MnrPmgH)J88c`2Kvo9H4K8*Td1C*k-15V!SCa)D)B#Tr9H>apLL@kaE^uf0 zC2<<vEGuOgm?sBR!Zhbw3fqcN74TV#L@sg;W&~MMuD7egX9Yv%-DOy*ey{Mj-S|#_ zv0Z*(x{QI%#|8%ymUckKS8yRIm223`qfBDNH4Yk*gLyXoM50CX)EK*%&>;^3w@`VB zV?s$ns-YN9K9mQz@*K!+VF)4f<*|f5%5*eFE-o$yk8=w-TJ<9mZB4(eKWrL-l@<-s z(?QgpO<>}23gJ3G*eN1K3~#CybMHbQ*0K-#PZj{l(G`VC8j2V}&D2!_dkOJ&Ivpz{ zJF1324~xcat!I$`bu0EUvIan$4bGI6r$6NOvy7b2Sg_h%QtJG$+yLlQJAQ;RLti1P zJikg9-`kLsgP@wa-YkiFcB~INwgrZFmuoEu`%NP(hlto@`4)3gp~7d&k7Rl=c|RuG zUj2Y7o@p{DEICGwjuktHa_mvyeTdfQc)URJyu=V7pE!`_Oj(6Dh93FU#Ncw(0;|yt zqw`_Vi(Rf9(hfD?tH?F#IiyljQ3?2>%g)jNGEsMYW+sw?QbE0nMx_XZ_7TK&MUsnn z17G+HJ+~F0O3J3;aO(#d7X@<3{;dSOp$gd(5&M3C?IwI|iK|K_g{+Xoo(<%Q5=g_+ zxQu)#9vtOpbQMqR7vaO1n3=hYxzw}BvU!xXp1Y5(Mx0A+OQ+}cptT@3n+(=&acx1? zX|M7vQMFN4umg*#hE`cfNhAeB@SZzAkR)UGr^T@4kyj@DL{)Bd3K`42ftv{@Cky)d zEAT1FiCl$(bH*;N-R?P+Wr$5$@5NR<>eD&B?%iXiH_Ow#qQ}zl;~E4wI>=2~n5r=T z5dc5;YTUySwj#9}bG@6=`;2tuoF7hx+a2Os+A%|;G#bGvj0=|6WZC|#5lbg<45a$> zQ>^OcQ$$^81Hhj@1*8nZ8pIRSkHn!)wW492X_Cr2uH1ZY^>q;o)liFHxn9Z|llHeo zTi9jR*h+=n-{y=nHY}Bl;0I4p)r!ZM;Babz#ad{2sICI_PdOe@Q@A7w+^R@np#FaM z?j7K48!b@0J4Gj;sY>QabWV$1VYktMwYGCOih}o@T~23Rj1!b)T7bg=B^TbdVS|q0 z3uy0Vz{Zovtm(h+mn32eY(+nGT}bJuC7*y05Rl7cWI7CFBoChBa7|`ORq;C_#L60T zG!&$4m?Ppja*ezgQsu@MGF?iyMtW6>7oko6&aXkBI!P&z-5alamOcV$X}2?VAS)xn z%s1EhMs~TSPP!{ng0+)R)ANTJ9)P4RDkooz4Q41icDJe&1#cj0+$AG>EaHwz#Afu7 zBnzeyFXqg$==j=721WM|q<IRibwWih0-Fj#hGKN-PP@zL7d#?7qY<xyqhH{2oN6w6 zoNW9U>D8lJd(nuEwn1G}x6FJ$H0R_^nJB~J{xZ4~wuJCx$y57m4F=<T@sPP=a&Gfg zNi=A)5Z1T^N=hujp43j*R~CyAX3CAiJb5l+^{j_js<1%zN{u|}2I8C+oUkWc?z5p# zydO+OLxcR4c8YaZzD`);jkW4Z&>nx?g*O3wUYt`_1;l1Bh?VMR!b7I?pFXQZQcgkB zCw;(BZ0n5N!@zyUinkrV#%oZAgw+uSG0NO1y#I-tWu^u5M;15YCRT^?C(o;3=8v$3 zH&RzD2O1#|!?5N;EhVVbRcY+5dopeiUe&7F1WFGwN{9L07nwmqF7F6FudV|Etd2$g zu4LF$R*+Zr$<;?ma1Lg%q0X-PA^aHTly18$6(|{<#SsR31d*}U(*=@O-)HogL!oZ~ zDG@l6SSHupBKpGio^pcU&%<G8_KPo0kCTo~xcnRo#gbn2qgqa679z&JrI~)qBX1%r zP5UZkvhEK`+W;F%<YltDih_)WbN>j7H2<w8^Iag^CJNSTB&trQKOt(b1AxJry9O!J zf8yW=SXmS#p=cxjC=~YH77FVFEXeMD1gI?nF5HfSQ;z=c65w0Lo0%Tq;1!JgKV|nc z>`;Kt{d)hYX!w{0uy%q#(&`_g@|gd3A^&^65g}4|I6vRHz36Tu)0Y~U-T2hQ6Wm<p zDZ%N(=A-?#C4rrWd8uKsGBoxha%+EQp}~Gpl<L0rBH|BOidXOyJAg+$2Hq-wnOQ)P z7$EHmiNacc57?@zb$`A5eJCA8biA5s%fri?7#~05zEuhmgMdW$b2Ry>dvgRHi+IlP z6wsprI~6k3FA(D2y8QOp8!{o#By&%U3J5Y^0WE}j@#9xgl~i|zc1#5DU+P?c%@`N| z0g{S@4tlBC841vcf}LGlj8}kiGN-cMW|qHT<oA#SW-};aYk#r^eX4D(tUBr9m%|@E z62AF8Nc_1>uh|_aLjlp)de+UBMagS31L1fAl;QY#u7(|f$RO*V>*><n0wjeW4RCO# zzz3#oPz+5@{%)MR+6CgWL7)T$bw?~3Zp&BZ=AweXT0vXaOs|v4jEszb;bLR_Ya>z* zD=VwYu+|A~43O^u;5iEm3j>2)z0UFRaVWYty1O75jZt|l%YM0orL0^R|J%6doZc$6 zu!`8lCkUu=5<JZVlU7+a+Lq&C=ltkpSRce^8qT`ulbtDI{QVk=X}Q?nBNoJKE-w@A z3c3db2~njxd(oTm5JNIU^_2G0a*{(Xx^S5FXZWwU6Z-km#VirOni`^vBWFM@zVtv0 zwb-lPav@Lbygot=iUEd2uz}|ukGcv)`UXO7K)ekgpIJM6>k9j~P|{_F8Atmq@{L_7 z$1Cw^L6@p!4kQvP(n@JGI_?u9H3}>@PhoQYtdyYjMqFAVw4M$_>Qs22nj|A+OGp`0 zUAXh%V9h;cJ_0{yqG0`}2S-CjShNJ_@XX<Dd+O}$1iW@ixN9Bp-@H;JYv$usj858T zkrE{hb(JC(F5>}4e#T6o%0e(HF!AMs$`7jctbBd1?;0Wbi}5Pl1u=egUhQq6fW;$H zP$(`f-|L~k|Nn-{xKHAc#NOr4YRS1|Qd2mIF+R=ov#^{&c#&%no`1_Bk`eYZ4n12i z;3J9Om@HTyXH1zGz@c|!{|hN+8z<2+{o3@8lZ@CLY$+7vRgB0NxhOH>9Yk#*I>(Q> zbeI<Te2)>7gnSJT?67?)V2TQI^jt5$;&$!#7iD1<7Z-DE?tScI4WM9VV+(QZ)RRCF z-!sG}vZg{bFA~NLl9xCTyRM(L36)x6=obpG=A*I4CJGf@mYy43wvtjd_HM;vo3mDw zDi?)H{TC~jM?CrfpN(Bp{F;E_;5*_57O&mVX@HzP6TIQ5<*9Z@g-r#(y%b#^O1giA zQ+yGq?arCHdOAvPK&q3oF&@<QQ=h9lAbf1076w0OvG}k3hU!>3gI#7DoTOi>nG)5O zQ8=E`tDI9jmif=W838BB>^+8Ziaf(gA;MmhDmN%BUukh(rP#gIZosRk3q7atIAJWh zHV+NGR4<$pI8HUw$SB5i!R;mrzOd$dg5*ffX{9{dRtQ7vvCY7%Of6`;dOXJL`be01 ziqcaMA)B4al!PCSPoHD9ueiJ=+|Vz^XI(7ZoHBf_!THhXls^LM$=;m4vhw}XIuLyg zDi6k0ASNK0*LuSD0Wj@GRr)L4ZgkVw4dM6`jrgd})2ZHrT{B4kOL~FWECo6e+XL~5 zk+tFTP9UhQ$JLdK`<~0AKgw$)vPtvU^p`wD+@nP|Q&U=$ZG%@$d*V66zi8OKjXBhB z12EJN4SrOZNsjpO`OPUg{MM2H?qU@w-5}IG6cI#eUO;}wx1DT|zIlYAj-xE#v;_O9 zkMNA-#5#O-OO519EpYxuF_c!a)74?SijfzFI^|oXJf5>U#zh?`ta&Yc<tmW)7bpAi z;le88=)496QRVACT1cszo|V{KlT`pe$0|h9x;enKFCHxvI;8_{{2nQF1x%On*&}yK zNP~7(m*KaZ!q<TO{!-5RV6-S`j7RV~uM)#_{xf&o;3ipHPTsPHhShKm2G|K%|JJ*u zWs^;IAU5Fu6Hkb$r@x1XQg%gV5lDo#d;idJjG}}9)#!I%^W6b1&WF`=fUJEJLqRh) z=yUBT_sIQLRW4<8_=P?bX4+2`06?$~uEYNMR{7@=t{Asl4D27S6bWoah&ldMrUU+w z+k=>f62=??SwwD1a0%DHxwsMJB^`&2PgFyu9cxu0x=Z7}iq4X1$uWpSC<*jwumhzc zg7jnz)tGlh!e;PKd+P_k1U3hsF4vzg%1h@W%>HnB{dBpdrI@i@a!u#)rOP+w<m!<M zh1OcgfY_{Ay3?*iKsVx)>{?5US!T@XxlINiO`JlzW38l1=&xqU7U_U%ySCzpqzmQ) zp$n(e^y&8G`b2+ruB*3KrQ$H+P6W!Ib}LC8Xv&SZG^^!+oaQHM%|$w=topE_xm#j3 zrelq^Vx4#BtkV`aj({w@qCWYG!GiJ*qDo@};OLO{^HQ+?J({%aMQl{Av8-6vUHQYd zJcqY{z|#3crofI2i4x{?bPW2F9F?K40_J?vs7Dr%JD&iA19?(_?R>1){|_2eQv$qT zr6VixZ}#)QT2qS005N{pH?q02-U0@<m*^HsE<Gjs1D%S(eE?8WE(+iAAI$0aEptlr z;yd)uW61zzo1tY>QtF?V@-Y~^FkbgI_#b);OMq}ymbz#DAgX9$9)lOEdfssd{&}qD z)Bu(ev8~emiC0S~!3&Se@+kjttXnD9s4EBWpSRHj9lTItPy1))0)^iwxbyiv5%vFo z(f?<EC8nvADfz#zu0<)J5#4grF$8Fcxx<CYMgaINC^4AO(Y;F9_;dWp&=@?Mnde6~ zf!v3*uJk4D%{KC~vDsc<{szt;vCb^aIj_@Mz)pHJ|CEftdlw&1P&)f>3KSa{=e<A` znB<9r$ykxvP$2Ltr}Nroj9Csqb0_|mt8qKdxPh<AG(~!XEWHi1(*q>yZ_t+OqUB<j z81Sq25jx6iYL0>1iU?Fe0FaA>!K1w>*Yg6zEgJxNSLeq-A_omY#ba*sW5}zl9PaJ8 zBiaJr`4!+;Co+*$q>_Lyy>HL^Q~=cCxAbos%F1kjK}{-v0_zr~34_1|klipUV{K*i z=r5=y>COBXP6G&aYH6RMvbos{2m@tL3il<I<GnY+CFK?5zQ&2ArsNc!(VwTjDPmIB zC}<azwT>h<SANdT9w5jqqcSg?78YQ_Uqh_9AS{2HPP1F3VfQuN@;d7##OwSwbF8zu zB?=<b`51kipVrge2(yG<uQeCjVML)QF1i3bktY{IONf^Gb0daXv&ji>E)19kg-sH+ zCksR-f^0K-Bl@v*z4+39ozq@!cMeGUoX8$+zMZOK3n*g+3L*!@R3oTwS!bPC?-M8V z(xM{Hcf{whDOVx`u;niP*9#h{19)MuevXhjc_77m?F3rv$T#%{IIT-nLAN^^TC1oh zrfCNreZZ@l%FWOy&cX10*x6uLQBYgjmt!cJ|0}Ks#zXZ;tABM=a9ABnzL1z;{X2TR z>9Fe9E=KKSx>)RKzXtc{^!JAmhFK-IS3a}wIz)SjT+3G5jAnubTze7ycN<5^K<3fv zjPY;g6ZBDjDXVC8ItZ*-TXs_C{QMH$ZZiel+pJc4@TUpwNVxMqvA1`Axqzh+j+*Ph zoI^UJlk{t6z^fs|_BK%$=`7;iIFn)SF}#i{#TtIZR~*~XfmcGMcI5eF1Zh242>H|j zIj$XbwKRk8>`sbmG!(VXxM>EP5p0xH6{`m}dj%RLWj|D!^JaJr`*QDAnU{%%aQXbg zucDC%vBcrP-k(dsc|=-?N6+KGMH3^_W>ZVu719dAbpx2WUBvbb-w;}h_azr$PF7gR z2s@E?CrRia$9VPzD+kTa&H^bcI~TRGPXkgw=%Yo3DwC4=LS3Y2QVaetw6G1rMzSai zy=adU)q;@?ndePc(`Z{-0{pexPw>LTMNa23!x-5ss@ZQoGAcCA(izQ%jh~cyZ0&!e z3-UTc=(MQSRV_$xi!j>H<odSoAT3Tv_y(!*4Pqyj<_waf>8M6e6bkyLz%nd@rEyD} z--G5OhC+SbxR$ayvk`?(lZA&@;viduUu6=Hp_9SWfhXU-jx<6`f4k0A1}CV8*<|E~ zH=2!U?!4YIz|Xuk=lkwzbh==CaG78l)?=i$a=n3bJU?t9f?g3Uc(Fx%I>rn1GL|Wy zH<E<L7QQ%Qx*snrTkq|558z<pYNEkX&E!?3-(4WpV6b*`UF9l9J0Bf=bvRxieDz}N z8U6ILrXVWB;LhPFf#`EMO3j%N?XP+Eu~YW3vF8YkA|m?B9eX|^SrprYfsE90_psMe ztjc}^-8Zm)1z#Zapc8$p`1sKQBnbdv@GMZ;<5H{6fkgKquWG=L8YA5Dqw5sm%}Fo7 zwvU5AvCibnTcCx#J^>`Z%;&r#UsLoAe`OT0Y>|U!sHg>lNrtuP*VMesK@`@T2SL<I zxNivvqSO-b2GgEMRJ2R7ypq>#K7fc1tT@Rf3%)PuN_8U3a2~;j@Hicv1&E0enX%BM zhw*)(8ykxv=hdqCp71kkmL)33SJ~AfRnYCAFnpv{BYpaN@JYs}GFI?qiSgNYoLQ*Q zr%y9D+Twf1S52B!V(<>`=Q5e~r`@Mdp-VIFUSM2Dk;>WEvs&v-c+&XGcs+2lh>$BE znMc;K&Z9$_gTP-B!<gr?>G}L?#Z2MZ9fxq$k>L@?rshZ3L4M-9c#Mu3hZ<_m2g|a? zOns7;?bLfFwS%+6j1kV}6`hvpoNf*uCaN%>UTb)~s2#k0B-I7%7e}Xvuufm9lA&}& zIv^2`NE-m{brKt{F$DDa!DH1ihlmT_%R0&c!P8E(x=pSeIi5oyNxnz7I50jh<_*As z&Y%!uis!Wy#Y=%<HjolDa4olA!EF>NE0RMoyY4%66N4rvrc&7^CnjdD5h7tyU)-}3 zL2O<d@rxHNUIyABTCLf|z3i8ZPVuo0(^hYBd;wjNb<3%oF}%+2Je{_scTpH3ZohT! zL9$_{;P^%&Ss9}=(<xrsdxv%82!s}2KaGAdymLZ<U;b(Kr|4<aaVG?|sC9!zSlEHe z@WYD{%xh*(c@;Ov&|RgL6#P^aex<z~6lJMNEuaYDHp!)sdtjn&FAQJSe4-`O4jWjt z<Ad&ZM$O7FGc#X)PizUy8Lq+wWdi%g^0Km9=dFcJhT^PX@Ew@q5YRS%)#n)SptK(b z7iKwRJ*SCN$-D8nHBbJ~?$4TK5<oigxPLCo6n-<eawqNU{F*=Ui~;sMfjADrUo}Pw zM`ZIe{s6g5@jjim)`YhR2cR0c5vt)_v9fW~0a+>|2tqc5cJ&J~T;2Px+|eX-rX)u= zT2B^gg{hD0``F*baL-YW&W4Pmu!c+8f53>C;&psGuS}hUj<cfq4%a=)7c?H`jL5yu z^FEQi9xXC6ZLy%>T9_&J0!=?upETInfiUUoEbZ28fd~<eLuQTuTTt=fhpCw&i3B$s zgCvQXqR9&<-_z#~nP*Yvb&fz6%xjpyhVCa=T-CK3yHXY&Rvocz)4pd;C>*nu9Xg)x z^rD*!aksNm3Pt(;68lQC?!)t7ni-_6q8VDshvaMTI)twx`)V5_aTJ$rTLxBY)?`gc zS%pHF&hG^cqFC=H>86zXE>_{#6c43c2RjOVoE1dfX1(mCaxkTeepZ7&EriV&Uif&k zQlJ|>Fpa_96=n~4PW;K*F7Wd%_6ywCl0g?aMc|gGs?BHz3V(v{DUu}JIl`;k!JZW? zRUdol2+5$}i`qA@*gE-QAH2A*`zc(D9zrQJ3ClFwut2w>1ZKGTn#Rv{;k5N@>kI6) zBP?Wr?E(X5P0u~QOD8m1G7}v(pmb-RyA}{`eHS^%=wK_RcKNiBZ0IuoUh3$Bt;)OM z-(^XibeGry4xiGCCIiO9Swd+P4im<hY%I!zC*Ct4lnHWN9F<S#t7?(G#*;D5%CAQ` zE|#@peC^7e_#(jrHAhlUy5+3gf-KbvZI|U-QPqL@tb|^WYWEEs!8-HX9)5X8{&b!N zXq-f4yVu#TbiSBaXD;|`Z5=ko1eEA1jfjOe+#d(<%1Q5w7syxNK*~m*zEGxEjqmoV zQ^cK`UF7DZFIayd;HcG~5Yxtv#b7UR(0riNlajw$KeR6%Bh}I=TvewLu~<pz+9hKa zVKFYwOBUt7V<%AZ_@|&(S~_losq(gZfdsdBsF`8LY*A}$_yK_%3Xj@RZ1gAolJK#z zrzWI&t$~B`v-k?dMsdBOm!J=T+lrflt#Sr8D2#S5;o=7(C}_*aYNvqe=%*x8mn8%( zk$o6mv8DY)pkqWlUz(d8e%Fq;4jJDL(+vGcm7<zXM{9n2pH1qRVeL~I$iqiu+c|5* zdJa7g`HVSd1z8mEsRKLRPz@$&*k|DF0<c?U(OryTUlnU=&PgZE=%1g5u3_!{!nI{$ ze)h1pHzlcqSiULS75%N`P6m?3`Wf>l3{e`u)9p^3zsGMqht2JOTY@M2F7do+?3s&g zyO-XXmo}32a&;rBWaq5JX4oMUT0m4f2C-_%FMsI=!TQe*1A^)%qHtoifBN$joK=-x zn<Vynxu0S!sBpz`a*F?<JqN0D8Wmi3#}SzjwOCObsLTR=!puGKNB`KcyZ}3y)$jrf z`ad^6G`HJ3xDNS0H*viW!G^GY(PB#W&)R}4n5g1>?f#+SaR>styE505*LS4+x0EtA z*k935%=|+)qI0`ddz6u%{^!&db1Te8hVcI1av27F4<#(jdZ2vXm#$`JX0o!fl9EA1 zbT>4^!0t0L63~<I?j7)8)Pf)2_rb?cOh}-rY;0`&@Buk>!ioqtqWd<_F-i(prmT5V z^DPRga8aTNxq4O{lK)XJ`p>-yNo)fp;yJf9QU;;W24b5!O4$mrDWRLoLr~f@?|Sz_ zj}Q$J7-v}|m1DdCYeRXsRo?nWcgP#KgEQF;{fEb$x5i1IIKXcy`2y4UOpVo#Pp_sa z9xHbPKW&;qL2Q)IAGiN47(;aT7gpJ^MGfpgwHnkpjdgW_EZ}vc&<D;yBmfO;>R0eW zI{h@}K==x9-837t!;#cDZ>hU@)`QrKtW)(e9XH_YTKZL1#>&tJ2$ouq4p#Y@OyU~Y zZI{QjZ6(k4fjy?Uyj)sWw&u?<)&`Ocd$}mR;ML3Z&{>d>Lmn2kfLvg@F)z**!NY#8 z(EGOro1Ed^0F61I0*A75`-iLx6Jdlz84tJOJ_ivZsbtXw_4S0hQkDMl=6yeZ{>)>l z2%D$GgC~o7P5Iu(=y^uul-!4QuVh2#kF$g?Rm6>lo--GjJF4WwDh-5|=8w*|^kdf# z<}@nemKkQKj&~1!v*uw(IPLZ-UBh3cbT>j%5nlR9<>^gSu*P2VQX@QyI8;To!fpfY zucN-?7dfVjI9KpV9LBeIK}`_!oIEFDR`<j9(T*TycrA*!{9XIeg18t%_DJ3<&Oj3W z2w<A40V$}e0U+7tejCu;fa{{N2UU%lni?QC5|>t5Z|K@Wd0O7O0Wji^-n1n2pbJmo zBfiI&FtSkD(C2wHcyFR)Y;C*NxExK-!}A%nxc&DNc*Y#?*v)wKm8*Y>_O6ZOa9HR~ z8Ko5Ubm$7<VabPjcMEwFm_M18b)bvrpZLxkK1;N_p<L5D|0?*xe<7y)1*+tG`^;|a z-JP1Lns!A6K3G+tyN9uGD{Oqj1+7jo=mP#5gM0)r!P_2D`Kn`nYat_B$m)PfLM_FN zHp$bv&$O<#VJAm{v<<cMo{<*gdRB$glr-45?*~QwaGp?qoS(!;U)oe5;mJ-d5Mh|C zQhdD2QrTV_al!9xiwybAPkmW%!$j_L5)@>UF{}YCXZ5Q9^t|9j+W%tit^c~()-F)G zySux)ySp2aMp{5xy1z)5gh+RHh;)~<v`T{@Au8>ic=oyXynF9I;Q9-GTr$^O>zQlD zc*Zk^f#C%lv4eHq<sRG9y5*CnU1EE5p`pkN3ky_Bh>Owqyo8-m36zrEU0r1FuyFXS zLPp(y@<_x({y`FMa58!kG&*&3dtSxvOgvu72dq$|(1k&cK^%8wkWT~NTUCp(zWK)F z21&#v^ku9BQ;zYuS-j`vai$;n@CA%&Vi!ex*YH#)9bpm8IL0pKN<{~O_~AKhL1yR@ zk_T99^zrwp-w3sP`S#6!P_HVLQH-_u5F>WOZ-@VK(B1@(?6ZVl^}ts`jp+rnW|TK$ z#9u`2IV#rda9>b1ZtpQCd#T)Q7Jd?g7YmztHARqquvwTVQ?|~CQ_(B`j3|?Oe=~n7 zgep$*F5>yy8_^H|)Z_mmxZmSpLf)8f$b75Y<EQfC*Pz$40aVEHytJ;HllBiEUgP6= zHs%d6`K~u_#R}VQFPo!UYy_c|q$3b#o$$oP3Rd!OsP?&1u&AX6fM1PZt$EJ_P>H!Z zC`#n|30UYf@Y3>RJTHGbdqM}Ew0y);x;q=d1g6ty#_fQE=2Y8(fP%#gip^rNapS+O zwVj4zN`&+eUKRtXqQKwEwpd()VU_Co4hybc9o4n@d>kQE5mT;h@;kW*iSgxy?N2=9 zk-lIvqT#)q9$F_rWk^p~!dk7-c&+M%tnOc2A+Qk`qd0b<)of%zF2>|5-PdWa^!ALu zdajO9u(l5mEl$Q1$&T{SIor;s<4m)j{AOyN`Ge*aQa<r7g5*ua%;8D=(58}(w%%vl zQ+po5fR<uPQ=4>^#b&VMwiia_DROo!%ZQrn$Ojj;>;JPt1x4_~RgM&2*e4X*jt(O# zq+Hk8XOtRt?VNTE<1fB93q9d@5<<iHOkuZxYLdyvCfJ$_^#Fia`8Qm*6Tta81ug1m zAex?P|4biwROYVo<MML5!#sl;rAXk7=iPzF_3rDUVCKRYaGD)BkdA<(K9T?xFcw-g zd=#bZ@><yX7BsqX3U7eGPho<xz6CC}jI}aA%dLLj5ocSOP%m}suE^iCLDGfRJ2YB^ z#?)c;*Y+j+z!`-G!$w2rhMDN1oinI_-kAII9`kpE$)se_8OK>~<!2UMeT7T8ofWZ< z^yad4YNna^V;*Fgfj}Y2Avu}6P}*bsFGrS!T|;3oqxBfUk*q|AaqhQpq||v{Tx^>M z)%;xezmR2=GzC!lFWv0e1L0<(af{#(`$O%iCGe8l2}wBJaY-;=C5LemZ+fq}7y)g| z%SPLYNoS?s>-!SVTs{7Vd0SAyz>psxiliO>eP192!-JbomA3NIS@iGwm?uzRlYtsC zBKx;RO9_yE@FI9#Cc6BC*?<a`6JO{vU#7peHL4a8re$~9aPs&2yFr7!4<{4_=Koje z^%wk{1$Y(X$KtL3kayq+fr9@6-@9G#_qM2bp;2VA-jB5YFR*_1`*;KXP43X5<XN?i z&$bK|avycO6MP#1vre&;sR;`IP2J1e*5{^28j&n>Fp*o|OxCHTl9Hs5+&?YfJoHS= zMBjdM^%ivM@cFf_X|y-Z>oI*gNtTuM%oUV-!@-zIQNi?cgb?b(h5~FD{KNnJV6uUu zp<=T+gg^P8fBf%%@q`XVLV5@FR1f|C{LY`t`AOI@A@xZmgw2Tm+unbEbp{@^dJN;M z&HuOCJzkbk0Ns(vcsfJz-~0Se>x5XKRn*V&i~r~;5*!LK=nkr}zx2z$KLu#$|920W z-<$-&T!jsFmCtwY4N>@wKZ;lZFXs{(lKs)<rwinl%Qv*@eye)OYS&xz_v%l+Ksa|^ z^D3aI1qi44&=~_16{*kZl9>&GyDjRj2AfFSPza{~?Lcc!TbI>PEGV-=BNK81NVRMf zngf0UP_2z4=4JRb3%;<u>0D9Z)b?Y^RPzCB>Huy7sJRDTOU5IHn){PkydFEFK=)R# zYH?=f4Dgy478hNbXBB|pC=dkz<mBp#3S!BiJK*2dXm!F63uH;7^_orD%u)CR2n^FO znOKgP#L2pD5Pl<gwks%_ek<@mkB7bcdKLZIy{$B(bo<2Tx$>KIyqeGzCKeWoC!0d` z%@%`#7E;;{$|KyYwM?wVeUJuVHkHCJWTh)s-j#fYV*8U{U@n@RFt5TyY0Ff0!-`HY zD@taTeQ;^UNKe8IeeiYKK8c=Dl{|7`fh9T<Q>hp$i9U1MN?U1X<VA$?NNy;9Z>?f1 z0Y0vBbo#SdsPPw4%F2B)iA)H!p(kIlV?-AZvhD9Qys*-57-9OGNS}AP?~bS2+S)!= z-U0Ey6}0FQ)z{r;{ivV{@Asp`+Htb)X%wj|69<REIVe4$5c54_Z(ElUM7hQgznlRg zshWX{SAfv+{;)KsG?pYwBCzuk<gq(JdymNtAQrF`)2>}}adA<7---)6g7&NKgeUSg zEsCW5TZ{}(Y&>l2H6OWvhrBH;w-J}>3ugso_mL?DcVmYk25VHGs}@;5ZS9qtqBUmS znMCgPzTd{zEcUJ@IObx`Gre0dC_acrPY>H&nTaKQXU_WAJHGJ>&k(g!>>5*1jyE+I zZ)3~5`xy`IZKFiA@iX9J1e&&4)CX*ro`haq^1)zZ{p9PvZwP#)pD6y5Xc|cS=`}p5 z!djNZ!(APj2)qH33K*mU$D_1~0T0*vk$$bvE;lDlA0#lrqw8vl+rIm-zigctxR>?p zTj$r>c}+>Pjq!06?9a?)Z>)6BeR^(lFH#xQMULwSoVgywyjq#smaSf`bKJPBhPi+G z+3|`MAw$!CRUttWe8-nOFeOD%Hy<Z*6?`@#rN(BYAG{2mW;MViJQ^7C&1QDFy|BHO zh+e=v0$j{epl_1<Vp@1hB*%*#+5PQ2MZS8^x*`yKP-c|B_cCe*B!Go-ZIr8bj*Bds zBMC06bnaozpbk+V__%jy%}5m2VU!-32xV)O$FfJ}Rv3LVzf4O@YgW;BZBY9AOcqEH zx=elio#rVj8V0$Do>D8Y9jWbkxDh?BmnHCj4|E&>L+A}}o=o53d;QE&eT66cb(+Ic z^UT)-d1+Y>rTra45y={Pd*5_etW9;AnGGggy5t9vvHv#j;UU0~SrEQh{(b=0fsN%2 z(_B7>0rPb2`&_<~Gl<YAIuI`%BaG^BC|zXe;yXYpSA1Cg#aqh1Rn<Q2!kw+I`NCQ_ zS}9#wIzQqULW{YXS&cbq9Eo!WaXI<RIOEJ!7W!o3zuyuICU^4)Otm4!Pq>O>`!;{n z`j`PtM9!YO#tj+noDG?Ucg%cd1ljI19;%0m6n=79HLa^ht4?+4zL&F#Ak86UH5e03 z8DxaY0Rh6YgFil+_pI3Ehyx)66%^FPRZw9OA5_OC638;QW)Y5Fs+LvQ`3uub)BkfQ z;`n6e1P;s)8#&}}lYIUs8m)`m(}1>5Jd|;=QhY!$$IS4Pmci0ZZ7$ExPwO~fx;PYn zj}Zj-y!C4RbjxiI%wNY|-3F{8@0ok&yT97pZGf!?Pyz=eEo1j>K;v?Kj??M82PF4A zFFir9nrq79NNsoCsV}q^ILW&R`;*Vo^Yu$+1#R`~{(60Lf^wVM>DuyX6zzYOq{qeP zMhvE<#cQ#}D*>E*9JIaE%}5isweu8v$Hs<KS*je5qR@!h)*aK$Pd|6;_${0*1*_IC zvN)wxd;*jTb*k4dyFKGgV*3BF#?GLH;<Hw<80ragw~0uaU$=%0*-Z_7l**6Bs#kSj z<$QYdmD;yx<!|rF!V9VS;w4lSE6H`J)CX#;S&I*rr0jnmy;ZVcon55H1^?$C>*b|8 z+?Gz31jlFZm9GV=wyP&?R47;JsD>Zgdzz!me|YSH)#OR^_R{=i<0&Y$1A3>g0~b%h z;^LfFYMy>RiYQ)nyOH31_@v&V4@{@rz|QStVQER=-SIPUN}Yb~(|Nx5qn0xOfD$F7 z=KEdOv#Q?E)j81t>hA`*@`PWMTCR94n;91~TQFR%8}6|3i1g=of}&^zSe5Ma-R{Db zrf1KNvCL_fT{<gvE_Nq!p1uqGesbb!Q$|e8-ebjuz4Y_d_HamNP*F*3Ee2r)IWWq( z_X^&Cryp6)ln4}2SjR};DXcH6=t_vAsV0_WKX*_zYTF@_DIb>-uuJ;b*Oq|pjEhcN z(OFeFqp2{gP#E8SucE#+9rt}fZuG4;>ivm8n39c6YT?F32Rv1{G<y<~0P=+C3fxZN zo|;s*6;~j?1{!R?x-M;71%>*+lO6_(rl2YQ`&w5khIr^KIx5;eGP)b*H#y-edhlT| ztEXfs<j+3wf8;aG<|MCMY^1Zi-hqKZr{(f#$Ej6*9q$cmBnk;^nwwwmTW@@|xLfZE z(&_y3X1`7p3k(d|CIl6=(&Nk7dKM#yD{UBx3?_E$NbV5Wy;BTAk^9w#pU|f|mzb0? z)`~|Wr+BP62&&II5RW!XlW2G7e_mH3JFMu=UpSX8Atxt}SB|8~RGZBcJ>>EBv{bo? zEl!{<B_J;+h$;SI@8M7o4x<v#a`#HlMz&=;jS99YO0kd*G6z=>taIyu)*2yT3y-4t zf_^OpO}K*2CXRkRw06<TDI2+ge~SZMK9vK4qd$z9F?El2^hA43^3ds9@l#!zckcrf zInp5^^=p=#^$6$(Z`*B~d$o+vI{;%dcuu~w%5Ss88D)B-Qi6{;@mW#4fQRrAARdf- zEO33&t+L(j_9H>`+p<lndglAbVhO+$(8=sPpCGGx@piTJaGbU7vWVO{$J?GYcQ2y{ zFhf_MdK|NJR)+W*`(l{ehFrnAQGEr-oZ;}T=BIZ=*M#N)=Nh6XgC*9*K+abq(u@?? zX+?1O?^I8(Jj)aJJ~nDw*pexGmg|*4$>tuY{Z1`;)TM2sbpS(@@4LWVIkLA<*eC;S z)OUbg=^J%FB@%;x)j!*3H(Ik^HUL6SqdKef&djG1iU+6X+lEUKcAE7Ogz>G#7p)RH z#<&A?<UvyWpvmu{h!j*X;h(7J7+_PLaH)o^!xt@Zj=Rwts|4CAG{kGXdpHp6xkl%? zyNfIJr64r(#D?+S^xOA-DTwCw+GeNJYVqD5y+G})*oqPrp7W;11p6wrwD!E2&xUOp zAlj;b+4<y|dzH`}qNL~2eZ5D{{KhmY+ye*+%u$Tx349Kuw{^1|`MM%jd3)NE=XRf= ziSwTF;(P(B*W<ZAJKX+$OkCEau3Fr>=e+uvq92Djb3Er@j(ZfDo7OkONVYz#a!TSY z`;_gT-}lcyYR#KvbjD!DIa+pYe|tF*v^K?U7vl_6qEENF1Wx06i;FxR+pR)J>!Gkn zbQBTj<Q=U%l*f|$2j87L_-}$>S7z3DYVhi<+4b#|<nlS)G_fr_U-OrBy5X@EiK#SC zvTDt~pIJCk!f~2yL#S7dr|=*9SZotVY(9pU%$GPZkfmWTNxo<C#jh!`%Bj`1TOp3J zJ#>TYqX(~dCi0d&a%618Twk}vV<j=qCni1yV-(5cNV|B_PE+RwcyGLXJ7zwT^y>NY zpJ@p$cAh34$-DNc3^ogJjHTZ}@y@WF*R<g1YO!WWo%A;QgfnelE?3e4t~GKdLP4DZ zGtpQA`L6@8W1L~r-QuM7SD9IM_8NZ{nDyiL-M*xJcyYCRs(22Aah3*MZO0yooVN|U z+%}TN(oBdn0>PyJWzh)jIx)TwO*dG-;+ZOFd&2Bg#?5K9y~C3EE9a6!xH!^{X_boH ze#?aP7n(BjYe<^P*HHQ&6yOo#YtnMXgB6+%Q65fSooh@C)?^RRvs<+o`_Flaf)urr zdVS7+CCOq~Zd^O!A-YT$S|=q4?-3;mDUa7<0p-}+@~+_fujbALKbfgAaEJ{)dNzYa z>)H%G-Y4LY!_cA`zbY}#%zK4?w<x+u(|y7+OY34mS98{Nq1h_Vpz+zAQl}=dNV4+w zOK3QZ7-4_~=@YUPLw%r)afs+i@e~(5B9xOFaj>RxP=3N?0qSNpR5{=87|LdHBQg4i z3~U`KaVkyGcjG$T@w|_LZa<o_+Y22tVa%<qqfHQ!2^pEPTEwC?YVq(p>qC5DTOR-b z6KfYRi1&Wd!_>=Gv)~ZJ%^;JJK~G^8UR|s2BD2afn+&w)%Qx7$Dl#rRw8e5iqR@qo zBji4VqABeLY^iD<UtWApcE{g|few&LUez0px#>lekXoM!=_-{thiDQxpk0G~#Ix$k zoI+-fSD}_WFD9Blap-U8L+RTqR2JCcWrYtT6NakNwev4esv|cFAm+XNCQRl66g%9o zNV4AE9yehYu*SXO>%vEvz5Vn%DANrKH<!i3a*VsC#fR{m+yT+YmyRJEahEKi`MGtr zk#UIR<m~VlEGlh`XmW2O61@XKNP^^pFYQl&yM6v@aov=6U!5G#2TzzJ@eNHCSC>!) zr<NOy9@t#x)8cun5_=SQ+w*p($>X|;KegBBOqh=|Io8clFuPbT{`CFrubEgf^SN{3 zgZyx$OF-%5&6S1!f}{lz;KlF^>K={zT4=g1(6rZ%ZC{U#c%NbQyt{oR6dN~5pcjJz z0^1UWyQEhT$plAD$vmSmw<X@9lyO_G`?e&&GO?n}T{f(N$|+9QHa08HU$7n@Awi#4 zvt!zc<TVM!aHeIr67Q;FxW7%*rOa<xd{l(D-vSolTTVH1hOo|3rf3V32TUxd><25D z6_|!u?phe4^p4-32G*5Teh%~8yq@;yhh{jU@LJGDD2TAUOHK@sD|It`Q~D|~_-@<& z3P>v3monnpM9}IeZ|?)O^7>FZ_OfuwoA|6@4-~IKb{b0a74qwtvZFo>lm(rOw<@U& zks;*UCwJdJMDy0Bri2D>P5XZ5o<K^Fv>486efGv3VV<G5kDRVS)(H5hn5U&-E)QZz zBT-WgAB4EamlpRAj>H5X$Wo>Vbf{7C_M^PAi&q;=;;A$eCrLDg@iT}cuzhE2Gn6#W z!vaAoy(T~PMYK!j<_WdvoF&zxUzGF4t7x`#cypWkOaj&@KpZHrw-ib9xgkGdepvKg z#V8uaDE%}Hk&p`jwhENv+`5cv;L8Xf7N<5>EP))!D+&jX^K`s96=)G+n8r1`9G6MC z)K_tFto20^4Hg$nShTnw8Bo8?jFE+C&~|6n?B()g%2O|4B`P%^B833Cc9DFdK$cfk zKN}JQCk+eLXj9v<lTcu@n6UFB`EhJXg)Uc%_}3gy`5Oz1q^LBp@&kRB^$@-f1Q)sz zdHhV|;r~*hG%mhEhl~<q*1!|kkIcw4oH2u;MkT&mbu>n*%1F6fyqzGbVUm-^U!^Ip zGfwm@v4SoLM|7}V=M}|aB<)q1<UIs6eJfr=Ds9PtSVjd`+*xvD3!FKJkrpcmE7i4B zba}nj@h}L;RTz0nI&~vEchcsgDDMvxnf(QzNG5p^6AA}C#wWXQEu-?8W`WzO2LDh# z3v={rp-pN_2=$AhMcJnpz9N0r2J)!>mf_rm<`f8t>M&(+?yC-E4WEb@d6Ia<;+Vd+ zd<wel%L-yNUrPQk!pGoMXFCj0S2=e3kut$yp4qq8kRZbeONE`+Fu<yUX4mZX5{6!m zwo5w7JqF6=g_99~HxgV70x=s?cYU5{gmV|s2PK$pY^9q+W@;JCrYAISS=*$Dx3vh> zw*B^V0o6yNjW>Ob%9;9u#{?fq$<%lR*E6N;_apq2deCptPgP+2z#=Tkh(I{Z$Xt|# zxu1NU-cP4Wt?(Qtsci4o{P&GvI5o2N*wb4#s&-BZe>yp2ybC=oCB{r!1}cYa-C@!? zCTzQ#v)Qrit5MXIG>9TsGFK5i(GxeQ@yo@XUFmFFJGnXtXL9yKVG^8ruz9?inAN&P zks6oM58^6_3B1HrAEw8`&b&-B{YPG90w)u$K6N8rZiuE+R~tBQkKldNUb06k_ozc> zVS)FHqH#PALvYaG?ZGmSDlAhDrZEa*r!o%U=I}TmQ`PoW`>fTo?(;%xqL|I&>?7^n z_(Fb}U4w?LW3<2?(<k5U_^bq#ljd8)aE1q<9DB5kl-sKmnp+g-j2rQj3JF1m?5nkI z?k}~b^ALRR`p9#h9Xzc`!_x6~tJCTUs89}>0&Zs;9P~if%gWKE-e`53ADKi=M2bp_ z?Z#}<)VzKcef-EcZBOTj7!K%kPjsaXzH-&R@)n*#L25f_bKaidq0E@#9GrQBklCS$ zkA>)OulgPrF(ppz2^Q<O5LomlGxA}|u=oa-QP=hiGWtFUbhj}ktqc|F>hw7=oZHYB z1h!y6SMp<Jr3@=5xG3V3;-DqlH1xXZz~>s8d8K31M13snp25_7laeM~LdPtnPlP7! zfnEFcV-f)yG1Oo&!Rj)G{(*iLPT3oe*z@6aS;%^p7l}e5LOlT;my-lZ1F|KXt|A(4 zlhblBHmXh9@>mxDr?LvX{RS}>YltiwKx#_(l?06N`ZWv5f&^#39Id&|06elu*BQP~ zn&cL;D!y<U1<IHz9BVQOH`TQ>T_mT8o^HEpbMk916#LN;PRlQ|v?fTOhr7BxljR&R zaOh814D`Bxu`9jvZ8Zbu#@2I4**!V5NgVE&iK%B<Pe92MF^;fa6xmO<Lzv*HV7v6n z-Sjzr&*=Uq-1H9U{=zFcHZbXBT5_~u92k`3EGDYzw)xN4CkhmZ?W*F^cwjTq4}ED` zGLTAd14<Apf(ViWhp7CuR8L|ZFZ*NbP45!*j1((BW1qGT5h1T8HAW$?F<8`(mlKet zip|Oqy@Yr$pui+Ko67T090lFX=asr>FS;)o7~&`{ZG=v`6s`!TN!hVshX(~A+(7qF zew~eWuymGF6dJ<Vhk_|In44U-)NPQRk=e~Src2VbY^hHqFHJT!BTwE)`2cGjD`b;t zb-`^b$DP^^?62K3(v95VQtiZ3IH`F`hRb-m1z3}A@k6k%Qn|{bIFffIPCED!d}DWp zN_s1VM{h2A(Zs%yq&`R6#zVz{5{$#;zT(#;x7aRWb~X^8<uSB*L%lutjVUq7bf(&d zGeBOgkdb~ga#W4X374r%R!zhjB1@Zl5Q>(JErPjr)Ghrs%J!!qe<sSrj=ZsY8&!WI z<M>mHN?SsR=ryyYTi6Di9Hy6a^;|^;vD|usrD?iSl{K^ZP)5^-Gp+MU-Vb^RJ{2}? zzZW^DAS@#DlRS1YRH4Up&G~601&kHCve?iox?aL*gO2amh_MHBFshOdilNXA5t+~| zR7&X+JC5x~;F@ve2_5QB`a1xmY9&dHPCHc9s)FQqHJ_n?w7)5bd#Uq7Ea)qCDZa>T zoM>ri<+-8de3CtrJ|+DS<DlG+FQc=_6C4PnKXRPog>9j7>PLSr1gPmjtINb}C7Y21 ze4)2iq=C)^<<%}S{47db3J1(R8$DC2v!xJg3hruqf_IG%;}Wp{)dD~j;>S}CrIw`@ z?`4{)Wy1S?t0H@Zv^vU#`$Af&%vo%)kswYA6_=p6I4AS^^i)iBr_uT>wY^(Sc{xSq zUEB^qFAS+VmJOpiE==AY;d^UD!sFKh^k+(qXN>SD5wF8=)W#T%smiUmu;N(^WOlb> z)JGWXjBSSH6=Mit9K_rUbgiY{loIaRmfk&~Y2vf%AnSiV;<lvZZ9)ZOljDj|=C{w; zVS%`d@b)a-98Nj8hG(x-v0arml|Vh2*X=A*p;R9Yl~18GMLBKGH(B!lV%LS0SJqrb z51|`B)>hRvsp0y1LP;fcm&N@Q3j%T+GHTK`bu<<Xrh;=NE8k=lXGqX;hUCp1=vAm` zl{9pM*ZC_WDs8Ea)W5YW##x!LTO0GIkVRH=giHH1I4%&#kNwd8s2j{1%#>W9>mUDu zlaju|Xh{PR_n_e2iUq-IM^$R3w(!k;#c@L_{zzH}XNIk&fYPv+=vZ-XtIHY@)Q~9s z<jDMF<ICfa61CcpMW?r&8mZ5Vv9I;qD%1Vdf>)@s%})Ed>}Y>!@6Tm#v2a_%jrT(v zi*hLj<1pO>`a&_2Xcp={RW_n0)3Kuo!D<+Q5oRw6=^P`s<m{toe68^+8$v$!Y|Mkw z>*eZja(A3ma`^63S~!=E_gFN3yL&bcld-SSELcax!r83SWR0?_vd3|0D76<XrC55s zCRPt;;b{D<sdo9ZAqNY&`bMdvT73LmOrNlDR$%&^RW8pWy7>C3-C|;x;zcyV6$4aI zm#6FzuzNzu*teKwjvX`uTDU9)l_!V>!!(cJTZfB1=LC!;;J99iVYss#Vn3fuA}B#6 zA7h=zoU+SGiKwbIe(o5q6uO|4_^`-{?Pm1jZRVU=wt<3kw(W&FYv=1>u6thp_CSK$ zj>IjLYT~3yU&oYIJjzly?ZGm54a+5^Ukq1%5w_)6@$O88XZ&8&$wZS6<H~3(7L8X# zB~di{rdpQUFfGWj3oy|X1_;#GTZC513Pgr%q>B~xea;p+ho!|<#KeaVYr(=dtvqN% zU(F4q?By#b53DfMC=qDF1(OwHB$#s%3N+YD4mT9dDfo3mD(cnnXA!t%4JWBe3l$EM z;$-TPRD{3&ME+9bWNqZtVzXx=t3P>O42@=e=-m9QriP<{z&HMlUa!S~GtE<spP_Mj zhjM<yhU1gjKlBomC`7B@z85l2RTv+~;yAHU(Rs+UrP85oR^iwl{~F1$%WI#+qPoiL zRJH+k0Oi!H8#jdjB@!<?{MrrOL{=kIP5?XgU1!#4HhRrM9l;!;XFZQBysBO*1#kbP z;vOtvD;B@&48v4~Q<6Z_#W1g^eExAGOhiu0yrax%r7xTmG-I#2NZM5l6!AIVtcBCv zZIvz1by%;Qq$y&z=4~UqhSGkusRhq)iDqd_Yi_A|`0O?o1JMN_KW|IV5k!hgRz?@{ zkt3n5q@2~|vsBkilz&quTGnV`<s!gbM}>?{4u7u<wD-$SODN)4!Qp*#qbd@kkWY+X zZ>R9Xw6QSd>b@D7UBOSA3`LiT$fk2a<w)qfihm*+t%sgoBe6^m!cJJjy{MOHb;Qt< z1Z~H1)%~_GTHu(>-lS-#v6Dt{DLJsQE(|J>ZB-f7DRpnCsK5EGlBUEjcvPbzN6Rpe z%~l>B{G!}LAJlz=ktU7^%?qFdXW!drMaq~NRU)X{fJT40eDHad$0BKr<nRfMzR8lP zd%iBrZF!^^X?PGvQ9bUI?=L(&jZCN&b77RPf)Fa?S;J!s=-Xv#S2-Yc*oR&8_CSvt zI*h!=646yTKi9f=!-R;NhaB={7`bbxjV^{0R_zqVR|A=etE7UCGkD%mn2dH><mP&7 z`0aV3^w&PR5;YUNBrSPM9a^8Ni71qukCmje9DTV=A>=f?VD-Ev5S5TaKA}@V3L&2! z9O*3MK+^CV{H{8wHJ?i8<Z0a6OZAKce`4mdVKT!EUrPK34Xw3vme2H50}z}Lu|S!u zOs9zc+<tA6Xa}y;qB_1fMS@#hPMc`ExOucv{Jgj;G$<IJ=|Fw9mhC!@0DiW3Wli+- zgw)l;6b7mYX~)JSB$p_eO0<s>*+0_w8Z0D8CW$XlUY5N&jd7CE#yK@o<>k{2&!XfM zp)sK6=lFKQ*XN=qhcinx=Gla~O>Z-R9G($-2BRNGWH?4c^pc6u%4AlTo*8{->^0<} zIY};U<tRKgTG=tG3Nj2a+(hCgKD0@kDMc+z%eO?shZp1DGyUOjID%SJTg%kn%S(Y3 z&iueJn;(m(=Hf`1l1W1dKS}#U%irH{!<pxhXDUgZ1{t0&r5f@vB}?F4%%EI=w^!WL zmH?hr8-`p=1rsFtO6&1%`hjQ`dg?@XWRm4H!%eOk!;uj#o&)HwsO>XCCF)^#Vcr%8 zX+qxhAB?bYgJ$dK&f@9KSQV<HDBVqEhA^;F3_}RhY>r0<TQby@i3$k_3Fy5lclo+_ zy)6nkS^W>Rlj+A-9Rv$L6;pB*+OFnkD5P&z2o25?d6M`dqq>$7DO`>UTaL8kjV>wS zD9SAN%2s|!Mu<w{8ES~)7O!?A34(}SQRR%H727=HFDYc_C#P0uK%?v;RYgeuSy6h~ zj4pt0=XAEEhp{C+ZZ<2b1YIR!Yc-A=pL|@A6bHv`OXVOCy6v>3A!8Or;X=n@FWAEr z&&Lz*=gg8dL5_@q)Plw#UT9A2KRL=rnR-1T=inE1#pgQS>lQD!f`Ih?CzC7j34i(b zX4*<n`P5mv`Ez0I-P0+1{`K4#V!el@*q2RZfv2qOvb0(+JXtZ<=*kl9p;;1ZlgBEo zrkM%HV@}EtnNb}LS9Pa}Ybv>1ly+yS(q6|_OTZ@&30GFhJ83l@W0~&Cy6cK0kQG}c z{VEtSmh-CMig$ytzn<=^)y-fwo!F6`$+?tptf^+~K}YX=!4UCX`ud14he<C*K_M=M zso(}1A{Gpt!Y7(Qm8wc18lfmrmDs7(pO4AzT*BoJ&EcQrDvwzbM}ZtI!t`spA<Ge) zhG0<GWH?^{_x4hV!YjhV3`#C6ZZtvJj-BY`6RGgyyC2H6M0Tl|X$`Pgtutpc-RY-t zT(l9oGZh#maCN%kW;SK$OiGA-_DTy|@WpD~a2iAVGsB`EAWZb5uVT={`EY!3+NCNB zVibozwA;=tKfq!LNlkRxF+hyuU96^#wI+^dEJ!1~wrK@&GSw8|+&E^#;frZ<gw8lV z9rO!vuvs(i6N$pbW7no&ju(7kO^i_U(^h8U>MO;JK{JGjjI*p)pzz+$gr{0U6JsO# zj-=#H?qWfK4M(^{#+Qk1y^XFc7ak~PWn5;20P_KP+bA68w+_HFe4x2)^~%DUtrdv& z#pLz5%IBw(-<P&{vK##<SXc9Y0!uulz#wfgI3vTNU3#6ls@R(B;G0Swrf!L|pSI1{ zk{S!mMO_q<f&$eCftt5eKRj0Qqm}b<*uhPL*nO5J^@(9@DHh}^k1N=fdw&*XmZVuN zKvS;_hX8o&vMpAh&gS0@pQ|S8{2=+*cm~ltuK1!7GoB=*k?QeNFT}B(1oO~A(MStn zFCt9oJ?xAOjx^Nz!jgoGLgFk17hFZ{Az!L|v)#|b7}Rf0{skd+;6}=DMRXM;S)KX! z^@q!Jn-H7|6%lM}E0!!SIkfL6Qcs-BPq`wBIc7rY`J(jCsf}@=pOnU#m}s<nML2cY z6W8}f^X^8mq{^ElSdO(euA<}<=fp7T(zpd6@GL_k^(Mmw3y0}Y(do*Ql>;5B&{a#e z`hk4=taWrKG#Vh?YI~fS;xVUN&$@#BREJm#Hrl>kAt32XL~*wpbrP?+*qr<pp1~+W zE*WpkC=*$j6c-sEeF0sEMQWD8X8XnHF)TL1*tET+ep8KPSW)BID`Pl|C9V*(m08I# z?wRCa{a=fp3xejmlv2#ddzdtdj&kv&6mRLP-J)><9dw-(V3>QQ!wXw%2FF;D<xh#u zXB`Tmb0fYo<1#1)^$%|HhxXIa#wqZ-CQI*U4uJ)_)H10ZmRToWt(>IIj+IX>{^@*e z6)cxvPit#!XDdczN7mGw{38GJI!C^j1m?G}Fbco7=+pK2y=0^KHWX%9CZb+CD9@IE zdH#N3)-h|;nH2h~rCOaVN!s%@deqB`K3cBUu0S9wI#&iiE}uu8)8R2t-1cQn04gx* z(;(7^MMn<vsq=n`VUu7BMJB7MO&3<Omax6oxAvs5FFr(<p80J?j$&+`d>6VjiWx+l z0*Te6SG}?6`su9M8Y|V)9}L@TEV$ch{tyYuu(@DsgjQdCt(vuninG?kMz^!C-iY|2 zI+5Y@I%@NDqqT|rc=J@y|Ck2GA$=poxIi)7Eq~wP<pl0#hsv6?8QilPy|(&(=bH4| zqIr<QJqU!eimv~~{h^M+I-09z2YcafNotsl2q{Hxq<5FG8iKRLQ-8nuCk<Tv$jo|z zny>i!U$VkGuz(w~7mgPe_0Nnk+!749SPMT>H1RJNFCLj<-=$HcW&e?ChJ$f>y!a93 zVAl3uE?$R7!9`qCMdnlg3stP;@uCy-<pIyXTnu=mwFM-}K+k|b%gaVko(PbCJX8`^ zc~u=HSAMw*pUPSgnI<eW5*{HTkP&AyZVgyYlBORT!X_Fn{~J95?t&OPN4UyBpVL%Z z-@st==dcFoB-@BsVNG+h=g{~Oz3OZJ;ir}q5PBu#zrAM@>HPBc_GV8DKR-WZWzlCc zv{cUU-J^~WDQ#_SV`F1wWsnEm2;9$Dq-FfaQ~9HZ78XL&my~q<@_rc?Qg0ShD1{3H zBeOjwVcGk4-UuAbb)wW7qdf1;K+lK#7`a;{PdB}mPg-eze;SxjF{%7q+!8-2%cOtY z@{zqaLyIIQwM<*DQ1p)%h=sHx`8X<i;==m>CGh@mAhs{zmhAcdrQth85TU!*+FC^a z?2%L=6&AE*VlPQN!aw<gNxwmVGJo@+p!^GE@c{dgT!?K{H2W|8xuAR;h~^5?mj5!= z#KXXF5{Gf(Bl(y9JkV-o|E5ETybhPLwzhu2!cF@tu+6U?Blpf$<SA9P)JQ{Y%Aa>s z?FA=d4+FlXuPH2OFhbB1uj`()IO+!m%35lX>HT@8nN#e~D0+Hn=%fP8#l^+1EEYa~ zG&JA<rGt>n`d3N{KmPRJA2D=mYs&_PZuJ}Ye{eT-BsXS*n#!l0o+}~H;Cp`2Abj>0 z1M@%|u6kE{omLU;^s~tMQH*h?=QYsGAAqaI$Q#A{)8S88^+|@Nri$0VKeyUtHMY{} zXq5w7^Hu<635|fWqPntzl*i`mXep4ClvFM(eBAmv|9N8MXATg*8wQvbuOIsSKWuGb z4a;`cY;z2?xo0@6MV%&3T5;k3ijzNaX!h!AcIR*?>>3aBm#troS}2X8&oMCAm??PL z3#lqF3;#sz;1iOb;m6ualUaN;xs-5(bWFCBqmKF787!~AqAC(9HCtI&nXT_)7`NE{ z2su8do>}%Z;*<Dm7)C}*l?fv78nn#(P-tAD`RfCiaYFKxd#Nb3?6eh=@Xx`6HOdJF z$Rwv99RI(J=1Y=C{u{}{toFYQkn70D$%sBNpW$Cd^V3H@&C_rZ5tP4u@sC~T0ld|} z&&irpk24eQRO_tW>91u3?sKL;tu9hyL4P?)g52@T=680kauU`V=gP2(2S429-R&>} zCY$sXNQeKLQ{}`bL6m;}r<)sbQoz-qQoqXJ?w78<{^At5r}+y&2$%%~^Uk{W(A7_y zZr8K=S65dTh5#Su9JuV(kiL6W`seK&P{VCSAHGCdepo#2^7{^als~-xYIb_jhWTQ8 z)(_D5h+e+xel)sd$Z>95G%)>P8D!cxeDV?yy?|KaEntq8?aq%r&T~OAz4Wi2cuets ztOnk?^78Uw10bFILpSe6k7A4K+4LL0@W0Ow$B<bIe)ygTigdi?LqT=&J#dr4y9hkC zrc}Mra`OeU4ijX2_A~Ay<ZUOdYYAk+DeeJ377YbAJm<yW=#>8Rejnd9TC9>M1+;Yk z)%iBCI0Lh%Fd(*03#NVIS5;L7{)#cf;M3s*i8@e=`+h?+cj(mg`FpPcbU`q6-uA^4 zz=rnAg_r>HA$vEF$<>0iP5}Re@2QE8w#9&DVgjs*lYKx&`UEIZm?0zO`5#q8Q~28h z7oLnUoF2fvTL!$gM`PLm;34st4-C2*pak4VXOK&_p{${Z&G?R+k<aoNR%kF|5e?*% zVp$Mk3h!VAKMI>0{7hD)0H0_D8ADRA#l6uU;F^v--3+=t@xTy0=BgUSd3<C9QfG^` zcy*BAk~}&DG)vY%AP)=-i{COek^6vW;d+mckAH_|xz9a9MjN>1(q<fkpK;ZBK1}*d zjO}UH6_8kKLz{%^ISo8*LI?nr*FegG#8SffJ4ie~;4P@<ilm?0O+Ok6gF1{%R_iIF z(PxpjfKX%Pv*D#Fyqkvi6Unos!(&&4+*aWpQ2cgHkAb@uV9Tuow(v+iX|zWC8{aKR z&hu_z|7sL-_aS^5n^%MW>yIWLAf+b9*dy0N5^yobs2E3d4lH!tK+!z~yLtDMCeE(2 zuw5I}Nft-qOc<_j6Tk$s2XG5Y2ml|@1L$nNjMZ2Ka&q+M;hA1k<2v`)iewFdMJT-O zxJyAY2VBPa$f}bkviO|{5HUOpeRBIP0kMx7Ni^>@;3IInJMP!)+<*J>^W@^Y@6)1v zNa)k?u!cZxeq8e5JQ0*kjUPr+yZ1{zJkG`(FyV-prEi+Z_KyyURT5cp#9y}R7h?u| zhMivikSi)kUYBp8DRM}ct&$F`zQL&Wx}=y=mehr$ecu^N4J?@{=g($g*4*cD?gON@ z_qQ9TrI?E)>rBray}>xizdU%hVFtLak2Q5b5|3HjgjIVeO)Dp%foWid3o)ixhF_z{ z4?ZMevGIPu#-B-qQusK#hw+UYOioI+O0d3-RuC9oJT}aTE{J4qP}Yi(Jl=ZTB5f;n zr0d#E#Pl8Uaw30b4>9TdPcH5|$ln4XXy++siMzP+V&{Xn5Pt3`L6zbmc@XD1tsU^9 zfA35F2Kn>udFkw)haYu7tD0uut``>b?*25`XHG$?|Hi~<S2m^*Sa6CJ<Q~PnAB`|0 zypF+-<aTbVmn+TJzWX^M`H(D`{KXH@4(iX}U{Vw8%XarFJB6`gL@Y<SWK>K(>a1Yr zYMoV9)#@nh0W;duK+J?u(am#=y(b@e<h34cyX8GqVz+?Wby6W!EpnYa^zm0c)ODm# z0t!E|_2f?vKihzng_O+&i(DK^S)#7OqtGB3{ak5J&&^_;qoX5SAyBCnc#hEY@D`*Y zExst&pMkIFi(kHg{(f)>o+8Nz?al5BpCG_sF^vrZbe1Yky^7m#$~&m3SmYBf7^YWC zHzI(OBAt7Ke}Ro2#vkT^(-+Cd$L4{X%7oW66Vf{56)fxsmpn8Ksk|ZuU-CUJbfuJ= zyfxC1T%cZ7Is*yhTMEHjZ63WO-?+W9MnC}9+Wmc5CW-tLuP8Qk6L(`)n5!7SMh%%6 z_?A9%Op4cv)6mT&A@rObNJ}pb0u$l9yvLw#T$q~3Iz%1y#lsPOY77CBl%p=j*Q5-> zm1>}fgCYt(6&coWz$}J^p$NWnx3!-JU+=HXIUy3G&?mB~*;9+5$|t7&-(%wQS=_1F zF^WALw)N=$3~j2UJfuMk%b@X+q;eeI<^IO%3E!smr_D_a<2T*FlJBViuBs7TD}^(x zdig;6*3=rIflbZlRWutRP0^F)!81U0VO+)LY?z@TNG_n7$*zrHXZAL+bT571R|l9u z+4u~B;JxBz9-5#YHLfsO?q&T<8?dAF{T$l)v!%FLPRe&dTM;=F%Yomd)P89*KhkoM zJ7qJ-a>7r*15QaMUo&6}mkf!Tn1m>(CG&0vooQf9>(vOa&1e!BF?p{C%p2@ohDf+& ztTsdB?OWy)Q3<-<`)#ozZjpYviptj(6}cbc45Hp@G5%FVd9Os<xu;4Ve0N@c|NI2{ zMtnPtH(-%1$%VR-HDNKvq;N)0CqHHsa9qA4shYm>X5$Y63!I}t&!?NEHa%^LZTTU# zcqP`Y$EDU5g#jWP#wPT%(C2g6)2+vq_VNXsJzeZ*A2hQ61+ZVQWE#y_1ymDjZt9kw zz}aVr)-t_?j#0W-J~ZIO$sBjK0k6Lmd<5?*wN4+yb?6q!Ofw$+MN6K+RteWJ)YS$R z=kqkAxC!&HYL~0ZW1-GO4f$)*#k_ezMS_mhK{C$+3MVleqW*H&r7QJqp|NQIUVegx z<h^H3UNyuAj!bU_2)e&mEB0-wYv#Vlgc3e@tt6cvlA0x}Z@`1|XM3|l{kT0Z&_aRG zIpul7N7}UJWa{*3iexe4;dwTUYsi~a+isso3v1J2489d-A}|d5MdhoC#ybhrI$HSc zw29a1;)Id}Emku5y?Asjtjn~~tWONqhYlK7Vr>nKF`kE;UzBhLDj#eqVVX`i%%~pq z3lx>gg;;60S6k?G*HwfZjhQ7KvZYcG`yQW}=|FFkcxT_sXC|tlez+s0JTao(r-M{j z%uVuZ<5-dq=C4lSzJ-o%>KAXsV5hvj39IRW5eU+0q9~wi9BNOj(GxG!JvHVl7t62C zW$f4Svf7e7oHk5m`G|tiq3`k1AxfcB2VdM@&eC=4&oMh#A;iS(n=F}9L7z0z4k!6R zb3fors*9*!rk^!XrQ`HViu&>iw`j_mAQhDlIZH58c{0(yPpIX{l_j1t%NTwWp8qDd zyiBh`h=fgd+p|J}En|hw#=aW1RF|$oQEo035>h;J_d^L4rd3V8$#@BIg$D&IveN*e zMPg}4N4Ms|mR4^zvJCRsMx9H>a;s|5<S(-Q+sE_GTckp*v3fVB)0i)W@m#}8a>lyD ziz=19Y`Lf{?}nISW!Xk}<|f^4vYlNT$L9dIwFK!1P`%hNrE-{$+;Dzi24980kcMhZ zW=d$h7{4?L(#X-vz8L%51o;I7v`pFdGt03iT#0CN*YRwV7^STC0)4rD-d`qg<t@n2 zFgU531)iJeBqZ$EXg0}N(cM;JhgQk&zW5++C?gd8_1HnBmqmN%RvDTuF&g$V>q9`) z#8yR5InI?s7qR}9LgraKnTr?7#1|d-Ig#dw&I(i`h}Vl-{o!ug@!4uVF84HeUt+Ba zwWgq^(ngNPH%gEDVi1q+af5jr3>xu$<}R&$&KF6Zs-=?5;uBAqcfO-E;}X!+7C%1} ze-%P9wkUat-?H7}hA8?z`9K|d|BF5$ebD(Ub3E6Ibk9<KF2wDVJ#OgByO*2a-^wMz zbmm{)sS86g7dZ8^mNu4=M9g(76kG60scYzT_A0D?5-O7@-)ONPrFm#*8Y?JT6c1&L z9oV$|b!?!ZIm<ZBj821Bh7S$-wHHZ@Vd?+tgR%_an9w`>KK*ujs`ITZ`=l%zY-7a+ znu#eW-cAR4{_W(Gwg;BsCddfsnTm2_<K`6ZPa$_3Vj&Q~x*@KY5Dxfgw17Q9t~CKV zVhijF-mF72A<dzzH<%2c+!Z4V6r1-LNS|e4ZDW6;FQO9+ML1bDYKp2MpLC@A?%<)i zSK1-b1znKs^owi)&-+5v1J!=1v3V2wn1GmXSxJsa?TyxtbV3>wGc!da0&$bEy_Vv< zz8Q=R?r$!#<Q=LCawnPPA!Jh?UA%|e@EW9nJi9{URpg}gB%#GvBuLXwl4Y9exn6rv zbCWga<q#i&1Lwg$(hP-O2EPy@0h@>P0P5Q1-S4{(zh}fYzop6+XvZ}plFKxhUcE@# z#G!ck<t@9vrdo-AmA@v#qMQ$eqDInD+D9#%Y=l)VitUwmxfY|rpvQDV=P(=Uhf56~ zVS?nP(Q+aG4>_n8v!7eJ$`qLO({u~a(8Eg<?P3ZIhF1vCbUNk^!=W}TwR#@>S<ElL zK8f=#H(#KUDlIc9vi?*_{2TSp@rn~WhP=NEPxTAdF1)}V?^+RVgs*nI7vW2xvqOtA zA6^~%{d8f~Rt+rfc(kUb+=Qsg%zn&Z_*8*+>w7dR?o3QA4jms&nfk@zjY5B-6yjUe zz3?^W-thRwkI7!<^&|-Dxs+ezkkX+oz5{f@u1!J=zlD#7%xlKJ<uzYCG8Q+mofYk` zOpe38RC3bF|7tO3{4AFq1^<;@9n7B2bej^+wyaWBr1W&Wq5Vv?@ssvc-j9r*>DwDZ zeiz5Os7)s&p7cGkYM;=v=g+VQ*w5$lbkNEyv-w{xp(m=CNY!?w=Fxlr4kF7J9sS?j zcLi4D7f>nqw+hK|+!w|d`R&6KKU$MD>KIzK@grg5V`U%dp^pA!`jIp985SD)VRh#u z5U^l0+H&^h2>yiiIg%D94DTy*zI3QDn$JL7K^0ruzLw&CCR$==mEBH;TY4ey_uaET zoQ<s?d(E;;(x)A3#I78uCGn}7s$2{uD)1{sFOKAHah7_H(T|jLC9lUcQylL{6(wZ2 zO>|#;&HU-Wg{aJ9e309Zm&;NVSQ=4Mzr3$5KceF->_02TKBbdVkujr@NHEPta>ET- zchdXu3fn1RYcB8p_?eYFX}SPc%Lg&-^i31Oua1gZj?zi;qf8LFKc`#27%5OvL+0{9 zw&RjQS#A5jm@e8hOo-hE@ADFR`JyPn_Z**&t!?>G<L79FnS-!AxXK+W_{CGK`Cn^( z4qzs&aEdYj`s4T({x!4yEV>x}UD-@9*d_KV_4{^i&x^Tg4Z`F-Sx~e+lPhm0%<2)U z$*D`Elu%`~+!J1&3|jPeRC7kwDBPam4C?tWlwRCLEnqmpYs~Rv1qVS==C0DGnLb(@ z;AkD_p;ovuT%AQrvPIu+k%&KUYPVK!lJJH<8HK=dJ1O${{KgWxk?Byvp*IfqSM!=T z7hko|lTwOuhUiI|-(hC;Pef2NSzyfJ-Be;wKKW{>;fStVXzMbbR#XuY#b<Z#GE7^h z7<xIZ;Yj^wcPBxsrLW)A?qTaE-!Zl+EOMANOB<Dj?re*_LhGN{sKTy`!Iv_r)}-y! zTnI~x!6r1P=`;JG1J$M)VJLkGWwGRyo@Ok0Us}!4r4EY%^X!gt&*8Z``$H>9S+%`b zw$@#S6G>XJSA{LM1rK`cY;VRjUpYZIjWl9uhMC!3t+RHahJ&5}4PjgH@@y%W<4R+( zMu|LR4lVVfyR%jAYCLw=Akp(r7~ezwgN0Wz%?YSUv5vnNRF3OLArG}9AQUymP|XsC zgQyGNg*?fKpI)ych?~!LAIF<qUjP08IpGXnrr}ErJ^YcjtMRq2W3goDWxqfkTb}Zs zatw7~<7Aq}USOk|i=D&CRdHSAel2?0WZcDiioNQwXq?S15vR!R0n?kW_x7QZU$k9M zD~w}SE>#_Zn-H4bq=nch=qNxG{l{uJ%8+Ni47{ikggy^0=(XVOVCoU!OR{kiZ0q~) zR-$%PpAb1#E%+uI4Z37sR?+yg69QpN*6oYN%N$J>Sy5Yo65|HF&wpOu$LHP&MaG;O zjz)1!tV}ZV)wVo&dI~jmj`2g5qFOl_W7+)j=mMzNy3jWJt=0wVZ~Trb$^@z?%>STY zqk@khl^_I8{5h>f&P0Q9kHYTHS)0t<ljLh}2uAU`Rj0`BeApII<3V<wJE&9fiwG^( z=QF4o#-?Iuh^dWr$$v1+{13Di=o1ECOl)+;4vieDLZ7HszL`D40W6<ZwwYwIrSY0F z^5X_tbcL+{pp8hOLOiRoy##7a1|sh~Y?=wLJgr9jikl5H-W{#?)y`uS9FenqG8?Ln z6m2c`Va(4yN%K#eCh;;f|5K9*2jfCDQ>3V{b|Cmm+wxGHg{0364e7<oUqiqN6*S-3 znv@vy4i|c&QzF;Z-NpEFF_*bt=~S^SSAmn|KgcLM5?;pUi&9UQl>}F5QIbCxx<x$J z!HG*MKMe=U-%Hv4Y;-Tl9+5k;)>)4~qwW7<dw%f%x$|RxOZA9j{z1L|=f~SeEa~ll zfC<JwE=f_<fjV}sx*^=b|Kgkf^V>5o9&zy(%L)_!_iq2$c=N-Y4_tzKc2U=ko*wEO z8uAP5q037vQOyJ*wOc*44)UwM&5p}}2@R0$J*cg%AfMqIes{rK2_#9&>%OYsY773S zulcXBKYsz$b99nr@wFgZ0Q<%_je6#QFbmjyHl6+_&Qw@TcyJ^nu?7Y;M<gxrwi2)d z&kjgpwMG9l@l-LtwYH$4wDZJOwxcaxQ!J1))Z377lY^w<Pt<{qiFCecbDKhENp6=k zJ<^}|oo`MxgS|+8iuLnV0hZdIxcuLDH-iJPbW<xF;eUDfOVmdYp3n+|;V&%rf8H9F zJp=@Uog4Dzubcnxj~n>MD57#Brv6K-&2k`68JTjA)xUoa)jbU8&2406_}Kq8_t$Mr zXuxJ$RgH!b>i`vLGMnFNIF9)J<QQ<|GJko}^7I=Y=DVtznnzi}3{La6(arcY%AndX z2uOsJkdUAxU*!w`_lBb{9>LP@Po%CjC^%1M6)7JpN__y#m2h{qn|;yb_N70I_nB+U zq5*JtcQR?C8t=Kgm`D`6P*PW)s(tqjWngP-x_WjpPcr!D@d^+`3UvMP2!njBZki?p zDCFkg7l6xrc!W*YK0UO~?6v^^uiJ0$o&(Ua1k~U^4+1p-iBke=Y=o1*f2`|j>lMOp zP>k>Ky##*P{ATVos9S3Wb!gQqMg8|I-S2lZOpm^PMdxkE`T)w}9OHgqDR#a*EO`wO zymvtJ@(~OUL*-}(mD&iH6zj4ZEzV2Ta=pOZ;3I%aGc^Cd9Ml0XlIQU4G4CWIt(oHI zuJ6;g0LnW961x6C-M51O!Af5r-<&0|MSn}%%q+J~Oet~4CQ)Z~o~<CRuA=i22!=lZ zWXW8ODcA8w$)jHCEpTfLIv*kgVpX|4ce|V};TE`f$OBI@z8HW8zA(sWv&lko9v-VU z8XI}4XtaXC{0MgXzshNNQ)6NsORVOMb8yCse||{}k&|Uf71xnUuu%4+wB2FS9ITjn zq%7F`0}!#S6y+CZhPyj>XmAq$bPpPI1OAe0=K~3Md<I|6Kxsh`IIK6(q9B?>qaq>? z6$9(ZZs(Q8e))X*T^hd7l}N&;SC7^X!M|@m)phJ;12u)D?i2^EUT7F($}AiOc>yim zaHvb=BKr6SzPNeagAy2u$4ckVf#5#)n~$~5XU*eD{^x@Fvq3PDUj=t>St-R=KSUIs zr!AER1nZh1qnDPRZB-r7y<9SWMvMR%sa{d~e+PIV+GClyHOz^=w;Yu~dxFqb48XiH z-gSZ4dW8MH8j-x%arSoq%%J&}&ESpT^G#sR5e)XHFRJ|vNr865qe(ES6^;1YCCy;4 zDFhW~m3aUwn|q_7kyDrVa9Jj}?l$m>qthM;Aq$u`+aXpPu6(_oCOgE#-TGEul)%Ew zOf2Gg0NjtxA1kJTM@D}KFp|4B1(->1DC}d6<|^s=C2-)_0`-pfsd;xc%k`Er5?82( zRCeFxZU9hn1Vj_?-&G)etiH%`?PTWSzXFOtYeP3+o74pm+REBV;%9H93BX@Dmh=4f zh;_CRLDk@?<Zhbo&a%f2hf}H>JQUfkEQat7z<8L-u782c)`{X7-r?HaVC=pHrW;T8 zAJZa$-%JL?FRp1GK$+0<58EHZ#p6i$$CH|H{vXcXF*>fc?*nbpU>c*blg5p0t7*)} zw#~+N8#_%J+qP}nwt05$NAFqZ+xawEvohMVXJ7y8M_a&*()<pBPryiUWdNVqkB1?( z6MakX{_P>-&4*ha&bo3QaB;r#A2GQyGRH}GgkvWxmSx8P%2h-^Pu2VO<n?w2yzvZX z(_akqwwuEG1M^J)L&05>ahrwc+3$ih3XjFuO<S~)^9+Fackd=7057MZrhWqEWIMpx zt_G1s4X3z6qZOPH!4m=Wd#&CxlXX13hh3+vJ?AO~O42q6J0|<YXR6i$&~87@Oe;z} zE;vfTV!4!uESFjn0N<W4yu+B;1#p_k#W4@SuDtj90RX^!sJ@3hivQTB=vLB<140&7 zG9IV%jw@u67<;ts-XY*6Kl1edE}Q|O{KrV>h1-`tR>pWDpgvstVW6rotxR&F6JP=Q zn>g>+{nBo?GeV5Ji8h5smq*yvJdP{+?3#tWmxbvXZ0i|_iT*?*5ng-+vKwH1{tyQ3 zwY)waaedh}NNy(~P(9VcwrK>6KB3e<ud@kS0C$4)CPx58V_;56EpTC(`ShJ=^I<Q8 zPVci1hy%hjb<6#<0XheFAF#<{f72^RCJ}At=4$K1A^Y5ihdjIk$%T2-S%YS|DEGNB zI+jsmn*ccNQ+NFkIK{UmkQ&R8zoa7**zLO>rdqewY{e|U6qPm(N{FwRhC28sNG8yd z>bW-nT*~j$-UuAAYg2+;_l?7mSsQ4cY?Zvs=yC}YIu-w~pof{VpOE~vHS>Wv#TpbA z7?Omx3x)|vu!VPIH7!#Zx$DtTN}b)rokEB_Uwj#P*<7ydmu>r!deE`E?O5+p$=RyI zMQt-93_Id#;0)meNxJW2W_QPa-ZE{ZDn-3tJP;P-DY2Q>EkF6Hcu?dxxS{Uu`i@5i zN|?*Ue!|mBR@IrzMz4hD_Y9$_DT8!4?3|b)0agyczpOZn9VCJZ|E>A)u&DR^7pde| z1C#Dz;SwX#el>sV=R1VX3CaJgiRH=w*JxAHzADDK16pjoQu%NW!1^|e_hue?Delik z<_{k1?vzgP@0kt@%A1gCzxK{uyS_blA%Fi_#otfv(}}ET1h@>>g@GpR)G->eiYAJ} z1CsomaxkQcOikX>sx<V(^D2(zcZ~ETTIW5@1U>LV*>dxuDl1L8relgWh(=ht#Kgag z6UdTzr%+h;<*%C68E_PRj=ShEc0*WQdm)77J+v>HrSL5eHE~U%0)|TeT-hyF9~r7d z3l*CqH(@I6ixYPXRB|5xR8D|>M*mc7{LT>`X*yL#mx;DG%#|>RwT|!Ij5!anQ6e}G zlQJ;Dnjl4v;vc0-jK?@E>ZrZm#)2=b9Y_r^e8GI%9n@Pc7u)N7gYa=?6B12#>>=m3 zG~;co9IV4PY>scCM-VQvyu;315eT2X)o2Wb9l$%Mk<)a<CSV}pNvf}E%{URv&gAHz zat@-Q(mkj@pLy(YQKHgdCW)4<zAxBef+czT$qvs_pF;b^o+Y$Re+ar>4KaqYXK<QD zJoWzzv;c;PN6`CxgDh~%i&J~=((b{c8+g)GJwz^;$mv)(-lJmrGp=1I8N=l28=6lL zcq%<_bsMhO&{e9)s;V9l&AFug+%FZH$@RUzb#87MBlsuCvIzEzzUwmjQxDAS{NDD@ z%l2bFLu4WMFtWGe50#_M0%)HH=j8^F6kvZ*T1X~+`NIi!a!}AyGXu^&V^@iBpP>Dd zBX5CHqwl1;%$s5wAqO{hUQ#Y$+JXwk(LcFyyv#^KgWZ5_vO~NhiiWWyYi&Vi@54>{ zrYOEwl;GS`1nifu2HZftB=PoV%xnP=ypwv(ZT1&jpoRC__9w^6i{&SXJLxD0%jBaY zfK#;TpF;8Dr%frDXFHtCSMJ?ddOwf|$y5?dAEbtNkMs8z`BCJPk<sG_O>MA!K71ZB zEcrAR%OFJo;RVla$ehB3hcXw)?3yGNYC6J?rVcab`L)a?hl`Yw0Mb93;yc<6b<Q;l zWPD?-1~C&)MB9Id$c*>Vk(Y9IRVQ?fjJ{lVb3Ak9i$GdcWc)3j3HQtOR7*9<WO>!z zc_JPfbW7@G<;oYka&GH4o8MBb$e@ez_X&k7Hx8F8GQkAm23Pn~Y0hsQMm(Mx)EOL& z8!G}0CGrKGO+zX=Js|N;YvAtXD&y_A5Z{VgdqLf+_DB7wmQK}Ba=VXAAj;fqm`t={ zfG`R)F`G}4-X>}}*YLqt+BLi!Cxjc)zO<xBXRGFm-W;bMG!=HJ17<uv_MQk7T+i3K zX!+~>Qe1?_$m{;1S>Ip5Y$)S0<BT^}Ud3Y;d%5ByJs)p7yiN1DhCKjoeo0tL45p=v ztPr}s2Z8?+GU~Bt{Z@@{#FrS}t2v~a;^-d9$w07Ehykc@B7;IfIEP#tUukeMs2rlh z5}l2B`y6y^xG!f0oadB%6g4+_kg%w%p8|Ae$qal|_VYbqWkj<pzKywbjLmNL@!m4* zAV=r=waPogz`lilr^a>ynIgjCYT<oJs}2>2B}<q=M}_ApH15J*m|UEW1rOyoI;}2$ z<|XOS`SPF1+9dED<@rq$CDSIt|E1PN!ca+SC7Q@jW$)SsOj0sb6rSK$*e{CFN6x-T zncnQmYuZU9O%S3##Iyb$rfaB^qJ`FbiUIzQSCyXl1hx3{he2@6)=ajA`U&Z<1-t3k z(D>UPe7n!~v2r5ns>>Z3;E(*+>$utk@;2(>Qi{4Mpan}TtKIUrgwrMr3vzNCLOUIh z{SpbQO#Nc0t=0)eCorfwLy;NaLO2FTJ{?&h5^|a6akHr)^$+Y)_$ewN>{AuTR1;AK zZa}~)ga}oWfTkl$r;>Pd(?7buivzl^It3F(f<NMYnB_(1CN*?)z1nw9twaQY%Asm~ zpm~nHCrp^VKY@qu2Ao)JB2_Fkqtr!b&I@7dA=N%rkMTa`jO6ori|TcBcQf*bGw(%9 zDGmElNI8*+A(6T;VIFS*g&gu)v4-VX6*^#<I&_u`3w_EfAjQ>u*68WVtUT|=&w{=Q z`1IB5-!=J_NYt1v9Sc36>dO}8$YKgh^F(tyEJ7zlX214JN5QBzUMlKXYpn+`@3?%U z%TjoXug+)u*v{2BD_dy<D;G<=+!IGVL_PSA3@FqHpGQx8q5anR)lqk|T_FXEZ=q(% z1qU*pWsd8IdbRg_WumPJt-qvGeD$)lFm&x#bdHsP4+EVV=DTzsC(Np#J{&m9otD(% zCINSYO*C49D#GyoGU({-RxKXmAzc~}7pwkIYWNcxW7!1(?6`Y$wa;fp=0_p@A;(;` z7;S*?*s$Emi4j_qeLK0s)g@(33-Y?g4BhgDqj-c!mb=B<+<l%pZ%M1v{A#|`e*xX9 zO`vNd{(XoVB_f;#)`$}Og~F3>(MAL94EizYQ#~{c4oWo&gYg-~$m~U%twpUBpJ#qe zdGuE}YzF(YEzvCGEOpW>S6|<kWIY3$AUly-r790XE)$awZzh4^I(t-}FJzL#jpylM zhBfgmdlHdc!^<p|J~$xazgBsJE#A_I9DgQHXq^9aL_+wJt^rIu*Mx)O0TXC^7trrG z-oP6gN?}mdK34qE$ugF8ol<AJIFN%lb_7*ecsi`aE*{y8h~8PrEa>E{oxm;-+R-Q0 z&B4LrgiAj-rQI8i^v(gh;C&eIpX$|xjZTj0oOq@SW{;ULOP(ifMtXZ4Jf(V2Z8IAN zbY=Z0zqV#s=hM8)EsvgqPqdrLq+z+dGjql#I6&5@_k}rAJ++$)cr$qBbzbH{v#YUN zNB9lIk_N<UJTh9MO2g?@n%ZKD$B0Fz=e6-Cnp_YaW0FD2xK2Ql=wT7bvuIL0Xx_&e zE}cC!iJa0Jo*k^iC<Zobeh9K26(Ul%X+7cZd0gg0cY38TRjmb+-7E~a*VED(Za{h2 zsgo@#G`zBlCO2)6t@C&n++}6m3QeCD?qIp%NsNSXTv9z=L<O<VgcyzJ8n#bfZsLwk z$%2ryzV|gA=7m$c+D65S^h+p;VSt@8@i<Nhte70VILU;f-4_aYDGso0PUa#_b~_DU zv&yfhCgJ)Dg_wt{`#v~5agl!$5tMJy?AdAY@XQj@@o8k$)m1dr=W0Khc=ol44TSar zpau}KG)|YAH^ZZu?IAsh9d{<KTf0W5S}per?A$b)pZi^<#2e*J7@__EY?Py+O;KgG z$?VD}`qDNfSAZ+Y+I=tE^CG<U<Yt1b!u)O??}HL$^_KGG3@)p(X3idzQJ&~uOYX-( zM1>yBAGGzm({}y!Cfq7AmMDW0v27YWbUIIXl5+W1C!-&hM6M6P+}s4&Ze5eepor)` zoO21GzdobsbM>cn+6nmLsabcz;)^eoegCn1y;+)Z_l2-b>#3o@i2U2*CzFTX+~6L& zkfVeaJQ`(Zi&ZOpqGVw!MKWzVzu*7}j@)t@x2iD<Vg;30cf@C4-)~>*1*B`#B3hU= z1^u|+OMkdli%#=Ukp%e<3rIqpVh)XQK{iLN&wQZ!`xHY|k;;Nl7N*{uX)M1F3r|#v zvvNZG*h5LgyauH}J9gD2ZZ*M_sEK@r(U4kY-{W^aR>TZ0?lw$w5m^tL=wk#AnL~Lv zS{E}Oo_?$Hl&ln7_TLp9n0Kpv_}!+$We%FXHAw_A>Bo>+g5Wq}YM~A7(lyTYT~}8G zYj6DR5&niTPw$9dG_vQ;_CpX!SArNlmr)#mZXz4`9-mW!U-kg7(5MxBmm5y=l}~iD zKqbw!^jQ<<$EBrtDxhwARz%3ivy8@zS`_hiXqR^K(7-RZ5rmKqwre*hYDcV_$9E8_ zi9qHvrQUV!tD)C2Shd=c%fvt`M-|6)T9FtBH03l*Jyap%D;)A%her8vpHZa}x6|nZ zM8Z!Yt0DB!Q=|k59gNCA*ocgfp`{&?3~~MuEHOFy-&)$`$QzM5IFDiheQvckEVV*u zAL<nLywxS@k=iBF`on<{*B?PK-E{i-Q|(@^o6X;aupu-a_1j}<OX%NLFCv{2KfeoR zApFxZ%kPSSE9X<9Sk!-yptdMsL%3CHC3Qc+rb6h21y?`cVScs+mM6fmZgYIXX?8hI zrasNyj}eNe`j)fy;4gPs_XU^hvnjjl{`&9oZf)MwVF!e7BHA3Tsq{cSgK$WvbQ1R7 z*`UKLvq+&Q)+7Ed{OA|GC~=b$QgEWW7VV!*{vEKE<xfZKA@|Dy+D|mAR1TLt>To>Y z)tJ|K!F$^>M<>j=)TBj#VLLlK5qDdfjW4CpD8rJ+YvilD-mHUvWW161Y=AD*hk?Ta zN)$IH^vgNsFSZI}3QHlILXedQ%e+5Y)WJar-QXPp9|?nf3MiL}65TH5g1~JfKXZNX zkr~YMI=4!tPE{OF!EAPnz=~A_!x&e*mgxTy>z2?ndW{aIYb+ZEC{O>hof@YC*C1u^ z9{O8csqW`|JSYklrJzGbWn=Z5Yo|E*$`rxGm7}jTvwwv0>uVB*OZC9VsbK$cK~75) zJ7$rnTiW{qEgfl}jO-9r($80lRmwX739L9ZMjcwti^ySSM5M76al8G;z;ODBd?&Tp zku#PeL`HaH*Cw(Okvx&y5_;Z<Zg@k+17YsRXTlu>C*&SC1hCFZTKh=}@}zX*bl2Pu zV=7BO1l{uLrBtg##qDnwj8nqjORPVUs@gGymIz8xeUWY*l~)^-3#gCzcABdiPv|_6 zoj*YIeF%)FPcl#4$!(ebwyA^P<^q35Uq)s=en><Q8sna&8I|`|T2~}51ktj3l4-UX z1?!Nth}O*kr;SI*i%ZJPS~*<O&<*v(<9G-+SkmxPj$T0FpQ8r&46L=Ny11@6lUVFx zys4>=AwrjNziz910zVPAK|g_Stnn2yR3Ere95t~m_eYmD3qd(yo!_^InFwtXT~^&k z6BcK;?^klv8Zwn%3h(5jfhCOF_<JzTC7moUnwvQhm&m#KY&)I$D)@`PXH3(qHpS*d zd&K72=PUOI_EZUFe}0I_r$&Z;U7g8mv!pxjsZv6=MIv4IkkLC7AJxuRi8tS`Z}n(M zp#i*r5-<f@tGI?kltSy3d8_%Vm*`qSUbrCqm#+}Hh#^eS*Zq5`uS&z(?&ic2)Zn?l zIII*CtEhgl2n^Y_{3g=fJ?vQ?W5e8s;PU@Tyd)193E>!6iuhNzTQA7U*0X|*ri7T| z+=8Zym&juut4m9wsUR_~d>af)cHClAQqp@`;c&<-VJo+_5TUvTR?rd}h(XIpQ*B~Y zdumZ%fB*AI4+QfMioMSO;dW(AzG&15h4;o7w_#!1TzT0LRXO}s_tV;ha9i%waS&;D z2KK<>_njpxDsv{36<_`$6Ig7>PX(Tgn`X|E+7seqMB}|47E0YG^1ryC7HLx7AioCZ z;7za)EBU7)jF-xlSkW!VWTXXCE}oQ&`Td_?0StIUjJg$bzR78JJj)5!k7j3>qJ?Z_ zl8D+Avspop6Jqd^KWQR<*w#v-n#atA&RpP@!+pvk%d}t(`k5=8589ci6NI^!MA*lK zwG~410-28%6_F=MtOzE>IPx3tue@PG^yvS9`WeFQff_)9qLSqlo}2O`S6{Z{=|7aF zhIv>&g$(s|+LaVnpX2mDGT=-^LL@GFngqxHWC8el@&`Y3`qo!eJJhskWY8%>;r;tl z&>)#KX6%fp6R2@hCc@e3RN;mD67hikrJO%QRy8m3bdCCslo-aJ?Oh&q^14@v@y?tB zTXuTz2zZf?$}E~6F*E*iUC1N_zo<g}za&QH8R7c4*P1RF`#<yvFn(FRb0Aj2*)9Kh z(0{Hez>~tf8zW$aWd)4>&z=7vNmkz3s+9<nYK;F6&EdoELx_|x_&GS!clf_gJBD;_ zj`Y)sdn_2n?qBCh9?0P_0g;68IFE#E{=c+VW3cxc6cFHhphqId^RInZraBS@4b91N zRE;SAJk7lr$$!7|HlcRwj|f<dIcpVoaQ^^@<8?^E&Y-7wZoicOzVyGI<^3~oh68UJ zFOUkRmS1NgG<mRa^m+bb8gl5(?rplb#XWDh$?-Dv*nBA@NK4aYCFh4_0_AvK1U&pq zTwKjif;$Z~EYMw`M58u+zIJDS2;{WJ(<+hK&H=Mmi$O@+NMcD@f$89zbxAI3!q<Yb zLNn#Bt)-OUpUxYVijt{VSp{pXb_U}kkhE_m|0Zde#nW8TQHutr8wn0ynyVD|)9e<- zMDvUk30qoPo+5VuU50cFtFK&NuR^B@|BsrV)0ZC?>jd;oF4qyFH2JzWk!Yvym_6&J zuV}2K)ZE4NbV=(nb+9n52CpTa|MTPHCR&kolWa!tLVDYNC+Spf93nyM)`2kyhwSa> zPw!)37Al#=TTGNPjn?xr<X)Jls8nkG$Ut839a-AbUEI~KayWKPSKlZF4(r#7lG&=g zSI?P00bQ)iCP7i@%8K%gJB8GHGHyoe<GM>}k1<(CW!m}s#g>gKqZx>AhkqplrRQ~? z6lBtD&%BIyo}<fcu1MC{ewMW`9vbnUw-?o|$TRD-Lp>cgU33IB|Gd*=I&F35mz(I; zUu#<(<)ZUEnHj*)Qq@*yIgR9!;=ZllocyI|`&#u|t928m=mXAa-C8d+UbD^RHU{03 zv%Y6b84jL<&7Kxizxd&*u%Y1&0O9gkE>^#5H~<kkKoE4Z%xRDaF9T7GqwHI7WwZ{~ z&J$|?JHxNR!FAi*!M1sqL_CG`yHebLgmm>#n_GF=jy@Z*JV|Rf2i=H$)l_<VKK{$% z2>*Le5x;!LZ7Ce5^w?8jkB3EnV7+p(@>iX6URYS@^HhIma^n&CNODv0;kaclC7;)i zndO}sg<TptIvQS&ri(eLWDd`X!Q{usteuZf__<S7trE?3XHj1AO?HO15(CxhR>(Nt zUG7%Lo^R;Fw0^p-D#%1G{OlWv;@-T_ez9B4kx&Z5h>HdE-0pmd9us_dYmR%}*-;YS z-`ln#V^GOYUt5948S&kkJe(tk<$hmyDkd1I-%H1!YcaT^^K3ZVnB~Idd5eOb1<?GB zjEIgwET&!Fo9(97rxsELZRCcCRU>nD68)akg|bzTsZ;IOPzp_h((Q2fv(>F4?W&u* z<A=S7d55Wky0z8?WU=stpFw%A_d$QBaw8Y*Bx{cMr5hd#Eu}dO??rL=o_`ZWELb%q z(6(8)Pdq`P;y<t6^&Xc`MOPf%AVlFE{B3Ub0Ne~{NVzlubWxrF=>=F7=7B=rnfLB< z0P@`zCz1O<KZZ*fpSrZmW@74KN@0lO6?kd_is&ZS%VXD}Ld(aOht~pFm1(##OqYe< zWhyg(SntpmCq1p^Ni<u9<pi2FEp(W-Q($e((`NB9xiO6_ym@`Y)cWg>1{=c>g0rMb z6Oo%){UFVU>2L|AgyV{f{hb-dnj34pl;J`QH@%)zjw!3gf~?xpIP=n^r|vD%ogF7| zW4_yY-@SAiY*}U#AbfKyne@DUH&}|bz~f|Qa83)SWk$KRwCaX+GUO%jv9T}!Hqg30 zrmTx~UZ>}HVaTYN*Wj3)OHqhzx!mZX@)%bis0-l=Zui~GNC7(1SxqhLPyh#cfJw8} z%wXt5c34=L3XN*?;txj0!*$y&<&liG{l5u=V&SU1&ZC)*vov^$3=Yrt$%b*%yQ64_ zh*)gWnrJGob;_K=oMxNx{=5du_Yc3x;3+7vxScE{el$d^#n~G>NB6H%k`cb!0A8f> zlK+bMwM;o)qx|nm&*KkpGq*_PFBJ<?eatH?bvhz9n`kpk3B|Zv&3en{p?6knrM0CO z&Uek+#-=kb3K-4COBZLW%N$daUOcQk7}=AXn+uXElvg!EwfG$M7LS{8cWsqAd+>ml zLf&c83J0Ew`r%}S&g-JRgjLfxSk``bQh9Y{(6U5!a<ko#X^nz_)#AMI$hFL3Ivy1o z=p3@}gP+jmwKMQ^tg+APVq2}(yXJIMD*8rEwT+Pw=PFrJ+Nk!tZZ07b5KMfrB&%Y* zmU};<@1D+?Oz|0)-9=WRWb)VMq|Z%FP0z=t^<6W)HupEq3RCiOvk5^IEWmzg(S44L zW2v$HxhevO-C;jCf-TA8LPW6XaPaSsHm!RNR{h>*<Me`54*d<--(uDchNz3P9LY{? zVa@2`-6Et?R0&8&MQgv*S`tvwU}$J(NeYx`elVU(4N$S#oa-<c%>GROf{P?Ip(8p5 z@LTWU)HG|$=fjL7|MrZ%FJl3|@0?g@4vhCGmv*xI60E(&8axekNvZWZWCA){8srXQ zLL7RS^0!bZMK}vL>%yG*uD2^WQ8XFoR@Ybu;huIS$D8L-$5dJxxw?v+d~uSJ*%v-| zwCRN8!9l|^8Epn<tu61~P)AX^q}#$o-StcX1gzGdeJ4wv`$A7|&k5ko3sKyPympEs zzep3WfMXJTiQF}FGgBKklHXl@|K&vEuih$-gHwgsfS&e|vJ%VmOl~wIT4b9giO#VS zjfU#O<56dGs2mC2u?B-zIu)@Ep|#^?h&n*-1wNgeX-es;n|;e<H&~vhkzv;jn&RWV zyXZ^NyJ54=lv8OILl>s(SDCkDHWUgns^PYz`k^SC+r_p_9}t-igD~^0=koT9X^eY| zrE^M}You0p6$Q)x|7c=x-lZ^5>=*fX5Y0mSC43kWk19&)e6F|hdm_$DCfAk8Za5^h zqKDY4oEU{U{We1tU1#owua$?KXXv}F9;zA|)qiq-cAg7Rn;&JQwJ4fLwYAzbU64%A z3JM8j%i47I3plqOM|5(+yU<nO4Z>_!)b(r7R(9|{DTZUWJRpnB(rVYPMsk&?)sGLE z&X=lHDt?};?oia=HoF=z=6Nhv7fqAYFkZ6kv#2^*&dVtL(Yh?x%dli^{rf!P=?v9? zOSW<SVGmeY>kJmBV^XYG&8}z)0b>e>^{Ve4nX8Bt7bN)bBDumjt5e+y8NU6o8`l}5 z!^<a};})5d(48$n2eNTB#_`;69Kl8v7^TW>2*Uuc=B!!vxskqCiGg}=6irCEm2*UE zt^5;Cy??IVql0rV93Gd`y#FUw91PyKW=-L&+#%Cc-n5r~lD~e?)ErkG&?&xSJz=v; zh6$A$U3&{gsk|)}6}tWR&yCRejg4*#m4-my)8(A<29TNRFv_uq>o6|h1X9^uGumAw z5ET&k&(bRk0>uJE%Nh*?r(bWnVEb!Y%DLrQTwKX5ebx#J!&2sDrIGM(p@!pIETmqu zyH+}zk?O<jR-iVQ>8#`xRUaJ9npsPh(}M;<<4|BWM1Vb&iY15BJo*uW`+W^0a3Rw6 zuIuO*;IclSJq>#4l(7D~VEKC@B?`;!go)|Q)9`f^RTQssgneSS@^I0zFM@)GQq4=a z$MW~yGM{A!r_c~9Hj6<~h3Co0Ut~a&;&EG?P}V4zTE5Zx)PU@8=r}jxwol|PoPzh$ zKO(z9hUaB5t*+!P(m8y|5)m3BVI-AfXVE;+y1c_Eh~P(y%LVnE7KsB^!^5t9#?Q$a z#!+S~k9D7&Y|)0O>mBN{a=E6PfxP)eGqR5RMF9v=nPs!lttjc14JhRG#-%g*tyTAB z2n7|*niC1~1bDpNk+Gh#??g3|0)Ck-+cPS490%JSN2OdY!-*JlZOwL%=IZ#)z8C@n z*EL+Hi7)$ITX$}IZd@nK){{(EfR*qW-#rvfyXj=ML58<t$F>g^LCm7^U>c_k!@$aR zTaNe{L4KaA=M=!!-UYv7(({<OK*I(={W5w!UISGg%Rq87AV*pSa)#Z4!uJiOkn@X* z$_h(Q23g7@vk?m^s0SM0iY>7<h;DbKbS)FijtbNV(^D(@-4Fa$xm6~WCtTjQRy<MO zPaA8h&6AQm1=LK_moMX&2rX{A@f|%yW<id{He*$jXKwi=B}Jtr=1UNtmF66-8{d8g zX}{^rmtck^C@RlP1M&vd^*O334KSjG{0*rx(Cv6$!PXlm5-Y|^&ZH0?nsQZV)cov_ zLd?tD2AUo0N>DgK-7D8xTwV^;j~9F}KE1|wrD~6Q9hf6HB2H(v7#t<eeCyTTbsuR| z8?X5Q6*<Z>ndfsUP9(wVHJLpQL6AW1usW-i%R{h$^+Pht4gB6Rm!Y`G!35WLRcqC$ zqp6dh_-U;+1A~jeTDNo^FX-Wh;sZDX9Xfm=f&Kv(8K+C-rT1nEKpavBC=>`>w_`}e zA}waOJ|c3j9{`qCyLsIpj=XhH*G_^qula0ob|mLuoJu4Oq2uXtLn{LIG&4vu@ZC3w zO$69FQ`JV)j>xrNE~`jiJz2G#r5@61Z63<bJPr`aPvfbyxJ+|l@kW=8^~4r0)ThQq zv>tc-Y7fOXv}m_lTTIzj_S4cFofmC^SFc3k+pFjs6pOMG?jOP%h4^9POyXUJzTdKT zF<3KKsz&cM)fI)=uwaB$uu3a8ECsKQ>p2`<p}&G`qU*eHR3<ZToKewqoQ$Tf9`dh} z07Hz<ns1`GMCNGm{l}INnCHX6V$6wI1QR+?BCDjU_+=tT7A-gRJh#JKFn*c*I+lcl zjqaN_BB*mXZu_iTX%B7tF>}obk<oy~o4RmK%^RKBElS5wDvbv^>dUjiL=mit0h{=e zjEm(&=$7-UICK7aWg+LCBDqzv7Ol0#W5l21IRDr)u@J4qDn*$CO{g~6*h{55YbDDT zFYB7~HG=Q*lYB|Zm%P3h1eH_;u-gSo#mbM18E>2z=Ro4FP$r;!e7M{hjV5RIAWZ?( zZJ=R!X(#A7v4c9ZZFU>2xRrp}aanEcjKUmnDx(^78Z~Cu%WXjVBen@t49i#=d8K`G z0i3Z2?CT$7%32@I9^RErfTj6eG6y(7WiU9uA?qAU8{D6Hy}1KU*>@b?F{Wl}4Mky> zE`{^$gutG)HNs<yZv=8nv-5lGZ?5~6<1JtQyEj7{SH%7ugoC03(_gulQmECo-ixNa zrr$K9)U@zpSZbV55!7is9Tx+i&a|iJ_g5mY-Wr^rwcJ@1=Z5R|tjb>6>`rE+@9K*; z0wifg_loLPHyIlkxK6J-docb=f+YuATX<|tN;aQv6rLB%X)ehsFdbz<CyXUGtkO2M z0!vFNqDB*(Q~4c$f^*Kx0wu%1K0lLycwLM{%(}^v&wyJM0YW*5-_>*=5>?cS)hHj; zkd2$`_t^b-5v(Q$S*ZIN>(*^befssa2(_^1D$d(AuX;0c-DPXRRUVD%hV%gs8&=7W zGNOCP#V`W*6MxNDKKC;)0u@Kg2y?e72Z`)ecl*8;=SDEpb_Wmy$}3%Tb93`Kp{w>K z$dzD2l8`7114qRIRX^PJY$e|EM@wA{bM`@t#2ZWFEg|gJp<Y!I^D%2~-n&(Wxil`$ zqvDo3iO0><T4`&H%XvQRgw&!KOOCvmC7v72+di_qt|7u{=x%zU?~K8GT!oyG)6PC7 zt6GWltUp};EVvX;w1?`jVIN1yNDqbm*2no$-_&KSD`rUkZFp$Lg2tL})#URPN3b6c zr`(01HL4Vpm~M0D=|vXNa3UY)WVwOEA0rz(!+aGs1MooO=2h<**(IR<FS3j4tQqyI z`OV=B(hmOs|IE9nEId3MFeF<Z0GL8jl8y-n2ZuuL_a9A8mZqkZwe+5Vq$?VXMMql! zNiZ?9XvR$370M5+`Xt@`G$bU%=gU3Nk(2rJ3-9ATiL4j`Ic7qns{o1z5HMq-^jH36 zhtA6t;OXy9mhRrq{zt!3m&56Tf#2k7K>re8BL4pR>({UAYlnO8sB@62%lAh+rr?Jn z8P+6RUYD13+mAKwPy96KdZVT^*(hj3WI1|xdoNv%oJHjF_bahrazw1DR**i!kukI> z(Y}4Ci0rpSQ~i)(S6gcwvO$<Wgh>oB8<;M`tFyEBH?zKC1bzw7pZP$Z!6W!QX3U$Q z#G}KClF!c%VHqAU7th~bZ^;n2WN-<truM$r+T!&^5*zp#S;)Q!4RiRr^t<a|`Sub# zpRN5+OGY-u&4Nby(Y`EeonPpmGWK9?iTQR1KZm#_Ek6eW6ZA$RgaB@IFbO|ALi?=$ z&W^A?E&#)6ZlS0cI$co7c6U?jJ+q?ZR$ZI1aM1Zp<RA4~x3F+vHn8znij_1WVtYxR zm&0U%<d>RK<g@fZ@>?F!hs^ESt2x_cAYa%*5w<34YS)yN^UYrOr#35QC!~}<`BDNp z#<Esyc}e9*P9md5vOGj3n*OL2g={hS#6se5tkA?2-Y~BG&k>T!uXiN@Q;gZOsb;;p zO+680ulfS7zRmM$EqJZ+?0%Xtf4b~Maj>6mWWsUSP67X2SGDOBFQAL9Mxf1`J9_8& zcXxI=)-Xf0-QBseM>m>l*cfktaw2Wdn;BnUU(2Oh0oLN8qK_j_fM^da>+JeqYW%ve zsHo#HwgoJHV-mbID7oW0^LV5D3{yzu?sNrKDOwI&-`#i>J0dqb`(x3^d@2J$I3^WP zpiWL1V3GpTNY)BP?XbB-_&7+n*upN8r6Ma4VE=S%aq%%Iy&OThqT#VZc+bn{inQOW zoKk2-=N&+Q4(jRMdN`=a?Rl{EYP64?I9SnI_{4%VM16J1jJOtiJN8oZU|Y$ztj0_x zhs9o2b62Dvj94*7KP+&;Q^m^Wb2BhCS?)KN_4Z0r8yK0wKCx$4F^@@#gPTkqr{})s zJqLXlEFGq&m+Hnd<t_}`6a5AhA-3Aww-Q<53c`U57&Xua(b89uE`+#81(@Gf9K4Vq zDPf8DK}g{KAO5+)AVNyLsUR+=u?*_@n5Jvj*jU-MXkt^$H8ZSRRYJ?h*iOl#MAN}5 z6!MMXJ5LNj6@_fKtcV7#ej71DQ{fa;df*iYHkMtICU1X~$c2sZF`v7x#oYO)-P&DY zrskYsTDovK-6ANfkZ}xK#$bG9%(5)A+`mXFCM*rJ=@!UW?C5zImKXIF6sbsXy1P&d zImIhxb}q>LIFI`IK_nc=&1@}nE=K}r{_9Y;J*i+*s4xKo0|U`uSg5jOOh(h<-YaQ5 z?&w4XItrme9)hOA$kX1$Iu3*u@bK{S)3I`dNu0VEFlZNm4c|3ibC>%n#8Vde^uF!& zsLckAE>}*mhkl@yV*=L$xr6)ec;Vw%S8VL(?XwhJgIwequt$7mo39=^o;g81@{$mg zR1Em?0l~)hPv_^OgP~yT^BRo=!aDnIK|y2shP@Cqj};Q%@&g*MO+(dwp_24eL4~r@ zQszI1N#3hM9jbWbBOqtB*xgW-H~E`RZSaFQgYHj)8!?=xg!K&}H+}Mk)2FK46SA7W z$Hcg%_kxohU*U$A1iv)_!M8^`^Uw@JAh$b0?J`%6*_?9`=m`G;&eBe@WTx-8Uaz}% z&iPhwHIo1PUfJNlb*xYo{-me=f?`3e8W1N!+|ae64fk>q-6c5tDlI3`K3U#mVbYu^ z=2P3TdzHe}Lis%(Ng12WlHK=)vOf(ZXNh$4m=ca$ZsP|Y0+;C4*&$(kWx+k=Hn*SC zBuaY8uH(?5oLDQ1v-?H?>&^jD(%~8$wN9CMNy|420<)Lvc+k|6<zZqI{J9@7!?`p; z_A!wrG<+MQnm5r898BC&uL6_Sm0G#j1+az_J_gLI^s^!M3#ENTH25qi?zQfc=|sQ# zd?4e+X*`N+(B6%Wg$WY`;Q^TiA=pGsMFq1FE@a{+!}r2$@y!;6tRUD9w^2<&!4Ihq zh|Itp7L)t<RkDNJ7ELR1-Cew$6>X)Rd=dx{^5ErTU_k!-S&!^`K$eGu#z*`ti^!V7 z)aUwTIDGg^048k}dsGmzjLEs{!H9q2If&dZrwi3#9Ue^>MW*q4yZwXP@oDDvs*&x0 z2sgKp%b<tPdZ4aqy(tTqpJ5BwG!_+C^`~^<UvKS|wqtG4^p0kisT_iFFioyI%T>(l zPDhfGB2<po`x8GTPbbrOa5M_Q33&<r^tcS?_Rq6hu!px~k9)aGhA~r8dS{T<;jr*S zUAwoOQ+2PCGBC``E-uUjSkSg;<si_S2D}x$VXNQYCAx|K?`zi$`!LLi{6`Rv5;G;| z!`$OofNS<9n)O+kl(NL+OpLvq{<>_paCtkq-Vc86(1fs64psNP{BDT?fz^N{5_X)@ zRe8*}+15pAbr#&;>&F<B>-8U3I!~>0s|ei*2rS!=t2%UmQwV)IwOtUnc>6~0cPxOE z%R|PQ=LptqIrLG`GVrYn_p56#@y^YCCD9nYZ0d>nb=_1R{XA|ux6uZ-_l%byG|&T4 zYrVBIB7vy9HMw)@ahmx6Vk>f&b)X6`jA@uPCsQ?gcO1*Zw^o-Ho~{+>1I+kz6EaL_ zfgT1avTo3C;+;iI0V(+2;2P|rV>KLH{&xhBkDE`|0{JP`m%to)GGEbU!Rjaf$8P_3 zIZMDE(K4)nEP=<lz&+{D=Zijiwg43(Rf1t<=aWUqlFN6U`&JZR<l}oNKH&d`qz$WE zaZ=YtzI^<ENmk;0)`46nZTvAi<#$m?To~f?VFf$*?}=v@4tyT&HaJHi@ByqJ3~yC6 z&0U8bBnxv7^lDV>s%32n3EUh?FQXle`{KOu`sry&kj8?!P_hL9t`*dgY@JaUH$h|R zsP~sT(Ce^uei={MPldC^6Zo7Ee?T!vPvRU+*E*i_=$KqTO?#vGF5a~I4g-viUHl^? z)IB5@LU)20KGyLoNlSOBs9Tg+_VB&h4(Cr8uX^H;WhXOim5<qUPMazn%@S9`sfC<Y zwcBP**gR*{rh)pJ?0BC1MQUv3oi^PNk`{^o85D$xY<TL@+7(d{T;yUrf<KlEnX@!8 zW|Qc0L#Z>G*hWMDR@$U(QfW67`p&O`B`c;H--&+hQ1r<ur=T!1(a+Q*Y#=G;vpzUX zK)I;3N~mkVg`KQe+DOFjax>VK4)m%jY2zRs9Klf?p0<%=!ES5=?<A9OMehU3c|2yM z#Hc@U><yr}3{#eDmtXP_KjcY~+bxvLZHaW?hPQvk=`n*<$dPiiLHE8b$g`4?m}yCS zK^MwGi%NqlBJuu2T39CymfJ$2Q+4;1%=`PdLmvqR7ZaNuM_ffv5F9*O5Dns{QzF=f zF$u0`LnhdpLEP`22wpEl5-za(S)<wnITa;IXg`!59g+wFqelaBkm#$l03Fe%O(dyy z)z4B}REq(>iDLPggJuD+T8{4#Y7&I1j2ZT)%LI}~I2@~5T~&3y=p{T={cc3OqSlwZ zI-%-j-L~uQ?R(KDGkkokd(_PWpWK9eq%Aq$qvCJF7#AFIY0A+p5EigqhFb*&hraBH z=I0s_seQT|2doXv3ls9(+AfFi2-d~lgZDm<sksJ0N;kU{muKzQMM$?D%tq(Jp`qdb zmP<}0mik`J%)(J^sgWhFsoK7ALG-~XjoQ%O^?tH_25F7WAh1r?L6->hMQ`#xRoYM) zT~W9@v4d5yzZVV4(p-v#B^o+zVWC_AZ?XdthhVrCRTvcX%nEM{9gKu<0iU`1>3x*E z_d!0d_Y^JKdOQ}o|A@HLs2o+EuRg?y9e&$K{x|4;!||je*c6V)nD_13e!LAR_OktO zVg=OH2P?zeSa*LA`bY{<3I^^+<Zr;}2JejzrUZdk)1oCqaCCu8Z-Pzy&m_`C09JhM zUVenVg~<FIWX~hE?l(uA@iL#otmntad@TE*v&T_RO5TliZ0)0TOwh=r$VWsEj!YO} z1F?D?7RW6gfV8o;9rGvb+a%b(CY*K~{`Qj#wG(`KP*7LPA6}IAp~ofyc*r^SN6ueG zel<KAjbe=8%ufG|%Yl^O^C51Dx2cU%|GJ>s;2{z2yl{Gdm-_Fx^zW$&xIu~pArZm# zH3@qD^ZWVty#4Q+bxuNl|MUO9k6e=0fQ~Ns^q(1iy;t}{3fOYu$80&|_fJ5-jKmMu zUv$KX+*#BW@$H}c-X}T|JfBq>vlbFONNm1;4#0+wH$P!Lr?j{~J+~F%jqks<1l~xP zRv;a6^(<NTWjCo8()VA#H&Q(a?2@P%gun?Rfbj4+<-g;P7pGUiW*52-4{#Iw-vaZ` z0q`dW&p`^@^Tc9pnOx!_r6Eh0%gFilxqAR|*}SUz1M&T9*5k73LUD1Tq^?|19iBBK zFFnzYJXYX}Yu$uNVbNBI$lx(9Yl3-k;brnjWi@k%=$1XF)Qr+sR<Wd{0kp8BEXZap z1*w{~9ixf`Twd+u*lx6tX!t#}11#_Wy?G*}QM}rk+Qmh+9=VCoaJY;WtNi?RiSEH; z4b}v|CjH`Kohui)2gt9|=i`c+@O@ND(g<grBc0>T%0&&OrCjTKFt%Y;wnN93mb3A@ zz{2~(%#1yXXSxmuVFWfob`6u0lZ5aO9<7&`cDP2y#zl&SvaH|!cM`PI3wmDx(Qt^_ zr}8Rg9N_dUPE9<o{A?LxO^Q2-i<JvsW4zbsgt9v>qFz|!!DNVb*)Wd_s49ZJEaNfP zQQR|IsX2dT;G->+O^g!-^~8s{nuP$`G*$9rwUZ?0)zeS+CnqPB-8rKQ<mz{4Vf&|6 zJmmym${p9Vnrc@jR#%<wWtY1{hQ&YPN4*YXiHQX%lohEqFy`jZE2=C7d}`EMv)!!c zq>Cb{7ih=2<XUn)RNPr)(K%T6|FxfZ7=go$E~#Q#P}1-tE<^iqwF$on^2@lQI*z7> z##;}WaUQ~*oY$d&qAf#8m+s?Qy{G%tsU|M%*qOtif&vXZykUmNaKg8gyHn);92BYJ zXvC{ZymHmA1fJIufbj=|R-=FGqSks%OHOXXh8+mK@{&qrj~P3AL$zIiq3pU5Xg@=& z=i#NjJM_xfpr$~%<VZEI+IIF7l`8r75&i&{F?M$G0R4(;bK`{%(i7CNlweZqc<Ln& z%CvENdE;CnL$evNTH9={k1$^1$2K++85@f@IX!)Nwm2$4x7R8@e!Un_3oB|~Su}N= z!#Q^TOdct%*bd<!nRF{Fq|*HTx?PGA3CG;=7F7yuLcU{?VL+a!Zsb_avvT0@a%3bT zEe6Zd_Q}lQ_87CE3rE$xutC`vC!tu4k*Y$`e8HuWRKC5%S;OwvWpg6>p{@&=TVvBv z+aj@if&H0R=F-v5xyx+sZv7`zlw4_Q&o>^~)_aNHJq!#yD?CqLSLW_btc<kyAAKHU ze_oA<?@MO{YGn5hqNuZcQpgp(zK(r9kuq1%SJahriF9_kDO@@$`|<XpGaSnl7UKce zX&GBg>}eZA8+_*@8nd#pAb0A;Ai)dmoX=WoETgm4^K=1R)6&Zj#xhyem)+*Uld2{8 z-C?Oz20yIUk`j0e`m0DT5@KGqSQTN6C>Sr*9QlLIo-i&>PJYDkrHO1YI@R(IYb`+4 z(HsDdZ1qJUz7feH{<8}F11Mw?fLAZhAtG_&eg*eQPe;$TH*<4vmXXm+Xla}>a(sIB zNgo@l=4kO*f<@(@MA`nMJv_oQsly*gvvY!;@YW;ppouUhfb0HzBwb@*o>Nt&-%4S5 z<$Q-*IM)SnO+Lklf!1xS`ZZ+<(S7=fJja_EKYyxA^Ze||Oq86$i<@Pui1B2BwQAsi zW5Gh$nU7r&F{o>AZ~Jr4koh(1_^7dwL8Oda3HvOwMk<^pr{<E+3-VXxmtDHWC_F>k zhGg3<>{_c5tu}NE;q-I_*5H`N@Ia8VKcW2M9oYje!pTGz@Gn`~pOmP?Tk)%~l*2q4 znyp#CX|sT9?v4IP#$GW}7zu=re}tOfv~%&;_rd>7xqc@G@r+B05p)~rMDGe}fWvXX zg46KvCPxWa?6I7|=kRzWux2?~p1=bMqoKQ5)E+vT>#`)%8-AluXD2Uj#l*uKX~=Z# z?d@d@%(kja`7YY^&?~q09}LF_3<+4`KG^I}@`L!t^+n3(zY05r<R6#gmCi*;%Fzsm zDM;K)ijx1?juFic?t8gRojrkN#!2hLhO<Mq7Lo{CIWcGE{6h?BgY2Jc?EKV5WzNRY zLRzxR!I2!D3msr17|QsAa&)3USqh;9@=*V7Sp(h-Kcg62BsH#N!*)KuxpwGwvlCn~ zn63WL?D67{1yhGdjnxm`LP_)<ZQE(&GPQWibW-0#Gr0efkHdG4pIi+oOwo&)$jXU` z;D?PA%t0@o2KS4*K&3yO`%MRgxlD$EvTFYJ{5|`X2Pt{e>Q=~BoIpl4_^VaUicaIt z8+6zQ2Q^hY>>{dc7?^->h7c)TzWUTDXi3o@1QQv(d~WxVOJ)1f&Rt_-79<nQqVNKA zU~x2^%4V30U5ohC6S&P0_%3l$_HvS#vaUPptDk>I{goJ4;U%!}tAJTQ-iKgn-p@s- zO%aNYnt*y@ZMIrz1oA7VbvL0F>h@yE{<R#wpX-E3t@ehEJi3t|5=KH}V5FIG&T!*g zBz04V_MF|o&tLSbR5cJ_LkUu)P-6cm#u<ET<m8ku&#wHaagnQd(_Kvwmx$PaR2ouF z(u1DdvdpMDzK6J&wb3JtVR+3`PX~nJ4DDu0?-pX<M`<90-Uw&17v*tvu9>%^HxKGv zG#pn9P|+(fWKeHpIIro!1TGmH=;?J<A3TdwYLh7#8Q<J{BwuWan6adOaLkj0A#hd9 zm+z{Ob>4LtTy}NTwn8^&9;h<PAP_-fS;qroW1ars8qJ}7w}YNBC3nBCzri}cF+<QD zC}(6CHZAujpTb9@H!9XUNDKpw!~#)gE*uM0Df-OhtlV_TMLvm}Ml&q|u|Qedjg^tr zWJJC00am<;@@g+gG=i>)os;6~8wtwakbzR#maJx6Vj>D+8I&JQ2ikIwkXgeUsoqC~ ziUJ;tX+RPHdAJBr`zn$Ow`ZmB-*X&Ha9tiGE1M)q#3VoJm66HFAWzf`!SiZ`8J2Fr z`ZvFal4>AV{^UgDlLl+k(5XlcR_X;rBkE_p@dPGIbc&yu_JpD3STtwl(o#{EkZ$d0 z8|Wa3^P)?qPZ>)(Ho>mApg`GY#_Yf6pO`tDuP*f_gi{6}vuw1}UTZsD^7w2@M;Q6& zYG;l)7Yu|iI9u=pOh#*ycl2nxCFlp}5jj3SfL6VAyg}@g@`cr62ok6$;WINj-Bx}R z`W25Wv@1zW4B4VJ#|x0;UE4`jscC8Th6pXKrc12jpFE_H9icdtfGp0;xe+es_%hXW zZe#pnEL#b}-x0BA7d80g;B_!)q5bvjJ{kz5NRpp54gXI13kx4@3~kFI=pq;LIDizn zbuYJTKSFaGp@P48xA(~;D>fLh%)1{pKgc>G%k^5)GoLNM>n+^hC`XIxiv(0OGP>U4 zE|Zumpccz;$s`14PXljI`$$0J=}}EhEfQeE_s6pm6EW{mi7yh`o(%yCE|}U+H5&j( z=2|5RFVqP61q@3eLlvR_HJPLMbU?%r39^5+K4Ce&z9aG{w0Njs^p7~GZh+lFn_|iq z=(J%Jq49ADaA8^x)p3q~Mfv;(H$riEZ+icv{278T_LD0T=9U35LdxIJZE~t^ih$oN zoH$?QnEsFuQTg%=3Uh-zL%vmrQu&8v&K^!Ktk8cKF~Am&wY9Tt)=<QxbAI|LB^va- zn@&2q3#A5t9e>O1M`sq#Ar%o+Q{{VY?wcq`!D|!y2oVx@^FP`<IhgcDMqu$kDF=jx ze=i$>l411`98D1Jl$4E0kEHGRj*LSDilg%X;+PUX??Jq7>sRVMK%XAOXHq97zEe2- z9X-slwCpz-IrS7%`}Oe5Oyzd$bx&9pDe)oV?eFOBUVtoyP0Xp1L>xh41C6HE(>05S z%$>>_8h5p0Ta2Gw<j|;o=+aSrAYRUIsS%0)mE3A0C%y%5ES{2*qOPufb$QAE#*7Wm zt4j$}pj9?U(rxl>Fro_jJNsHd(f*PnnZFH5o$I(!-G^LUm8Z_+^-z8I`=u--O<p&e zVTtoSvU(Uy<`m3*$Hp0dt&R{+nHmJx-~>8)SwoTwY}S+uw-zECA4!4ryC)!_H$X6J z#cHk6Q1a)`*S~g?4@co2(qs9Z&vhFno#3U<W-pzFTN8Z^)g((+@$Ig#>RE>e2N!7y z%m&&Q_74&L+jT%hLt8g*{38lAUEpAtZb`$+b#rtv!G5fXYd5}gNX7hdjBdZ$VDUJ` z-nR+-E+p$t?o!Hm{1o-mQO|}=h}fCGB=4Jtnv&X)*UQ`s;SuPfx_qPVgisZ(^yyI7 zVZ2--*|3?iNef|@KpoNBV_Z}l3A`Lq5or;$OFZm!#T*mQ1hALo#2hxeJAK%4<i3Ae zoXa=7tmrnUM6o|^-INrW&z17HoUfagwXo8YcVid<<Pww7F#qNZkgf>0XcChF<bT7_ zqDHO4aRew*e@sf=qWYHA4+QJ(jidw-rvPaVE$+@nM!SIL9O5^&!F;u;f}P!E%iU^N z(L87mh+*gvoT<0nil<R$wNZSrhBx$2pI2NAbc|Mq^;?BkxfZ+*;<GHe@AKTtmayiY znl*BIwlKQYp;}O0wN*2JooQa~FlZfh<yJQD8I-A@p+9!0JZ&tpx-vR`3cy@{K;#K0 z+@N19)T#^Y+1XifZu?3K`6QA+^9I2xaKN^P&7~`tUo<aPXm*n9y_QgQEvEX`LuK<2 zDua8X)*ULNSxz5onU8{yw{&O&3-R&gQ~f5IZx;6d3l9cXG2D(Nn$xAQ%{Nc2Q~%F5 zgR3TEvgcHs#j<<+$P->JSeUr@6j1a;9lAvW4~9ujUYZH3ySk##PhP=@w(`iSAS63D zuzd3y*4<wVJ0cfMma6^N48|TSJRtjsh+tV5V?ytCQZN=0IF`XpIJ-^w2=Lyo_V&yd zb_@=}E?D$E2lnS67w+`jTr$xYAxiN8wUXM}TJ-pBqbe-pOla(ga{qkqnf6o;dmZ&i zNW*>qEHc?=AQsmHpqg6&L;mCA<G3rC4-)_ezA4D?+yJ83l<&DfX36~Dw(Chr6X&DZ zlKzjSppTBZI)q57HD+k_o782^zMitWr_*JuoX^q_=lfXh8L+!lzSUifu(TYi-5pq< zC(wB$1tPG8tvY%p31m{WH)UyfG<z+d6x89%Md5aH5l?!apT~~EvFIyw>@cqF*%tnh zJ%m~-g9}O*|C{<eIdtgjVJ`)*7Z~;Sa1{(YUg@4U2Qbldy~RzZ(n)M(&r@hTJ|BE< zJIKtZr^co|T9KEKnYzUKy+db;e(jBpG+vc9(y=}q-|lL1iBQcX8hg#UJ~FdX7*z&5 zkiQTHKEVGu{qGjP>k}9|IQ{Hv$GR3cEx3Fc=R|Xpnsa?e`J+Kq*VdXw;mgu$@+9k! zJM<AhEQ6;-6+44pwML@1lj=(OVKLJ@!-R*v3_Mx!k)&Fd#fJd8Z#8eu?mHo5eoM~0 zprnC~g0o6KZps^BNgI+sM%i0lwee>q6Fy$Vc_SxXiZ^j3J4;lH$kT2Q6Qb;OsIK*m zXQ<sM?1(~XOSP6bW+vw5PXOEciPhzLU(O_uHRS}zY^DQ(dD3~@9XMA)(WuuQmu19L z`MiD#2nguu5eE}r-W~P+5eciTtOQ6{9pA_za_LlxuU{1@KFeYc4GndCkaN1+9vB{F z(+Q7=h?s{*v)tL;4Xo`tP$9>=75Z?gqb+G#(&Jg)T2wgIxT*U8u=UnaQFZ^@FwM|I zcc*}KcS#FKOP7E&(%lS=NJ@97bTgz#3rKf22-5K!?{EFywch_&EY_LeoW1wwN?L7o zaK@8IhElVZtBFr-z;O+R(6j4;@!I=H-qdwj<ENAF!F0ZT2fzN5=&SV^duW+PA7Dm5 z#2cq>6|SC&En0g%NRU%ZL}BVP*_d=cqo0^QB=jvxBft0cA2~Gyoik`w_|UXv36ZPP z$acWo`mJMpD*f?8mX+5|%&xlXZKfDJ1r10r?sZw(I4!FP4WJ7%)btnA%`a+28K;^m zkcHxd^7D)G9IS@~zW<5@W8yIVko_ODk}3(qEkjiN>=JO&?I;owx%-n-;lxrWamwGv zx%X*H0~km7qR~j;L=sL(b3~XWVf8?eOINq;*I^f5Iro}BLRY0C6{j{I1)Sss62&>F z5kGin9U64ObWCp8Ki?*yd**Q)N@AdLb~72lzJW_(-@ogL(&b{lh<rs%LxG&}YZG0w zUyh*&Dok!V96s{gkP7R~#HORA6_gE)ZUH#aPz-d8(Z8&_7vO#}eT=LCV<mV3VEP6R z=>6GJ`wT~C_!aVlswY*z9u*L3!HN@!gM%Y+NB1SSCQ6yXMJOu<%1!b?G4FM#0Mea7 zxBt`M-02EF$n&^ZOlD#o7d1u7T`LLe(2V<npl9HF*8V#mUqzD4AmOTN!`ZeDUBPT9 zq8AB6lNX4L^(igOs8&oSBjCJPGgi5!Sr*I3uALDO9l%|e4Gzwf-mTURd**L!kTMH2 zp!jN0@z65C^F;)AG8sJW48Mv%f%t`BZ)2Hmt8_ob-@i5<#KZ5K$QeLc9Hte58K;#Y zlaZ1<`(<4F>d$=TsDv*YVa9|Uy5M?9>YNS6Z3ooJ6&rg#{)ZLTqGagH(eG!N4RNGH z>VM8k|H8-M;Y^HcSK8|Zux;R5j4fmEn$lyD82R$TVu{WC6ikJ}4Wns7>gih^c}cQX z(sE7Tcg5d=MR1n`N|7IajDL@kRT<?)6loeeflQHfU_7Y2zL`5TeC~NbKH@A8T`-dY zbG4B7{=zFV!<W(>qUL&^E<`kpZWVmV`zTk#A_s$%U!npeM7C8|S65V^*-*owg_H;3 zZo0pblMA8gjmE<CchZ6jXvh1>)b4xtyGSXOxo+U7*$4ogoVfmm8syzZ4w&&ou|^n& zX?Gb?&%Z)fOMndpXNTg=9qKz7JtOdWt$Ma$s3NC)?18CEP1XFaW%4Sl?+Z$cZg9{F zFI;ayzI^EuVYkGE2}6`;(CDFZldQLjEi_muUkZ=>RcPBj211$y*F1a-E}tsJyva_w z7N)&V@6~~cH(O_QMJ2kA;>W>QTXAn0GJK8O@8(pYAS?=>X_<b<q1+1$tXp5-R&?P) zYA!OahZTovwf=QjFR)DAjT{TyU8@;q#z<e&&lZw%Pij6Rbp14n1Sz6o{_wUp|9R5T zRS|U4Fjw{P)=XU}KXOo6G^qFLf2Mpm9<4MU9f57^EB=Q5gjVj=Nr&O`Dj-ns-m*U} z^22~kZ#zoKx^#pSuOcTFEY8DjNKLLV_%{B|OZ;p0#a7vYkIhw@yHJN%Xc5$aATGL< zgqRlPnm#k)L#)t3XU<xKTp28O-~;93&HB_$FB*H`I8+^v!Y{@Nkf7fFDJm|O>M}1L z$V<Pk6FBb#f`D*~^FZ(+A|~Fvyu4IjmHp>)yZ-#r2O1)roSiwxdyMc>^5p{~ii?Uk zW%Ft!Kj3f}g++lFWg}wu732ji%JU~+!GYfh+wR@GhW6<O5;dlw@Dm#8tFy$Gen=A} zG|hecBzOAC)m>QF<LmGGw~nve`H8axLYJuBNkSx<e|qlM{w_#}O51B*IlMOb?C$Jl zZ9IQbaT`h5_Yq7gTr!$b^J4^JYBesZR23WhD@8jkT<U2$hB%soTkY=L{h%GJG-z9A zlEYyq8&;Lq4i9T94UaBkXlU5<OFNQ*K*ungZ9YAi<;RyVxjZzig5UG18sDU+@ic6a zTO2(dJX1x#<NFT){ufm5iiOD;32$NCcJ6tu9XsyVoXtF*4>+y}Gqp0!d~DkEgoTVL zD_jg8{jmyuyQu$QdrFcez^EUB%308Fhf_a+2q-YtfP?q0x^5I3c}6G|0U0K#qIv0* z#+a;{STbD;FJh%^bEYi>4Yx3|`?y-_GrB?WS0<V7YFB7i7%}f?@dG)v4=eXoOPLkX zV|sw}IUv`K>IXF`NFV4Df|^KxzP>!Iy@b`Q1F3G*laeB#NkcPz*51s?sG1n#{<+Zt z-9W^g-@|o~1dZM+sYsO^G4g?zOYs-(P7lH&EU{ad(})O3R8z3dS=WPu8uV|PSLq=T zDn>lN^*6un@zz<^U*`BLy;8~}5PA|yg+eLCl&IW(CCCBi`K?Q&M0C{}=BeFJbF)-t z=l)9yca^2U&L*cRh3GD<G`bOMma;<p;=8GfjJHLfsN=mR)9wL2Px_Cc?s99rfd8LJ zOHDWwY{?8)K2=AkNp%L==6ZzgPn%vB!r6z<n9I%7tx@NBdt!H`A0`~^=#zy4B=5?T z8I4}*N37+$ceGgr>BiH%4#)Vtbwv-*2+dP#uJj$*jh_{se%0`74Vhcjw2c|-X6O%! zJg`Q5IDk;*zr1qUgCgM<DT;e`pR7x&y>)XC7}S2^n`t=dZ{{R^Z)Lp{nc&v_vEeV_ z@f+Ndy`%zT*#Ej;`3s5xtS}D)7j>m*9olPUSx(9UIvj45?VuPK6gV=BwlCwmDX3mR zVO_RBq+;5^E8pifGNVym;^;=T_q%*Fq$Gz~LrALYV7<+fp^RsHcgzUN^uAdW7E#gV z)rg3^F4Gd3`C8~=Y#Bt1JsL(+K$(ch4byWIe1-LC4Ty1LJm&|CM+B|=pOad^4O78$ z6@bhYQD0bjHr(42g0OKUFLN=><WTox(X-%pq$vcD^ZgCpc&Zm}D1b1)1I{?aYGfE2 zVcfSet{Oar+7(liA0QIM!RJW)CnD?(Xr5%B`7INPbR#!Sg9Q26Hcg?pB;LB~UbZ(# z7T?F>K+i{h8`NREuk7AlJT8ieK^S4soC+`GA3}VRvy7W0U77k5)Hf9@>YaE+9ARVR zpE#6KhD{!c&%Nu4xJM=HzH(3Ne-B}0)1eaXhNYE?r-=t4O|TN6%bBwNIQrunCTteJ z1{61>sh5H`WSt|%q9DFAAq(!oc)ir!Z)g51DqA0vq~iI||5?iU(DZXSKlk@ZpcuW7 z6K<JR#eJ0;*^9#-{RABGm44GGbP^@`!}ygwnw1j>>y2+O3bSijbMhtk)TBZSSNLIJ zzX^DslpRkQJrsi?WZjNq1sxw5zCT(JedjDA?~DNNJ5sDO08W5}e&CtPo0sy@O(JyQ ze&x2tGYa#op&4UJc~beg0Rvh`*}P_J5B*)r{_9j~LY@hh-JnDI?khqz6Ke~{N6(Y+ zYqS9=<x;BZ@RU#Y_*fOv975qUc{=N%9Iz)#o!}vJmEKJr*W^gDKC*BkS{KmLyAEl= zK5^i2yj?4xY=VSnfa=moKSyl{JomYW+c5r{GHU{v`0rldW@h*FwBukcGs&b$Iw*@f ziOy{>AHRuJQLV)LJ>_O?FT)S(hxLFFzP}tX7>uL}3g!IEo?VO)TN)lg(<yTT#0GCT zj)dJ(|MGJ|39&FS$C;<OoU8JlP@Afnc>$bo5l!=c9QZnD9$d~Mz@)y0d!k>r)fb+1 zr=^CmjV1jkz(erQS}6;HyE+n;a^S&8{O4K~(t*BCHJJR&|K0ok4~JDN7YN#@?GEFC z`M>MzzhUQUOjw#w9SCXi{|~uoGEXZJ>NLM5Me;8R**`~~|6W)J0)v>`DzsdR_n-3+ z$4~4Yqm6kc-@*ckt$)5dDiJWq9>Ke4g<8t9DB_*}|K5N=JPfwfJ@Y?T12g<(SQAMl zSN>rh{_X;@sBrU>!9!cINmD-s{vjn=B(bR|dr7YbpU?XaPg_f4PqQzFR(tT%uy~2d zDFT}NjvP4!*~{ZF|AVLh?=FVO1uDx1c*ZzR5_CNG0IPumyGleH-f`(M-i08p#;mOh zA1@J^2&RvF({3>yKKB*-cvBfIqQ=hxr_%aXaj~Db1sB7^iq5$udt;rv;u#4aT)p|T z>>pNMhw(FMhLCITl_gd^+(gfmAGe>@dAQFwKIbz|zE)$dKkd+<(#a(_NuZ@j<g~vS z11c8VJKeWrQ=vVZpXSLN4j=ELMn}V|jEPxTc((=_fz4t*{M8y2*n0W7@p)PBgRJ)b z7{6RC%^{y7-`q^;CzqcDE0y9t#lT9s%o@qSuqSs@H-9{y*pmF<bjIb_EnTX~HJf2$ zOWN#+*0Q2dZ)GbRf6;1yq2JZe*Z?fuWyHenU_+vNR)^+?^Nqf&tSq4Mg5Q<8V5-Ic z$vb+03sCG|Yz<-gy_6#V@89jT1NtoGxs&!T(g@+!plO6~-e?Lo*3n8!E?-LXr&;-a z?W(BIM{Vtxz!BM6SPPM9OCz^$cQT0yK~)_qdKJwO%vL)B7MkF(C<(LBEt|^f>ZeB+ zuwWfS)6yA#Qt}(u8#?6-Y;GGoIVJ=Fw$85WON_C0kFdba^SgI%!kiNs$}WHZ`LNew zR##ovud7w`m80uvAKS&ykcpALixDnXxXB}%Ss`64v2Sl9dZojsxxg@k$2LWx4Arx< zt=?+hz+p3n_^`JZM1a;`^IY*7MWaNPflkIiZQSS4zr={rP_uG$wETTH1y7gP99u~> zw|g&w1qB;FTIe~TC)ICu`19}-h_}tb#^wM_seqJi|M2iWn@Yp^fA=#H4uU*28P2?8 zsI24;Mds(<i{pxuy;G4t@RAE>kEI#}!_smHS<Gek&ylE}#=gg3UXi4EKdnq+oe?K` z)^;7mZH7`27V5CW`IpptKUw7lf7`a06zNjlbrC3382(l$^G+GP!&L&Cw8=qhcUwqU zSUQc`YIL7dz;3aXb}L1nW^0SI*$*Q)f#XBD{@SdTyGlX=_7COA3fUfWzB#`xPPdD( zFKI+xdg*gEVMwG1BAf|;uLda6YL-j+Li1J;M@|meap)T0U#(>E@two+)e2bI`2A&c zTEza71+Xa-0r^!dEB(f+dCu0>Au5N5-g1=4R!M2(*{^TYa(L%AkUCjXRTUda`^^Lj zHyB_ylJeTk&F$SgNfsFI|3Vv~J?Bz^c)c5L-Mtmf>;7*pqH2CLaDNNFT)!Cpu_-f< z)2#EjMxHFxj{FlezupkDPlj=8`uVX@-_Ubnur2~^r&fm?y#^aY%Mp$uzNiZ`W<1Y6 zBren;V#WqiUGKdW{-wM7pmAX_o(%K0BbS(Z;MI&)YF@>W!N=oyXR25ZIXw9gJF>wj zrh$ltyv(E)Ap}4j;9nEZm@u!W@9Qqy($?A7_~C5Th0<U5VSpi!_46<}IXOOyxaiFW zvNasSgxb4ze-^B)R=r5SO2-CcXxHd_T&dPDb92)>JDUUogiLq}pm(@R8_l!Z$n04} zph??EJfQ1CaudoL+4+$>3vudlnc@G>t+W2~@3ZyMZvV#)IPi4N&ecLjiL{N(nJXc1 zq}9=YO9Z7ksj=H7*TLCnxl3>5%R3aUtR~rlzT@A&+yoVuSX(q#J`(<JF;5(S&^D~j zBE9xr3W7gZ4%tr<3J6-;s^jCoBfAd$D7n_%|8aVYLFIRQCcWcR=hN*)G5HdY`4E(5 zrr4faTc_-si&XCXQp39cW*QN=7sSWv79#0a%S*H`XT3=Q+)0u&Fi}R`J4#Or^?`=z zN&AgUtbZ^J>X^!FL8zvx9x#KQWI?$OmkdK`bzeP}Vf7*|<zB9a3I}B?{Ur`!iFw=1 z{xGi`d<iX^mWu67*CE0WvpLawohc#+*R^EvrKA{fxmqoIBl+CBvqGmP8|6*XHgv;n zueA9;%TNy@LIl`IBKW!c6N0<8TNL6lqa1y;z^W!ujnl)U6b0kk*LPDbg=-FCLuPWL zBe8A|?I#r7Lu*@qZjt~-kMb{)l0eD0rdK`SrAE*%9h4%#Ri*^UD>w_}ka-D%sGJXH zKm?30P>94ElLC+p<LBX9^)fZ`*^@tXW9b-Od;8XqA(CUIcAqgkFC0z>66->Fazum$ z5ej1(=Yx=HgIZSn@);d}rLul61e3T7|I629Xav22U`H>-C{}vG52qqdXCO?uQf)LX z=8sZR(K4I+$3V{b<9GSM_<8Xa3as&Z&*faCv}{Z_PHT<BKXvo0rD5y-o*rbvs8S$F zfsea(4IbB7&G^J$`w+eBbVqkS(c_>9p{w=+MTL@#9Y8*8xYA2m&VRlz80twZU~_+h z$?JpC<Tkf@Xu3bTS`u_1YneUox-2de9o$d9{=`X{%1}}v`ACGJ_I*PJ)sBp>VtD&; zq0+8DBQgOMYvzDih}BH&My4L%I+Fp)M$h2lx1(=0(o+0JCKev1qa^~Ma20!sZ;pGp z<Hd}JSkp&nZ1R1Dvh`d6IKrAO$fml&t)U6(So<_zmm=!p02Ga>5ik^=3tBKMWC|WF z4^bW}P4sp(cy8ox#EhxkkT)g3!LBNc|4tRv>^jtt_9BiRd*!)3)qlV}-f^jV?!*Uy zzt_2kKG?A#8@x+Nqf=F#0I!C(o3PfL>#`20KF_PYM`4_J{DAc<;390v4D<KhX&@I1 zZgZLQgZm77(nz?H{QT3yO~BnwY6QzA(7NV4{DmH<H@w|1h^N8=psMcIY;3aoQ+ZwX zGtaT%5~?y-6?0qbvZO?@kbh2FPEvt87f2xkQ?~syJ(l%vEi0CeQsw(oKM6|1ps+!T zk`Q@=CPxl1Tm?lGP&pqXS=n-CPn6Umz>*DP$1!+L(YQ%e{>qjHZVzx=*s6{<Ce0|j zbeSjvBGqGCF5YkpBnPeTX!BGNA4;FG)12I0(?}2VxL(zNTV^OID<-haDL=N`e}_)u zkKn~a1aALn&m*uXJcEcRa5i&5Whz-~_M6Hw@@<7yVX<T$Nf6PqlXTO}+|tq#^%1n6 zkf0R=f8-{#jsemm>hCB@rp2LIW>NJuY8oJ(Pyg{nLjrBXG{Wcb@Q`a(voBlcYgY6* zw?qA~L9~3WC{(>OcnNFn&Q^=NcetUciRKtva{_SG)Gq*xizzQ^8HbRN5DUwZ9<R(F z1AT;LhnnF-JP=~)eL}aLi&%~-ed@Vah@Ol?bIS!GY;+*d(ff*Grs>c8FhT!Wu!l{p z@cfvy{)Yz*4Q_zKO+BG-|2D8jh|n$@ZSArC7!eS=Tm8L|0((kKctwcUFcxt#h^4!( zX7fQqU*DGNE1=|>vEkjtf{!P>;|vJI9_?AUH*ymo1CmL)pRSj-wzeL!DFtG?V^vCU zuhDu}bC;jE`i6;X<ma8wLtN0WP^AHVQJVB)gehx`2G969GDf$ZK<KOf4@2`cRp;V7 zf}OhN@1m+b+rmCBS<zQbjETGm2b6Z9CH5>u;~;Tc^rt|MCPmG^ASK@2?@2x)=1fPb zofzbSf9V=kgwcY-pV@p^EeRmeHE7eEL=UbHei7t>#+6-$Zz6(D19KMW(Dyh^FWU_` z{siOeuGvX{aTi$8hUilf?W^|W`Y%HYZ)U91XsLExzTE9CWEu4Lk&=(cC2X$L?B!KZ z^F^Sa3-&W=phorzcy+7Oo~x0OM)Pw!ImaNK`BSZzFt;Sa^3dZe6HL_0cw$|TF(@ev zw4V_x8sj2RJ#CC>BId_Y<>%RS#|nB<ttul08N~7bY|-#@Oi{~=A$Rb(p}*>qg4Z$B z4F_fAAyC7t{q8=gLTXM#Z5hRLQfb=VjGXoeY^chIC!pt`Fs=52UqL>+niv}$6x#CG z?z+XxBm|^CO4^ZQY%jF98r=Eg6H9F4f5sA4PL}j5j6V~O>=}elg#Iv@Tb2#+lltT& zK9BtX*SG{Y4V&zjBxWUgvi<KrK2o5^ApME~2YUi8ZFs9AAAAe+Ok5=uKUK_3J|c90 z{i6{3z_kFVyI12CY#J8<{0xk)Rxl+3xE0Uc2T#i&nB;J4K7d>7TYWuH#``xXzp#)l z2DCiu&|J_2uvIhHK-zh~(QSc<6J_|6*1U`J{4w$%qw&X_3u{_BnNqwIlSS6?$&DVA zN-!@ma)#G>dG(!^lC(6y&%t#Ca&gmI9dz!nq%18i*4ll(>EWvU-b^kufBr}NaZ1(L zmzze|B%aA4a0zo0%_M$b&8h){#qa-wpPkSU7Hv8kwzK6z>9_9DR!flS@d8h+U#s@a zp$yDLeHl`Q{~gZ4>ESx>_51tyi%edqtwA|CAG4Cl&7JR?GN<*HpM|3~ag)uISvV#8 zyBgK0&r*V0rmJ=V4n&nN!+|A=T?O=|+EA;lUhndGv07MeK<DVVy+SxKDSs&n2i#WK zu>$9Z<wN*-JRTWE#`5!Uk$GZCv7FZ<^s}C)sm8Kjqqlr?^lx)=qX4ZEEvrlIgUb4P zGg*4(rW6lftzWAaPOQXPMEI{HhDb2t!tR{+of4<=>b+`3zlcwYit}GfbW${NG!rs{ zTJ0zxewD+wZf_X1`5f6|hE`h*-6z7itH69`OmWD`119)1&k0CZg74^C&F;buyaWP* z5l)wy<t;6iiL2>3C)B#XfC=Q~+z(-@b_Xn1*lPaB@ssUG~ObGcm#Zfuoj@Wb#K1 zGh1nM1I}&$#_HnWa0<*dc~gKS0=>ib`SC6{9enc{LkT6<#^>hr%{U;4oA}C&OhV2q zBMOE|Wiv#5e+JOTlkh-WDe^r;aS}HZGS&-JUGUj2hEiOIVUuBrfLSkq+bw+R;Vlq` ze!dO`tc^fn`4y0e;R*EXCM)iwahjVv^u&Pf>G~)B76tHSU_>ddl{>_^s>OEKO0vm@ zGGdb|Hh*22=E@&rV97EPr=S!W@MNyDBDxgWYzMxhTrira-?oSKF-S0$#02hoNAUIS zDj?kQ4U55K7YVE-+D3C_Cr)l@?C4aqIyybwaAx~C-HSZ>6KhS?*wy7R=~cu9(x(Ve zfJVU0+9Iu-kP4Yx#|?1ubG#qu_Bpw(KkEv~3ayjY-97P;iKp<}_h1LgJfLwJM4)Ou zhc)f1Z4D6xi{Z7kKqdLhadMQz5~};YvhL0@_Sds9<fLL-otmmh?IP}Fzg@Hc?v4K* zx`Vnvy31mr7!|)3BwnakD}`3+HeEDkHe;<8P3qiBjb3W%?M<{x^p#8W-qvviMGUFL zN6i?hfy@ixz6L@bd4-nJ6UX?AN}<zYf{PIhnEXVm;xnHM@mSbfh?JGJs3)L8qAdBy z)u{8+);=GGk{U|Yt3Cu|jqhFuxE4<BUYpvqKkCx97$<2`;eV1O{2;Y@i2Ifxm2z{t z?Mn(~MZ1dQ%elOCQmAo3b7xaM9F0}<6l@g^bfS_iXLtk9e~L_*$>=|yX2-|#6;{x5 zCOpQ+^O(Rry(x1yRN!vX3cgb|W^zd50g)0d0!|h*J;X0a-w@2Mf%@W!G*0+s^oB`m z9x0m;c{u+Fgf!|F0zURIROBV4bIyJLQdk~z-`~LO4c;{IZu7ZuxTOoJ4btC4d`2?F z)&dl?&VZaAU`HiHO}>=k#s;53Z*lvvihf^S5}RvA_?v{FkAI)Wasq|KSc1ABkF`GW zk&%*cU#MtWf^>t+@=2Kn(paTQl@%0%LL*eT7>b{mJD`|jY&j7Vu=vaGW>KbXee2c) zb>}SI2S!3|RU1wC52mXaLLq6ND^CX#mmvyB*wH2%8S!TUC1ax0Z^DMima)(;tNeJY z7_8oPkYNoZAi)Pu&~}+vcQRDMAgpoE&de_^+3;0*kjDN@!qW|HI$k(A{AtQ=J)c0s zKqhXzOS?5}5kZYShXshUyO<Lq6?VgsELrED(5PWdQRy_9;V5c43*hr$iy|c-bQSE# z6vJjev(J8gRvj<;J>~VEIq5Gdgd_{Y3}YFaLt*>a>_(sKg7sE@U<TZPe#QRg6)fdZ zLtEZ!<%yW^PZ0_hRpTj@U;2N@;C|5}S@24ns<bW>`VwD(e1x9(a|YOhPAPNLR~;JL zo}92{G7kY5T=Fn2o0T}Z!A=UbIQylsRnyVqto%4soe|#|ce|IniXdJb2KkR*Yr2<U zy1-Yk5WSQsP|H_}>#|1DNv>TsrJgr+3biA0`4#!998g>y5Obc}O61xV+Pb-?vV&i( zkBvuWXA(4W@SiGIv?|*|TQ+w#0Vj+Fe)VrP)!H+*@;419LDVG0I?%?`?w4nMht>}# zi;aKdMtc_APZpEY(m;_|O$8=uChynv)b2hr^78&UUZ_`tbN+aLb#RU@;(f&=Prgqa z0o1*a+hp*3{0Z!zx#QH_I3|D*z?3TrHIF8LCh3(1xEIw9mxNZHoXNn=jdw9Dw8d#d z3j8h{%;gz;kcY)|n|02|!;?$FV_K&RFg94bSL;5d#2<NnWWv4)ZD+kw0EI^+BigEG zcGnq4AOvZl!$Jmh-8S!H<M7x<<2#F(D1v<<vyC;f;{3t{#>WeSoKGFQ=NyNX4*h7M z%yEp~cHa&qswl2!KT!u9%+po29+yq60PvA|xwpX=oC;>sV2kDnt5T*j0WTUNq>gq{ zu3XkvhzP1Uq|rGHdFqjV;Taw$DfA4zDCfb*RA{g-9oo91*F9dy@X(T%UjKKXm$=Ez z(O|e3B_C<%!$K#;+`IwvI*R{L@5>v8f{lRqQpkTMq@HsLm?&5VS=F~Ap|d-4_~gD6 z!s{^{LY*fBgqyJ|+GIJo;2RlN&L%7HwCi<t?&&1%6nRkoo^cFLkZSW9J0TMJ4D}P# z8duiZfKMN&e6d8S=amP0hJ;m%tfJSy+sEh7gP#t$Lf#C@wT>ww<)(k*V~v_z0HOMX z1VPp@jZep>hjo6#0^Rkr6XXAWq=wK64~vi&Y0^8J%h!?F1w%KP`#itma+5~)u11us zaNrWYMe+q6R)6xr8>n?R?L(=<L`PLdk8?L4ibG89&E^?xAVOKzEDb})SvOn{1$f>` zd??Y@=B88R)SASE1a<-jN<<J-7zt4^MA3=D?rqInA3pevNgDHjgR&I0yt#4AhQ#e4 zsUy`SC&*^)Qkg{5_o%AbL>pBoYBM)UAbvIfMc_DtQWKYp!CSy)hRGEvlfa_UF`SgW zvzuUdfgnwNB-EfXd;MVGhO7gmO3PYw5wi}ChewJ_d<u`pb-LMcSx^Q`&cF!UEW3n$ z>2(Q}gP%aH&euH8JJ9jm$J~I8I6nqofca$8(;`Geu}iToHo%(2AD8BEVl8JhRBf@y zdyV>ET@tP<#n7|i!H!Ae!StWIWQ>8fAe%ob?JU9buH{Q7k~}09gA<7E`BKUxIkYXE zwR4t)wrr?Z9Ijhk$|HnN<wgwbhW|$aEM+2rz=4gh)r*Ha{zjNZm{$__ZMJ$ZBpjv{ zi*Aw-^hII@mJ`;}I?dXw3A9sN^NyGqs$2=kD?B8v3FSyI)K)pBuI}NPaBL{qRBLrB zjvIP^W4M>s!hMiRid9hyrXlDb*=Vt7@cF}sRk@xEOLI(o7{pI&Aqv)P42_@U>Lia= zS3oHJT1K|g&IFALJR!@Zn!=rrwv<&u-L$}g<u`hjnT&6kMl}>VA1(RkK(`dauM8K& zzvlm7FK<t!d0rF3PAN~N%m&B-qB^ZidQf_io-iu0;7qi%wBEhLW}>vl|4oa^StvDG zwNP&rPbp5JolX^`yiIg2$`GW=5cETG1B`@35k5(zp`l^wjLJR!zTOhLiI;?9z$U{B z%TDsQd&x8{ir^gq@7a7EJR|Wo6n}#g)GenO7RXtO;gz0|@y_ehC$6^8L;?6NR1Ga( zY)&v!EE6WdVm#X_Q8SLQ9b_ne57AM6&1omhG7cG+kvL>>{c`C6RV+jl+cP5f!Sn!u zZ+;y@4eq%h%Z3^4xX4UtpwjC+vo4_K*G83AH+Gb~T_`T6^RYR%crXb$`5iV=j0C>< zVmTVhk}Jfonq>^;wq#Y7E|A0b$QIb}XcZ-3*$gv#3)g1ss4Nhc*U@XBkJg1O#iTl6 zE$RuA&{x-it)**ST{E2}77GqsF{&wRLh7Z>j|ZsgqKk|9_o%{BOG|qAuga(=dj+9h zK;u<kYrsr1B?;`~W9x+uk*&z15)$%I6nVxN4vNEh;{+v%O?l2g5COF$jD|xqW9h1a z+fiCTbg1t*RZJI0iHt}jSuCEg1eL|e0v~s<mbFFxTdSq(9*OBK2=!O!M%a6jnAc+} zl%*yde7w<r?iTnanLTmx6VvLO(z49IBrR}$vrL4?gqXO-t0-zj+?{Ci)9jX%PB^~6 zK*39dP+n)B55ko~seh2_q3BojUs5!xEJwB9?Ip<4#^_-267B9<GZf`%bRfaQ&F-rW zCtZ~Sf}5HVVZGPmBC(^VC>(x)_RN-jAnp*%kYBqt7GNA(*Xt84a^|}A)2T?4kg0^= z<e?1AUt3X>f*G;nqiO4<H6+^{>>zh!n`Xe0%P`4=!0*IyE-i2ioyNM(+!tR)GqprX zREn_6jXtNA5h0r7CW;Q`?_~skw2-hMWb!wO{P^*sP!{Ggj#g0X220e6?Ih}`aI@nY z=2wS{&4KUX=~HZ;B=c0dWIt#~b?HNhMisFckz6s!L*BlMSOcVHRMf!_)Y1wH3j55S zRBb`Q-(h<1fDSULlu#C0uE5h?dK?S<Ox(VpdNUOjOuZK|RgCFQr-vC~5y(;d^_<&D z)vq_Gtpzwq`ac?q><N5xrVD86OyKnQr)tkF>gxiOy_4wpwcX1X8am5y5!kDB+^l2~ zF36tSzJIMqaHz5#f)5+o{*{jq&kj@m9?Sx0WpnYUJ8)xE@NvXg2QSh>NlEsx;DXG| z_lmQGTWL%W9}(|eie5ilr`y_oJ8P3m$wcR@wcJ8#LEByN5Wj|zR=Y@9X~*{F+4`lA ze{hh+sFH4Di_OizC6?!YNf`bE^<3FbHB7pn+<u`QImil};rd<f<nm5b?4`%pauMS6 z-EkW10kg}a(~PwNzR5`cH-$+=1~=zL*{kG++SUhSX&zIxUi8E0j~4=_p<Djl!AIge zDjsF6Nf>DcC#t&jqN_F`1Sx(@R!nLk>}GDJ+iNdVURZ97T^S~qoTE8`VT8P(J0>!c z8s}Nfm1ZzVbSAIA`5SsS?&SRjs=(vD-vPz6SZL~Cc(s8b+%0+cZz;`xTM|toutAB& zXVJR7%~S_NG+k~_%&HRmYLRu^k}sP8jDbdYnffev;A!&r<*WVXYhkZy94iTvFzO@= zrN1IqU-}ps0xsS<=vm|jRWyX+P|fT!VxQm=#vl?h3h%4X5QM|<F+PE?S_nS&XaBji z8Bzj55~6?jNqz)46X9Ayw_YN3sumkdP|fRPgWj{)4E_!^SbX%TAiuk{bCRC(gV$Y# zPH<%NtoFAt;ogd%Wr4r&{MPbF9)*I_4Motpuf%!YDSD&ozeGk(h@e2Q;L11)lN0<0 zL`vDKXi{s54Pm^}fPSRDn`^ZRP!KG3OP-wtGTAFby8q5c|K%qC@nh125<(^a58d|P z)Q%W30P>E@^m$ABZ}aB=CM>{7LGu?6M?fb3FC+V(V+|mr_ynes5&+tlW0U^RRQ8|Y zEE>FS<SO(3c|mkcSUVvI`75N<)KC9rY94Dr=XZ}4ODBn-4J;q?(tmkpfeCM2_D!HW zNz1!4*4Sv^>fy9-IzvdD>NKH!7w#H4|8bB7<;lS$VjZgsUp?hg{QE8XVj+BoocjdI zy`iP)2s$N5oF?2yB65!pxD>4FJL<;cK!SL<lo5QLXh_H6bv0xr&HDH4`@hc}bQA*f zDs&EckF-7+DP-$O+D(LSX>Iy)Jru7|rnW?npI20`_vQ2=-Eeh<w+%Rmm#gTf_5s$_ z!}iPA(RxwQKgRY`6k@FE6#%|W7Zrzuo@_hk>eB&8vo(Qu0AV5FdAp{G9mTK0{BBSw zB)U{1<EBURn%NZRA@X!ibLVd#_u>E)+tbW>H3SRTDC8JY=6q*h<4erOm^IuQX=v-$ z^B^m-8e@B;x9T5PpRlg%M+|gy{w(NqPk-eKklbwe7`x=rK)yT?9mDFo{m$7HJ+a<w zrvzIR+iN5x40Um7ky*Q{KWnV@$iYoAXvw;CrGq%`eHfN_@9u@vLNB%R<L$-U48HfQ zDXlJht3WTF8sz$H?PY+_Sc2AjKMw*7ihX&BGsyp&n_B+4QLqh??r0ExW!tbt=`3?I zl#OkELG=vEcHoSkc5vn;IVod~R43aG6Ur!=T8~ITRiDM-i>6=y_P$ngx7L$Py@VZ3 z!)IH5Z&+h1Cje^C*@uKShyGzN-Qz4OMpZ&6U?F5>1-85I-96Ojcf*z1@NfJPUh^Rq zF0S#emuzO1p`SDJUWYR+d&VGvEQ30Mq*$Ca+@Nwd+xE_`VfHSIkEL>2qARd!GSSe< zX1$yh%um^Vb1P8|uBlVj_7114f`X^50Y?IE-@KphHyQm$cb)%OP4Jy9(5fn7;2&Q< zK&d5ncZi8wS`+;*jbQ_*^4pixjn}5h@mBlx!p^Bp0s$d8o|v)!-43VuAXS}dUpP>& zNT^#bA>`YpC`$d`2QvstE0OpcYW_y>f%bECoX9T=krG};mH|c1Li7|M)knWT_?NY> zt#K^-<h0!<-%kyBT_ev27scP)(Hi=nwI-d};lTII+BMrzGUSYL?d|AN$v^D5`0Q5F z-S%*tuq?2dwrP^fPbQ)BDEt7TPc-j~i5wvZlm-4Qq>Br5w0AgU7A6#S=$Np<XG$&& zo$O=)rP<n&cCY@xM%BMQ{oveU$EJfrN-F+AQT%RP?HyZGR7(Hg-HO9|#i)r5yjNOM zjY1BabV`1P%@gnkHO@m#mGny9a8REg9iJC|{Y>YuZ(|jFU#dy`V+6<<L76UgMhMQ% z&PHqP-3+Mh0uZvH!NI16hINf1^^=u$4<JAb#Pup!5ttx)^HXR3>r@ApEh&KS?vUzX z@}Wckphd{9o2jY6{cqPf$`clEu0Wp01BlLmnd_zbG->=)Om_~^^n83X^!0}m#*DXs zk4QmgKg1Y8li?f|0BO<Ak=^_$n1a;GLV0Ey%;hvq^LPXi>A?LkztPaKW3{vja=iF7 zLnaOoluZBpv<p%`J^UF$)1<8}0GL@ZUC!E#v(rfchLS}(w*<MFVg2%x=VRbpU$P9d z`ZVC`AR`{0nNFSIv;xADV#G<C{C8%KBu2JMl&cn6x`3FTWn(!jM%V7alO^5)**>C{ zr}=lg+4)qOYuOPL=U$p1Y2)~@@Wi$1%#UW@#ivfH^qc=CivZP;jusXc`ue1!a$}Y* z8XD6;B%+R*8ppP1A?E*<<ep?2iKww;8ri#UNr~@U0K#w!(U0Dyc}9jDn({Dge!`=% z3&Nq^u6wQGn7ZPUzi_n*z1Qm$Rbe~E<Q~|{NI-vAzSSRz`*John^W~i@K_D*Pf^ie zxuTPZj*iaKM&GU7Lg&b@MVs<!Ddg8<Z>7Sds1n14M({yxofMPEnHNHt0Du?2Mk(&4 zIR3L}YM|hXo*o;)6I98fjL@+fUelcYIAXk>xG=o}4{fUG7&S0PhV&@?3Ox&ObzOxY z`Ifqhrf-ayBYwKa{{81q&DK_cebRTSzn<qTQ9=GX_3J0xY(g1?7Kb{Bfu`q9lp<T+ zbNIe*9gctj2o$`U>wf-=QUGHKPNYm&SQya1_&tW0+x>Kz7>T@)?f-mF__PwJ8~D<) zh(tiNrl>HpP&Dq2GuOLxJ>UG{(92u~^dop&SVLtsc>%j?RZ9!5H{ejDdZSOgsD5F9 z`FbEjaJ0=F<yQ7N1Pkgrb5tAefA|q32U0hytR^T6nexaaCl~n|Ge$^t(zqb;di&sD zvXXUd?8jfzSpN4>1V}@3lZ2y1-Fw)@*Fp$a$&4m-C_aWAU-K_;8Kcef0U-~X2f~^u ztOG*oh;{vU6MLJ@H;VbuQF=G02iM+$1`a((A)}<*d$^2^<RVOw@en=X`ythh?DX>T z^@w>}e7oOiF+Y<umyP`n-U|e860o>@dS`k@ZV{qx<TVt~gJV>419&JB8CApGm_}vX zgjN6(QV+aWXM&>$MdjPczx(XJvDI)&I3T%WO%yGWqMlZF-evPomgEKV<jUnt19~mg zWHimFLJc+*-d%uQ=klg=wtLZ2k8N{>pN(FR8OG3#OK8Kz^V>iu1~Z-SJyLdePT3`4 zrq=`IgYn~A4)>jJ$Xcx*TY7eXw<#*Uc_%3ax{F{>;qYxcGT>MpEnMqJ1MYGB3T^ny zTLle-PlCP^<6W26(6z39>?l-9f4rF;-;=Ag8)>Q=xtlDnvy*WRdO%DdLr`^ZXE>6* zLJ|GvHpiI7b30*?o$sOuM1P-VxqM4o^N3{z7o<Rgq_*%|)gYXW52A59zxA1b8Xw7L zYTCTA8mDZwB?RyvtK9B%m#q0J{keB@g1$}XRboojseWH23g<WPwC~YPdb#Yfx$0TJ zR`W1d%p(<<l9c2N7#K{#FD@>Cf@E9}0zd&tsS!K%GUoXHUgVK(1jBWL)+eH%8_>15 zbz|JW8kHV3+Q2(fGij6bA=m!+t?X<`BvAhrI)0Gq6awU)0drDp#O1(n3yDcK)qw~B z%+zbq)CL`+GZR@_6J(Pi46VskXE^Y{Z_T3U9@|Pq^P&+$hCIW3LfVQjtCxJla*xPC zFX$WDt1VPRUtqOBvcfwT;7e7d3A`lZOmUP5%iV6$H39udlqX^%)cgtu%ixK!r89^? z^~PGU1@HF<F1+wHs+N5DGK(macDUx@P$dIXT~bPa?qkz0s4Xi-?c}cHo79=&*j9Mk zWbx>(9l$I>eTcNvpG^_JKnC5Akm|2h>koku|0Ea{Dofl^0&KZ{x<$Rh36#VQu;JU$ ziEynnx>04BV}s;!nR%ZviOWqvxVNicm5U{)z<__<PA;xt$~yFf$p95nYd&+;16m)k z{U(IiTM@5`@K*OP!<%Z{+eaC!F;=XSk<k~WrhqVj5;ckbTRlq#yJCcK)A^QgH$Ba! zT4c(%ZQyn^rZT2<B%txiT{U@Ji2A+XLKE98q-*aFU$c<9d_dN{8II!I>}+FG6VRs2 z?Ydizo2IvZBU7enM$xMYCmjMXJ{vmp!ovD_pl6C}zg7b_gEwW%%Lc6maY}%ZD8d=Y zHVEU;5s<M5q;4oEC?J;sJXV0BK0H(e9FvNQG?#Qpi9(&C!Q=^fU61Ai+gN^l#N;pY z#t(<&1!>$s^v$Au^$-IhVGaAkqG1&P?1*uTUVamV(7_-7H2{MS;{u)J0fL@0QhjfC z<Ba{8#vFxWy0$bJ&JxbDvh;k>{_rt)UGt<Dt)9!=%d6)snwiPsc{Ah4#<Zd6`)Kd| zS(t(Jb$8!ElhCqmU+0Vnr6?_BFLk>pzeFFE3zZw-mYJst59y=YJ0d5-iO~lAfQT~U znX#fH*2p6oJkAcyVIFn$BR%*b>l(263j`b45GIK}5^>sbfY>2@k3`fMK5xP?Xkv5d z5U4QlCH+fvfj>5Vc*5FPLMcnR%Nt^Z>j|vd;Pyo8Hv@G*8BE@mYjW9v<(PtfrcyDG z>esVBg|wpuD*(Uyc&;3dfi%ILj26~XZ#sZ9#m|<C#vIW~*oPF-x#Q#%K2MdKOBe!E z7?{kMUi$*JUM;S-2k~II^K%I3(k*B%7B{v?EdV~HO`VtLUz^ZcNzZi(dydBsR&E7Q zit>#Iy47WUmj$taoO_7dLM$miP(PWWS9Sdm-+SQdf4c#x6#!|4rD<7>XO>k~`T~H| zL$(}Jr7To}JQR#M3h-;<by7fE`V(L#%w^oiVx3Pwc8-4mO0j!7zm_?TrSs17H@3DC zH-PV?t76XkSM^qc7ON{@U=xL!&oah7`>w1Q*?r+@A*5i6f64e3!s43s3947}X|^O{ zaL((HF4Ng$F=QSi);M6LEr8P6=+7WV+cZ7L60~b_AAIdqz4W`NtoroW=j<BeMZ3}c z?NWmD{7uE~WvuS?Y;vY-@A9PD_3)4bD=rpfZb#=KypO3g{-2Qz9RrSP?}Cs_5>kvj z!%Pjf1erWJYP+MiWa`oA`g|kqtT_8*C$eJZ{BTB|0hpx47p((Bbt~%o^tLx5U(t{4 z*8EtUX!@EOr_l9^x5aq;*Y7wNy%9M%lGB{ezAaFvT<!SeA!LTa++KJ$?`HPEL;;I^ z8qB)ZsnO$ozycR18-8`5J)Kp6<Lv)uI0eF3oNj33TClP?$ZXLh%g3jHa@fKlUfp*n zwrZD|QvvmF9f{{N8~3&~6=|A%b7OSZ_9$nb=)$vxaDmDl11T0Jclxk*VL6fQ2XLZ* z=Sb>x=+rG>xU~R8BFb?Hy_g|el1U+8PSf5{AcHYujL+kA8A$jc$}9lQlQ7Yz41i;7 zUY?)UJ&{5-zFK7~sHhBnW>CreZtch3aSq2kF*1S{i~Gh2e?&Fc{co*#o;tt{W4&d4 z{|zB1E};u(Jug<xi3_LV$WJn=LH79@5UZ}CF-6=Vr4@UQpuNY=x`Lb_ao#<lsMkPn zGjmmX=T(Opk7c@%*FeklI9f?0@Z2+gbje-iS)5N3HX0Qm4&&xN(Uv$?WjR-QypP!G z?Af5yxNBLEIAppaF17<iuExp&({$RGKP*I;5RRtur@8LmDX*h5oN)PYk?{K!9RTl) zBqjzv{^witJ;jp8>u%Elm&4d@@2ZrPPM=zK9w|Pu=cgCW*%^*(DvlAZ*<W#Y2NSj5 zeYTUusa8Gr%5Wp6t@>AZF0O<f&6UbShc$XlYvFH#@OpzT@+$r}_5)~y$)R8QKq&ul z{~$v}=4y`>{g}(88D&;x+g&EXCiQM~k$p=05S^EA5bdAvy5S!UOm=EX0?Zp7$-n@O z-^AY5%7Mvxp=LOD>GH`5<iZ;fWa5`G(YVIXCaC4MYE&_>9jbfL6FTE^V##Cg<gL_5 zF#6K@e2P1U^tlILE`x)3U{(IyhcYK0LA>%9{q)T2^7hBlYfM!Wm$D`%$>)l@)?5I` z#6$UX5yD?oTpD~Y-g!@>rlC<VWqk_RVa|op9C{5h6Shnay#OQ3=W3u>C}dIeX4%Cg z{Bn0p+JE1(6t*(H?)njc_}2X&fuP^MUED{E-K==AduwY#jc^PhGR*HG9P}qkJRo4@ z91nbkx@$5dnl5F;#Gq#?sH#FXm~iIFw3clfgLrtPo#uZ2ROgtzzIMkOQt<X8g*_K# zT>~uL3>5PuhUcw4(#-hhDRFNXjTTG~R}9(;XRj_71m903JSDmGf04NGxSktikzpSl zThOQ)GD9UW(<XO}i`ss%Hu#MUAqBtQ3=<RtFw=8QP=@=7D>25kyZzW8hI^Iv*R0sm zGqsmoR9QlN^P5*PFpv%Wy%(!9QJ5{}3D<y+-cvE&XWycHpq*28=H5d|65psdirEw# zPFLG~UZ_9R4}apP<L8gX$dF=3A|*8sK^t5d?9}hPss5PG{1i|?i!=ajva99&wkjFZ z4V0mF0pXNCZ!&NEa&$^&2L^@G)2U<WFp!E$W6<Xdf*FOj`@^kW+f0zkp8c)#S>8jH zQJ^&}%rK{Mm__f?x<|-yb_wN+89^Yp;6N%WPz)Oa7|W&0nZugY*F@IR+{01Y<1As3 zjcZz+>I)g}X1+5D2U}&B?lwr2<0EfrT*9#Sd#e~pC1UTU?l!;&ydBE)^yOk$v{S69 z1S!_W$iY|g3%$k8y!K*LmE+~XuEyJ@-+b*Psz>pir^#?RAoU1Z1a)$SS~%@E=zS1e zva4rlvCGbekfqcvUARj0;74I1RO%Wq9EXH1C0o|fTn!{}BMM~;75g<R!odaRbV%BU z5hlNe&Aw#h+7=V5Ghku{JmgzUNfRa<Jtzl^JgC>5YcJ0?pJl_y!Vi<-;tKxe4s+lS z4h@+zYs!|m?8?HgN~cKZ^`fB2@MMe?s)WgA&nEk7g4es4Y@JQ=&s@FJPdW|CuZXrf zIy^_WoEZ!=`o^++MnZW`B~P_?gu-)aKZorJi^R3^<$Q;Iazl)S911=8`H8H(S5F!x z%r6`WpB4R8$fDd3N)sww60ZrngZL<p^>W%Q5X^-fy7pN%X2!ZoP>|?GSz-9yt5?@; z-Z_17cDL(aLcZqFJiz-uM-q9q^muf32TY>y-Byvtq@q^8D#hy&hu_saMG-@??R%yV z<v3<fP*wwj5fES{CBQ_YAQ@SiRZhVt(z)Gs-yn)+_EHK0VnOPGHY)y&2c@WQ=3|n> z(4g12a<{1`Gd<ylxB)x^o$=D3FQ!f=2a=q{U7#?8!;nme^PLOiD1Gm9d!;6lU5g>8 zV5}Z~lcb&$W>QYs$Ekfi@#YFMg?FgR@!Z%V+gbc!66-Sevte70LC@mHta~ztvy>V? z;)fCfXq}!bD=X<P(BpFVTTFaI#$VNW8Hj%;V)qpQf*T%<U;Y?-Lh?-{W9ojwqz}DY zbp(WcH9+suZBn&CE6zAlWnQ2K;wA<uUk{c}r7m=6ea`HAbbL+6Z`6=Yaz)+-TtaPB z&@j`WblWB(VUap@=ICCEzJs#>SsD}Misx5!9^@5F-G+rZWW>eL6i4K<ejth3-gRNW z--!4WgMGX((O%gyFZHoNzh6Wg6(b@N6IL^`DyVdDOS4tq>uT#9q2^O<Uw3O-o`Y<z z1Wds1D`MhkLOH8FI%5}%L-fJILOfJvt&zNt;{L*pr86dP6U2kuude6#k2}+QjSK5h z^Du_?_R4L|jk5wK@CfXk1BV_0cmBu)sI;`ek%fHH7Au^`2Pme6gQcyVi6&Mz1F<8$ z{>bv5#}Wj$_a@MfT3nYXA0wR~Ywo%4ak-F5^f|Ns8;9WM9&Wg~w<2TveRFeIUgm2b zC(Euidw0GY*%oGZ(zbCOa}n+^cDc1+9wBrU=;E2jEE)w!?7f77Ps^?4u&;h8l(_~Q zZi@5eK&SB78?hAig^y1OGBWPd*>v<8KAM%|(<12`m6}(KJxMB{O@*iNmoDhcR!^5E zvLd0!99x^_g}!?|u4>V?XShEv6yXJy8xub3Nc>=qBvbPN&@Tu?1L;iojjGE6c~5v? zP?m_-Cv|lkha3c091Hm#YW7WZY6!x^6w0;eKK*uVb(7sf@L6g8t2Ue2^1Yk@ia~iF zR!C+r#VFA7AvJOQ-Pq@ZHzpB*(#+f<2?z^GagxES;?VPdIx;S~IG8TEQ2U^$OX%X_ zvJtxfgQ;AncBsy+alBJcW<By?X9^|W?9=9$dov{synWvvZ*@KNW_tdb-W8y?O$<=m z-Sn<9pGx+63-Zr!daxuwE*Qwgk5qVoM50xl*^9|zyHleEtCsH8KgCx4U2Y9={+P2K z%$D_&2ei69h<J08lL+4&TI%k4V6zsKmvhH~7;@{Si!B6jK9-e=N|S}9c69)umXowH zYwxGwxl!f-EBnX($gWkRFIfC1FQv^@qGwG@lXn%X--zD|D0zp|vk3b?-rkchi8fJK zX_%k>h8=62^xV)>6aO=V)#v8GRl^bl(`_|1$Y>a<auYc*_#Xb!7r_c#0Q7TaBgXjR z{ap=A@Tk^rke4Y~-BO=HX(ugHxf)n3vAT3B)^<D_{$UCPm0|MT%8IcL9^O;fjfah| zrUtD;*k}hW=#3M!V~689EoCcF*flENW!RzUozE*ut**!OM92S3ZkRW<y-}HvNr$T$ zuhivMu|_>T<}E3FGYwB!aJ7g~pDohth4M~D`t4XmD+4zh_b=DBVWf+#;itNAhaYph zjBher1#03{o5sf!<fFJGs6NJ5kV>x`q~lvvTYG>N<h}3=R#<r7MEnA@dK;h6LDbt} zW#CnS^yYQm?Dx9dO~}5Q%#(KblIhtg7f<m9^XiwrW7zW)4B;eD1OsM?_=Oe}HV<>w zvW3|e+-mAZcl_DXHF^N67`d5FC}9S>R*?oK^|gMuEz_?z%*2@X>gX|<0v~?hF;OmZ z^H5<UqT@=*i;CsT3vlyWzR9HSTmh;ET-}PCP;Z=D-lmx~k3jxVBo}}E>ipK-qlTY@ zkujSTV#TKU>FA>-^VLSH->1#|^XtkZcYHcMOUr>*@pk*vVv6xA?A+rb+}usmJ8v|R z5J`Fi1+neD(oG?S(oFO!Tije*#UJ{e2qDx$puY2msVOzeM1D56Q-kBsrgWRa#XK*f zoBYe)cD%eqNS9|FXkCQd4rJAZzLIEXZ5b_+j`0|b>b8~)=#vXGq4b<LyZWvoMe-BJ zmtI+8ufOsy2a<p4*ZCKtc=v)JPtl9rtxPKd>px-i3HUj?4W0d8>kS7wLbU}&uc`80 z6dx4s?kr<`S&-LWUGZu>itqUI%Qs(|>T(@d9CLGyY;tjckp+cHJ>nrwmE`!}*Rzr< zQPl-Z3T7Jwr1)<eNSegZa%75xrapH##e@j|N||8y&Yp;E)Ee<d{JT@Npn^eHu`6q< zMEswNB;{PK-jm*QZch>Ld4BEsIoSj#@G=^3Zv${7ZnMsO_qZ{6>=$`P{d$qaX=6UM zQE`wV^h*hlB~|b8^)-NTEFF0v1ywILU3}m8fNO&h#IO)VC~{VIW=XF^7TX)1Bc7w8 zWs6<##APsTOd_%lezJ9E(gUBlfEf1wk@l8Rac$eWXpjVV2=4Cg?h-V(2X_eW?!hfM z1b25QxVyW%yK`r<_d4s`_w&AA)f$aSF=ve$qxa8x6=LLG3A(X;FgrY25PZ$zpu^vj z6cKzZwA06zp%8`Rr*7A${;S!<Iv7yD&PSx^T9o~M@kr?Z)J?$Ozy{d?7rzHJy!%^7 zL4qctfDi^GqG%s#{&Ot<_jCqcd>O<9AoVbGagtMjqU*o4*Z;iy|GCmvfrikNQ!l3d z@8kXZjvJtmH@CO0Z3fyMH=r1UdVe2HIVx!Ghql_8bRo!!8PZtW|K2UQZwU+t6LQsi zWAhwI3DVyUq?YKrnPv1WG$Bq|qShYmKmTqx2WVpJJH}=5@PCR9L~vAak|6nZsKiKt z;Azb%b9$?~hNV@{*UZ@c;+FaQ0$wz>206@}g8BQax0KH<&r`vz#p5S1;49kNI;phT z9UUDN<I}6Bb<31gVR!d;Zq3b4V7@gB7dJN(UyF8VWDl(@U><rxk7?5Qj{AM)x6e*) zIMW?w_V4YJd3aZc#La!uVY0%JTAjeqNgYo4$`L5$<Bz{YjPF9T9UpG4`1I%0LR~w* zfVBYQz}(z|=pw3@Y1ti<bh(l<{`nBT%K{6^Mb>nu^CObWv)XHNenz;OqGPbVlhaC$ zz1<jBFZMx+sq+J<e|#VMK~CQCva<^FBP6I-!}ClS{ero<gUWC&J<pu7j!J_)c@MGG z*-ERXjXeunn#blxQ{j<DTNFGmkvz;+`;s;93fpzQlwCUAjH-vFvDnzDS)0cbcwwQJ zn+k7XbR#57esplHvNH0FcbE}cDpsqZZCy(5{q&%8Zil&pg%?{C{AYdf%j79`)!3)h zFVSt5>Z|n#*!L%J;4eirCcA4M7P(XpD`n&RCClw)w&xjq-UL>^{Z@9_RZBJJy{<%Z zHBJjF<dS(!mgd{<nxt>996_;HD)qX{wOcA7M}b%^ASFhjZwZhZmH-oiPHM)nBKn5e zn{R0TSpdKihx)U`p@2vtt6v@OPuV-%WCg4aEnd}|r~wmacb@W7U*}I#64M#D$s;2w zHPDKbMV*>k`;S_F#Fk<?es@2)c(_-p*G<jbIX~94FU{&dZ8NZQQ%O6_DTWVIXsM`v z%?~ZYWHhj_S8vku?A+FVsmU~e3V2YjuHJk1=Ko$_o94Dz0o<*s)k+eJqsE$51MbT% zGi@+J-dOT5JFLC_v@e+bTR*>E_Tsb6I~MRtNojU>|1N1Yfe*|qD6hWS!DR01LBG7B zzuJKrCznp)IjdjhUDaN=f02C-_kQVb>>?6_cWE;!QI&b<%9&i`bbmiB2%XLs%_|sx zP4xE7JAc|l0K!?BNl7MPYxEqfb?##G?w+2eD_YaI+{Ydu@WWj$1TDk+GoHtRrO@^g z_E1WE!8$xT+P3>s(~@mD%IhYlHw>Megd~m6^9c}``#=k!^B;e(YyW3a0$mw6=oS+f za-lA*ftZ+xQXTibiesx+p|zv}({NYlOs*pR$QLco((=W#$nbD^Q4zNnPm3tlMo&+- zvJ^H;YDU_6ZeA?qo$Ke~5w6<@!(-ihl}(4{vew%QcdDB0(Mhq>S<tD<xlK#!at|Bf z%yrfFz0*B^2nQ7l>9t0XVdVCl*T&dpjg;-asDg@vw6wDyc6o{kY$37phcg&(sA;1z zeGd;l%i1+&iiRTI4o>w`(rf(0pr=5tC>~#Uhv{Om^4*h|n?LxR<(g9S5$ytQKK}}z z!8Oc`25mrVOEFcsDUTCB2Ek9;1g?kl{k*y3sxvVWu{(PR@TTcsi#Rn|q`cj+$we$M z`_6S5yk=7*Ypx$bL1sjv6aQoo!LhF(qNAgaF()=b{j(kWb-E%V3c28Y-<b$RGPQ3k z@srI03HPG2As}DV^1+lxJ3K$8x2_iz18?Z4z%4F1c3NYiL<1chFV2B`h~Z<RZke3> zlKMjOC05kt{^6*_jg@M4Wd*!3Pn4g`a(*m7KmQfq@pGoau_Kb_E92wG{2kO23Bs75 zBbihhOw7G_CGQGNE68fT1lfIOguc|n?Ch<uA5yC2G~Ulv98fbcpUT9e=1v#NS;c<~ z(0RP(_HrT;sMNGnS08r1WgMBSrLirUj}EaF6r@gnZ2});Hpv@F4x_za@aBU_RxM(! zbk1-n^j9rVJ*+k!-pd_SyqTOdiRfglGd98A+A1=M?`w0PMyKT~kxBCQ*iBFcAUilH zttLBC=s!$UpYg)0ErCUnoX)?UKP0FLAwj?ejlj;fD8YyKB%m4|I63idHzU%?fw16q zR<5n!p7e2%E>$?cdb)(Ss&d6@@s~!R#Y`6qbf*vp^L<;AP2`{0zj<0dIa6ub&oINZ zHOAFE3MgbW#J=IeiR|lJyMs@vW+WU_QT`}Cffy2K#!8fJIf`YfGx-C;R}|YwOd&|B zYXP>3F)OTR@)ChRzU_oe(XQIIla=xM=@|Wed7KqcU(6v0jplx@g7g70yBVC~4PdLP zay9Jh>V#(MK7omK#H-g_KGHNk<5@Q8w9tOd3Bs{sadFhw$1HI=o~uV8zdCQ$gd%{A z(7Bq^O!^*n)}mWv%ff0dE#_%?5kGlF6pnayf6<Nb?9mPYmpr2D!0_4T=H}DohNz#U z13dqps^2I;4Z7f&9e#g(!$ATL*kw0QiBJ`14g$x)&)xaVqlb+7u1%MzVVd7OlS;$6 zZk=stxW3Ncj$H<|<mZbY&gdqkE3dCUU%efjuc;*=8ZnqE*HmxwwzpHX^<f_rjE~y} z>8&YS3WD$tk@T-RiNBVoL!po_Vq?F`iaRT_f|5W+Aj&}&g^Z5dEOm^>W(0K{^oZjN zZ|9bj(s*umon6`4o16D1Pxm#tSSbO1KVF4HGdBwd8+T15F}Eao{<MN%OSvo7`HLKB zCWA{y#E#s<X^kk{{sV{zxn$0ol2#l%$hQX%yHkb>p^fdZK7$)--TSe4dj7E{ExsFx zOX57<wJcf)1nK7|_7^S1I;<#NRZZ*{-7-uEYB&>tBy5t$S0LwaKi&E5+57#xt*s5f z`%~9n^83MT&&XoGZ*~Rh@p=Dy^b$aNP=Ov5WISya;6)EH`h75lYgqQLk}cpaFpG@% zC52|zQ+8>iD*}aAcqjsupWO|zm>Prm<z)SCYR({nw~FlaA^xC;f>$9>4e5F-2Y%Km zPYio9D>)4-J=3)4(1A=Hmbpe>6}M<zeQoXNLi$To(fQ$MIOF7rO+O||=7ofaP`<<M z(OUgn(u)Ty9YgfpcE1JRf)EP<S<#$<cu9k$q;BO0@7}SktvxV&LR#UPso9b=I5>$Y zeL|9%%AJULY^T$%VTYB|25P||1JL&2)`5pEp;`|r0JK6QmC0PQ`^L|hN%5QLLs+e} zl7_}9gr-Im4wc&m$WWe5#om%*LV}&6gRy<CMj$fH@66d$b~oI+dV_w3HL-oeRAiLU zFFl{79ZU^(D8A1TSYPBN>JWAJY<34XST5=_B|}n%|M-zTF*Q|fI!QFzztxcVXfAP& z9^tc&|J~gqa~D((nhnc=rypZ)p(h|%YdzR?NPYBS*|xI2TYmy$mQK6+pDchn;>Vrl z#XU06E(XL8-gB_K+xuYx%F1ZE3au0T>(+S<<1io%{603SrDor$7Y=Xk8a;W2@MYgu z+jM$9(gxX|F$YhF);1Y}d+>Gi12da4eI%mhy$8VtLRCRPDTEyymsfN;7A^fAJdb`X zK>vcrvS9T-QYeCD2S=pEL<1*Y`QB3a`0XuH=_9ZEE-7RHKd5y>!|>h|_RanisrPTv z4MH4rt2sFTRKbtBobuBP(<Obq8f9)9DI>KoyxZOMxwW%D^%;9STIWfOCeIrZt2|pR z%r*3t7(AYMDrUuxm1AO@<zSvfEa@$XpT0>gEmpPcbH4Hfs|{y5sYZSY|Ee@SHf=|P zvlTP(2VY1}kLY4J_5>Wo`(x>v7gce?CgK}|<e4vB=<NcT-RD1OIJlk945b>@+$^L= z$bX~WbgFu8_eF?D;tUQCw*dZBKtf_~e+|rP{cR7;vxb6#VrviNOF^6i-nAM%0+IKb zB1HhR6lQ*0b3Y%&ffLg0hA=lBwlXT?+3Il8+L{>JjHl+t>3I6J<4kQ#6@dg}d0^{# zy(CCqaD88S=`cM=wtSxmp4ZfUigxit#oiibE-+PVM&ZkTw&?HSL&#RXvF5H|E<pCn za&*}g0(H%v{*t)y<7HJtTYCR*!K}t8-8Nj@U5ojf$JR&ZRRdW`L923)RW5gJxWU5M z3QUoc?Rw&iis{{Y&Q%^O&hbp2O}<lX-^1Z(pwam*2?>eaO0qzAMxy7B9-LT<#|+td z7J`%%ZQ17dl`723%$qfm5H$^;%uf~ZxR9}Y7U3wF8t4&uc5YFu<wIpA_>)?5mv>3c zPTQuLzWK9|U8OD9e-z^DnwGE?L6909%+G);z{>jNQEKVT28T+qy!smns64c(KaK@j zeKn@p!hHcVvn$#OCQL#Ot_+~~IAkcR|8yg))H(%rE*+00oAq*k4aM3r1kHm*M@`)= z^DD={@jk>K0-?UHPT^ZvXleYawd!0LcGFiMfh!zeChLzJKdFtV4*NMTwxWl3vkPnc zJp@&Gd3mE*@`9W}PY<;o2bx+j2F?+5!ph6((q;h=p^p;CHGoggMuoJrwz1KzHk33l z*ai%)7^rMXj2BPSnQuJ;;qE63jDDF3n@7jTS&i5G<McE%JS8P1CkC#mZPiwY_CNa? zFhm8c8Fz!Wh(Dgew{LnhB-AMigHcWTLuP@L?3~H-*`0&$5`J2uc1AI=AGVLzq<4Xw ztc$`9c7u!)T1=U(PdQ4<8-^SwM0Z#n!|mC=`S{|9Z%x_`_GB_T#MIZD?s{uP7H*F? zm<>rz=!r^5=>O&5oZjnH`5>E=xW?mDLj)pZ-L{S;Mz7KB+E`psF{$+CWg6pW(fy0| zfWqIl@xN^%D)<f7?N04MYd|4gqVww@*<E-G^>PDhLuZaPR;Hb&-c<hd=xn;IMEW%L zLtova5v>!`GynkMwUSxL$}$8F-d(Zv6zX!T*{_F>ILopqHE`cz&HhMEM){h(&bt_b zido?8>b2GwK!Met>_ArLW+6G;T~l3m^jqereo94x>`AMKW`&3GR8=#qqlzQ*r{))p z#dNxXu>SDHm2KGwMlT~TI=$2LOzS@U`r27rPcI@(Jy3j=k%6SdRG(8@6Ibk5v(nu! z_V#4?VwvVAuE(al&#M+p2IrYEk7q@FY?|Nsbk(C1vGLQ!XP^8Oe;0f+1rmn601;*- zo51Eb(?9q$kT<M;3yel}8fBe-*+lRrXvT0~1rQ61I*kIk%r~H85?Y&@p6<Aeu$j05 zbY9;pudc4R&l+<8N2{CytUn}r+_*8IUNNlpCH;e6_TK?SI6l*7e8x*{@7G%j4P_-I z0mzPrUi!VA9fls&_A9ipACZxNrg6MJY)2u8W)$&V4G=#F{C>SVT@H4QM(VgNv-z!> zRZZbhke|N>2zn|-=6XZ__^0DcupT-QPWO+f4I1!=MqOgBp0ZM`6Dt@7uR2-PMG$#g za%ec0T$-y)?A%5S?5kZlt_pb7Ab9ibU4k=APt@&eBi%7IxgHCbaWn4a*d3cZiY9}0 zQNCs@D{n><(12ePjTS15Aji>@sgtiMNcCi2PIBIX6EU%#go&^C~Bt-8s2ypVq$L zv7&BAzMM4_GS}j9v1~8Dhd6zRApklB(s5X5I1XS!FT`N$rdV5V^)osDSy727FQ4b; zR!@=QB-Q~9Wuz1wA1=)GJh>MC2sza>%0I{h0H}z6c<TTB@)%X@{i=7^92em%D`Vo{ zSkiiG5!V0uP(SEiykx<hK3cB>iRWgpjXdH`y^Ow3E<n)HOdzM{PxIYO!A>xC_sg{% z;Sc$W%yrh=HB3zJHN`+UzqJjUclSq<K)5N4*J@7AOSsiG@#7W(M|lp*h?1z;_wiJ+ zm-AF|Jc|RdJJ^QVwgkbJxw|VB94XzGLbtExEUU3}N|@R8!;cdqC&4C0Ry@<K4jxwq zH8n`1LBI9K^>bdv*<~6%?UsTRWeg{@T^r9d=l9-k?gr7U?w+hw3%bLq*{++K7k^!q z`KJOo)}ExKq(GizrC@v>1&Y#34*u&wQ9(wRG{mG|f#4&-4*(4focTkn=2E@>nViY) zpQJ|%1TzJl(`q-P1{MP|pHZV^Jh%@tD@GqKx4nV=X^Rzr+o`0TTmp#nMB&KVgMvQE z<}UKTPV@i%M7i+-g`L}}5P}n!MIW^Nak1MX-Yl9WA!;PK=2wv#4W)cHC8~=FhnAQw zuxj5QqTauu2?H3*uu9HdXyEpn(PKcf^?cDDo}(dn{nKO9<$J4#kYrdo#u6NP>P9qw zi~Z3k%GWVJA>5)a*oPzE6ixC1e$RUr8sF5gR7P^V;=^V$p(_J>yEl8)-ksW{%r(Bi z0&=jvhZ-)7$0*zaxfo%!yjhMT?13Yk^wck0g}nTaw!1;MMfUrTTICXGB}!Rz<#lRl zGNWp3q3<SZeLW(o?j_Y9l)zYwYf@|yvE&{loW+CJPv<mgaaenLezCD%9pm)D0>~n4 zbZ+RsT{tY5@2@y@LlHfFCB-Q0Lo<<rL3YjAO)b~T&n*Gnr{w&!f`=q!fhfj3DGabv z8SgW~t&@H&#RHHdo_VxR2SnPR$<+1S78o6{ZsTuE<IYg5bYOXm%l4YPNW!Dd7{z1* z?1?;)3}C6$cKVVrU!VL)a?1*G)g!PRvqcfyd$Qs~>lS(6a(}&3jc$!L!xxlO491!= zeq+v_I)pNvDK35Z_QHd*rmz;IP}Mklw->{eq+ij2;LLG1q=-0=$ApiiTSoto(5p}Q z?R=Dd9ablMK!S(4rn>r<8ci|>Z!1fIbTa6dp0`i;-BggAJ2Zs^!G&4s*4A{VoFasL zd>N*^7`V5HUD?@0(1pr^WL1ZH{2rIR=!+2sKMiRPR)8ud^fE{0;G<bj%q;}}gJ7Yt zsV}5(<1znBH$Q<#o*F2pl%QU$u;x~f6wMZG!;0VNcVK4g{W^aVj;U#CNi{5%>GP#b zpB(lwhrawuih5Q4FS`JRFid<X0c-rZ3`5@Ko_^Zpy_<u&m~CYLd&nWbf)uy!=;M*> z*BF6(RVBh7Ipe?0QOd4v`VeAv{LuBDom_~oa$)@Kg;;-f1wAE5JTq_mRLjUmXfnuD zyS^ls5i&}Tag(yUJUCBQST+FUbKV`^KT8Uh(m+gM2)2OW+hGaO>jgDt*`qEOKCQ$? zZ2E|q!V5tP%S#CU3+eJvgI2bvt6zqGUZt-`74FN1U83;cyQ4JYh(Xpxh5br(tm=0k zA5)m6!`x#axfoEDB^5HIhmfl-SCNQFmNv)1Z#wa~oJj72ZrbI0G?wG2y=a@SRudU7 zA%$Y;_A&HlDF+HT>h>CJeX~ZLN=`})rhR|>&BkXsCXAcH0Dem5ESyq&7o_Ka<Y=O= z3y4Y*#7ft5l4jvT)GE+lSj0WLWFh2Q+}?dTM$<S^YOp7r8{PB~0uCvMNvWcu<bU(J z-~Af<lj)_VWodZ=1nY;~C^Ga$p>AhQ>FAjfmQqq|dT_*aVYmwN5gH@P%5GB7@5UoG z=jG+OBxj2W8&F-6^=>kY+<i+pAT%%#VGb%M_<19z+9N<jCG3u6u%m=QMwcI-4Dehi zz}!8|u)*L@%|p;6f$(LJGWmujoArZc9gmf${+zcS0HPoi@@tBWcrN7qZe_(KWADS4 z1Cbvd?`5B?Dx#8fyu)KOXM4FDZK)Le#nP#y3W`phZ0|W+y`EsxxvM|Ubdd8s^%DG^ zHV;KBU8`mEn(Em(WWIp0;&+WD(E-LXeM6w}+P+=z6Z$#5f2)T<g3j`E+Ds8vR{vA_ z+g)>WxM&!d4*e^}*49AB&b^xw9&Q*rDVc=~nX0>{gAU$cUTnS$?&2-Hy#rxDH@H;F z|ALwVkvgm5BuGwRnVGjMhhK-ULZ_z<GFKJG+7V?N4Lz8+WxWn48gF55^q#u=?)tUS z?xl$>=`-9SC!u;y>M0Ms7j)(<se2V276S}xWFtsmO<>c(v1RfaAWV$;nywg@$4=Ck ze|zz-yJ+P65cy=FVGK*wpKUm-d#}XH!5oIW_5(8+oMH<JqT9p8_;VX&i!xar!>q3m zLv1)p11dXu!$QdtF2d*t5j6nibu8;ZSCB-V2{SWeY7oW=7dX`Y)bp2TN<xFr{6nZ; zx&R1~SjnICBt|8WYi%ztz87dIF;Wn(4S`mC6~2>z9vrX3FS=U}qR2shM9e5u2M%0m z5>;cHSWi7Da=+kzR9&ieJfQ_3W}%Dal2#zrbHsSPKd<-qg-F2mNq%yBd)%+0L;fxS zc>a_C-YhstEMy(HK$%DNPx@}U>sT@~IUQZ(fye+!pLDCFUVUTN+nIAm4#-;WK(tP; zWwgynP+SD~8EEg+C8gP9KJIQ=8N9PqqQ#aE4}C+1&}t((!PYz)$?LLvLFzyU@&PuM zhlKRT*-$-NN@-xzB{(!0_;k1{B5v=2>=!p5d1T>-qqie}*7S6`s9PMukSrLtU1+sb z2nQp~m=i5g1U}e%V%@|s<ODVoox>E?^7_y2+MZV<$s%a^BPos-?{Bg6c#9nXSbA30 zknXihn$DVns#p~msC;f!Im<5Xz6JHLQ?>sVqQ~%<qOOqCfldd#_hGp$HKm7Av3zBU zuUjJ4X^Yv%SvhUU#z~UfR#{6@J>c)LlNssr44t3;jaY4c*AH$>bntU^RSP<Di0!#_ z&FJ2x;4A}2^lw<~kM$q5W-h3S4Xot*^RlpOG*v7o?!#Y6d|eMG#n^tGh)^%#EZmai zg13n+<KUU<P+($3K-m*vro<wYl@E(@t$>t>ekg;fZ{3b7o0)*M3jf@~O+Hp?X>O}r zcI<#Gpd4>$#G$oa8HZRQ(Zyo7Q;pVF8`4+Qz3m@X&)Eh!6)p$=>Ek3u2M1XivK0<G zelJ-PeDGK>o9ZqMgKcZi%%|sU*lj6U*?d@kF8e)LSZJX_it1l{Qlv5N(C~##0&5I- z`X1dsLbgq%q@d?`bO76^+G!=@7RL`93f8i+GNnjt#G3+Au56_(tsftwj2E6z4G4IF znGDP*DS~9S?4+NOVjnmB5$sTbj&OhvZ*EATk(4$D&1k%vhw;<g+#J*AkCs8O!Ax*j z{w|qJ4JDeU@ivy8sukIFshAd9VV<p3niVR&$+x+$J<))kZ-n-4@^dP-iD{0C_MCaY zkZhi8nN$>}!{Y83siw4Gta3r*&RHKfs$#1hiozb*R{MrArP*e5lrO9XTEeEQg~QLE z-#*kD8jI!SEgv1FMMPlb5PEfq91wlYJ}r>l{FuUEi+lp#F)<d49Mv7eU`u2yfGYAz zhyF<JGB#<uC!M)2&U`CCH+uF1x_vP>fvpb$-!9_Bg2RkF05S4}73*;_5Ha$FH5&6T zWB8r(&tT7Vw*7=bVHeJMLQfdbeUT4djX<(Qs&z9ZAt8nX6+O_rK@u|H2vN}G5H{w1 z#{(yF4cNPN`{dy<HZ2YR->obF9hB$rJ{tb4I3gIDzSfA1IPe3&^fr|2OMp;sM<Rg( zZyy8mikT?s!kFE1W%U@&Fm(7I+1LkAEJyxq7h;A=SV~AOO7Mj|t)eVL9Kj7L5rO&# z)lJbhoG(N?1i34YoKJ~-|3y*hP(Z0>f0b_=9qBfs{il@2n1vhwOHlB9G`#vRjrzY| zw75WpAn9?rITimsfB%QLREY3F?D~JbGGr+HXqY?>{hx+H3>oCgGiv98ss2CpUuQq$ zFOWyu%dIywAs1U0NsE8GVR^)Z<Kt_$Hou_%{Hg#4!LRva`Un(gLfGJ8a{K>beZ}xW zNxYdcU~_ENm<;}Ny)Pl81nedvLoUU?q}o3>+^HrcKoekaXzsmcL2Ff|>cDZK!e%-X zH=Tnr@)Y~IMX+Efl9d@%kF<BR-lc|a5ZS}aqq&HDF850Wn~EbBf{L^0>=EVRr0b<Z z;?e1gD?J(P)A8xS0TuOhiu5H488}Zy<Q6kk85>)NfCc>4qPF_v^3rEbCBu*iIGMTC z#YJs}thvK2GU<&rfSGM5TQxYYGBz%{eUfLU^l$>Vbga8;=2HA>r(`ZAZfooPi~cKG z(cGcI_4TVYJG!h%RnO<o#Vje2(*<o#?jnY{VioM9q{)hk!I5W{)@Rp`G|9;z6%94H zBpXisJ5kI%eQ;JN^}A<hXR*MyqMsSicmxE1?;U>Uh{WfQ^oM4hoSf9-wogv_w;oS} z0l8I_d%e)=&-J1q=#(8hy?X%j<VwHOIu>OX$os)^<Y>a`vMr(Q7|hH(r!;$}<=krP zrPH;v4W~`Fo;$PAURhD`KI>oS7C}W#H(;W(pqryAj^&uLZcUvdR-w?q&C>*BTz7=L ztvjAFV)}kyYRU7qf=^6DtVK%;K<U%IujO-dXG24GDC9s=fhn(DuE!}Bht4R3CUSwa z!T?@w)yt~02fR2ir?V}KuiUh?V_v!L@^zZNt%ouwNzP^46vCuuf87JH>!;e7!+sL? z^i%>c`y$1&jI)fPp`qJzGTBs}8Y9KdeG6{euEhn&WE*a;mC1z-doH<-t7=kWVlHxW z7VnDD`r{(}8zogEn)Z6V{jsrFX8HbRt7V~imKi2k7?Chwl6DS2ViXmb0l72wdn20- z{mkP$+d&A<j)2)TH#e6dtzH@m{N>GOd3)NF?iGnab=`ELXm(4PM3>_4N)^Kd<;z^( z22$(gI`iILF^s+qIM9QEes40vtoqVlFmWGegJ*qk7Wf87>VkzP7BAQ?uAZaf$iYi7 zwaUWxQH)~BA<JS4A5y8*dmbZ`;msE<DT;yaFeeA@^3t+4>h)6H#9nb*{CNtUW~}vW ziMZok`#Q^Coqc7X2_wfYk(Ql|<eLW1Rm=Jgjb=V;va1cZ6Ah>_WyLs@FJDK8<*X*= zymq4dv|UAaR@e`!#(kZqpfrQBz^mOgyHrZX$;By;4fGC*#^;O^(k~q5C1sAzj`?E% zlm6MrLPdOk@9xo4iA**<w#vmeSHp8dwZoF%k;-cAC5yJZ681Hf-@AR*{Pxjf{q@ZG z)ae-r6dN_Z{!_9TV?%;>eIH{Y{dYTPBmljtJ@-SD^uH#YSlBK$b@q^-^5bO~%tlC4 z^;$tcpqH-B7sGzLZBsm!V`=tyi(1@z48nlvcUxY<x<84OFDhoU>|_hMCbx_ZymzYY z7Gn|1X{qT>i4%<=<;6Al<p-q&$|kWmJ5^ezri4Yy%7HJ0n(*oBt%+d5>x$$pJLBxE zn$^YtEuegw_GeoVFu~*}O>Y@Gn?yue{#+7b1y11R{P{u#ha;9)?*vXI&fWd33BC4^ z0u`L?r4~JGs7}jhJO{L;1)|Wx1|X>W0}se1F%VKyhcN{SmIi9pTOt(=P6Yn+`{$d; z&<rrG9L?pbE~7s^ZPmj(#nrLta@O11qY(v7^-O;(d@@610=v7tzeeKTytk%tb5U`! zEqOhRFu}p|E_}tHsm0-OIhgOD(!M2=ZTg+WQb)CMEw;bYg(A?szO4B)r->Q%x%=`L zVoVH}GxaZckYr=Tge%A6YHKY}n;iph-E|C+D|=lg5U<%(HoT|N_mb5oK?XdwuOBO4 zP!eLY!e{MvDk(V^G1xJl=a<)Xn!L0pUlAz8ab;vMV4>(?Wx_v^+O(OC4ph$1w{W*G z98m?2wLY1@JU}sS_XTPM+54VTmisR*E@D7wYG|a%n#jo^j6^eZ-5$>eUym4B|FdBO zXgD2sPwaO(!`!znNP#zF!`@X^($!oTWQ^U9yNb{Z=y5vJn^@8cqCMZz)c5`GM8ssr zLpJwIDo!enon$2Hy*!SRp66o|5?o&+xW1smXWNR3zIu5dr8O;l^3SDXLN4}k*W0^g z_kr;vRYNLMS4V`hH=htsEaQ~W=l|2&B(=5zEf4Ag1A<sKcm=PC$U&0uW9(<xaNBM2 zq&US!9Ig`4Cb0k*qQ~x8kj=5!t)vyLJeyJvQk$}o#<QoS7tp*<)8*rvGyOfV@bFPr z&k>CY_4P~GSZ>_h+GVr=$qx{rFK26%QT$$#_N`OomkHO;uW=AM{Evfj@OV5)85xlU z2Y?Cmp|4E6X7BC!ykp8f_VS-28bok|nV2A(DHF3xVGSgAD^}F#PQ_6TR-;fG63qOl zMN9s7iY)pQ7{KKL0Nz1APWiEU{p}hr?Y4A{Cm;&<)_sq88Z%CP{uyc}JZM;$$@o=( zZze5PKP1e*<8rOinO%%Lo}BSoxlDKcVN%@e8vUF6RPyKo?6LFt*P>Z~aYtQoY{<!~ zGQQ4V=*lMUc$Mi-W(H<maAF-1MtCK{JjOkxbJvpPFcK=nkmC}6^J*X7Y4^#qcf3(4 z5wi&i53%I6WufBCbA1;F7=Ba2vz$=H0%U=Zo`c`Id<G>&Ku-B8AiayC5Z8pq?~+Jz ze7zkhE-0?v^Di5C$sJ8c=Qm?|>x_JAzNE$jVn;4_bl+2KPxVzb-8+vN`#z=zbUy|* z9$b&}%N=}ES6>9kJ)73cKES%&4JIkvU^1TZ^5PL7*6sRc;A%}o#r(6Py;K{hg!(u@ zyO9!*ggE*-NHDVL(h<8p)ohQr{0I`L#j53)yKVGzYT*B!ji0G66Ec;^woI&qgfJoy zsnjp7B?+_!-@AY|Gf#~_v=Z8pi4ErW3%KOqPPBkNDufV*A1gKXM+G@KXC)1{t{qCX zJO;Wf3*>~1O=m7p9sJ7x7dDNWnznkps8zg|gKNE}ADJ9T)SrE=GSv$E$nrDSiw$6) zH+BmBHEQfD`|W6>e}+<1^vIx>sd_aUQha#SqRR%^jArmiNsfz41`Q=8h|nT?;34nA zpf$r(I6MRvc~jFXAO8~bcZu?=efIU?DHKp6AVid4ko>H1JF-ZzAl3wwm@t$AK-R6> zAB}H)O0JCnoch%gQcF+h;0F8z52#(D3VaiJZ1ITO8#}m&`!JYy<VIZGt{;Fo$6gU` z6QZOu)zgQo%8NfO84p;HiBW`hnb9CI_VQl2`B86z8?09~)YJyrx0vAw$aQ7GqZ1Hr zZY`A1J-G>>AJz`@Ln-|a17+Fn;_Eo8^<}z~Ep5Y24He~NJ>>0mbX4(tHxaueqMW56 zY_%UtkgbWgj%R1B?A>3DYoHP<<<jvKgkKUO>W0!^iY_Gb{_d!H5kAYn<nX4OPp?g0 zgSy}EL?Ze`QyrIJf|D<#FW$Y>py8&?h_u%_4FL86frN-_9PwpyQFk>a69df#ZzSUg z?uNG}&&67nhhO0d20?0H^M~dV&RMXrk={5ymx^-mbzW3)hnJPt*Z5pxT<DR!M5GvV zOG_X#p~FQ59f&7<08V+pIR18}m<e~ea{weC221L=pVk3!yZCN&x?fO>Ifmv>@>8w5 zo<Dd8pzF9~H8#2c6Fz|Od>_3tfE5AkJof-j4FuA0pkIY{gPzX=$Z)#znyVM@YnFke z>*%Fhos?t$PHk9VcK-C^O+NEDAIlfc%Toh5xYaQmT~&@WFSc|uzNLv*{=`Up*=_2T zPlQ3t747;qmeMI~Wf$$w)N8H<y8}p8N;A3iUNHsD999Xfk7BuE+?&6HL$(Ml_W?Pc zn_Kghb(2-$4-UyLDV28L0GCNH1Z0q%I&RsHm+YJ(M$g|}fE<q6++Nj1TR7Ohd4;V& zvx0v;(<jVJh{53lB_Q<t<B9p(Wg!ZEP%inL`kY~Q_-?TX9y{Ic^y`y7IDY|_J0LZ< zeey$Y0ZcF1Lvkz*#9v4In`P&p;$*momNibT^D?Vd#9dRtyd<Or3x-=NigRjb%W;nE zTsd){3D2b>uGY5i^nVKsHy0JmA0wDs#6S(6!bC$sEfb&=9@|ll^H^Ey2HP_<dYU49 z#UlfikgRKHK;hTY)GXoAFDxnXKfLt7BnPd{2XeYxBc*MChM9y?xw)*U$n~VEza5~2 zfLMdbrz``cH0e=*L`opfZ~>j0UtDbRe0EA54f=IC7)Nd^U%&`9iMIXKxb*$0;+@Ft z5n%2gfe8upkBt2joZ;zA&WQY3x7jE1Q~K+=tAn(Xr1(v6*lunJb521i3fBsb5Lp7k zR&S7Eru?$f8gu6RWXenS)3!D$_1sm^Q-mhAboXRtgF^m6%9}G^=&|CE(cd1(LlyI# z5)vGh<Hz6L?okS)%b&kf+imSytkb!D6Fs}yjTTEolv1tuK~0l(IcgJ!g+*3G6JV*= zThZ~;ep)8*o>#t#E(sTllgmKAYlOUG*=>shH)z&voHd!Djb}fleCFRzP=JH@0|fDO zsbGn5LHWYH4zF5GPWGvQRsV6Yc{<*E3puuTL)(c%%_+S*l`T(=yN3*$s<tY9)u*B6 zB^<oO{wPtu2fuX>wI2!Rr#ZJ4QS<f=tD$|mmXv%N>w!SzFCwJaH+S>n+gBqSqu~!# z9!@5l<i!v#=xuME)DY(8Eoo_2YlCEs2NU*r9tw~GFO>sLTIGOZ8Y>lV|LtkMqt94^ zw_Y%~`!bmS1w>Zj)qb2;RYRlLA@UGm{i?Q>C0!O`7n_S{t$=;yyRfXRthqUrc$Qy( zB(7$w3!}najmh|0hc|x&I<1CCJ$B&pZ`z#f>;qsaMBH~c>)<Xm#g7a5a0h0}ldjn4 zDsx8}>n!Smk@pn8Z{LzPq?x@H7H{)At9(yXuIKius{?0zBzDx!gzQJiz&|mWjcIO( znw{=`f3B>pDnNG)@m_av8?AC%0l!mr=4ogFs4e9c{rlHbIP8?pfzMd?XPe|-%Ze7O z)eV?phG5`0NIpPDPl)!rxVb%qQe#CKH%b){Q##0riauqF_B^ODF-uD3N#iNl*6H9g zmlJrsxI~T4DQfzc3U9@m-58G!{dVD~w!qmt5P}Sbif8#>q#q1HCwz?dv-cUz``b~D zJJm9NBN<GbwpGFP-JltD`CwOU=O<J*HcDVp`_&<46{65k)1^X#tQ2k{M&1n{Gcz0S zoXc>ok{iL<!KS);)X3OdWiA{TaaJg!*y{H6^?=9oz2fg@RN^e;7oReJ2l3iXZx=Iw zC3F#HSKd911Q6KuOlns2zz?dJ{j$5L(oWFI-IhsypA&VW8pti-nBYgTmuNFG#d457 zv)3{y@i`nI=Jx_-#c<ypj=T)#2f~j>u9UQ!*%#74;zzmGBaqI>U|I-4sYL^7Hrdyb zpSP}-G;?~V|C1b2HSdQO7vb*kwY_%x2Rk2t?<UXd+X!#-5V}$?kHUbef<CJ_Y&Fr? zLpL@l5W@zf19sEDsm{|gstD-yMkk+kw-uwKlhV<|8_>BMhG+0%K2~%`dPZG%S^DgN zM~;oL^^qfvM73u2eFJ)2Zx!o>Xci-AA<yrk`5>V|M9thwgCP0%55`-HN4Budv=46X zSz%Nsc^=!u#BFTYlszm#8}t;YZ!cpT_JtSTaKo(pFNuWAe+?*qg!tgTNa7#v0v|Wq zF)>e_=tBehNJTbJ)mJyWdI}UukVij-=?u>?*b-#Zua%aZIaK)vG*a%!BzsqWf}e<b zFL1XNBubdzAL|YX@w#mckdrH6mH_8pbJ8z_%@TAK6B}_aNRef^lOG;qb&!BY5A*4I z<*@j6j}6X=$}&KB5+EUMwOkLUiGSA#^t$rzaBy%XDrHYsi5(+Z@<xU7Iy&ucw?{T! z*Qr0elx=M2gI(B@$9~f4tvozDh~v3}>nfJUh#+?n3cpHhW+Mt<zMba4F_O`K=D{F% zEX{UEF+F)MmMGEje>ZWiaMm*i_0+%PFEKfDXcZpia?B=m5rjd`k&pb%%gx&t;zy9N z+}_?^+PJaXeUGjAs$LK^BfhgPwdMlG{0*3+I?#JtUss>WmB*M8YcgiJ)POG7PO0nF zYS;agQih*9mWnS8#&!`6Gs&mz;1EDHweKX1JBV7w3j2m7$b*wzxPo3gpEODpQ9q7; z;|$rzE8icZaVQ!={kbd5?+ezy`(fuJ;z3kMI6J0wYV4Aw^Ld%Mi3Ntm99?Sk)u_dF zM?)^dVzIc9MwYkW^Xi9+Z}fqJHl_uFLJQ~$(Ws(3gtsDS`YXc4T&8!}Q<1AwNK#50 zxaKp(3e9&9PiZ>*zJ9l>ZG^HC>!$iS%GZ6HOUKZBSro!<v0EPzJIC$k%e7hjntFSB zI;RPYffVlvo?eUr@ksrC;^ruh?sRVF*gB=6+zjJr_FPj`WenM(!a_(mb#?Vn0$?i1 zFM_1egm^YXuDtKSjM}`mt*GLVenVs9tzszz#4i{I19likGSK_t1P;0ruuX}W>WqMr z1KntXB0lI@-j%3Z;j9jdQpM6I9tuRodRcydC2LT{e|ote1)mCG-dS(FpD=-`HhN*z zFbptDU#yU6(jVn#p@`S}NIb+>csRZxWDt$p10rw8nz)^&gSTh*;^LEAII6m!cx_2V zf7jevv7A2B?{)P}++3}^yb48!3PomR{gHPcLF<o>9wg)3mX}W)9dl{+D2!CUz$|Ug zg-aU<c{q!5(oC+&+l=Di%2AV?_6&H@ihDVDsp|sPoS4kEk-y&}pn(#C)EcZsnP5ep zl9jH~4>T}oY%4v@hoX#bpNF3lfM3Sd+2PZ3t`f6yYQ@Y#7lJOx>#yZ*v%xExwwQIj z{9SO!z;3#h{nGOy0%OwVo&TujiN01qLeDj8*u5klZ>juwG_$g<AA8M*wYe2xY$(Ws zNa%WRlS@At(mN#qEfv{nMPn+~kkPOw#nSqHZ78xIeMbuV2aKl?-!4Zh47yCMfTY+{ z9=&I1DKm#t`@>dPt34#=L{E<hiQb<H!EteyM*)aJ7eXLjfAWt3-HNKNZimyUM#C~( z&hs|@d&mlfG(yz$AJ1+NoQkq?8xRyxDN`%VZnabkjIkO5B&GaEtR29l5qKO9pFY?e z=Fc;`fEIG8#rfRtBaHD_nk0E9lR?oF3%HcVDYh(I_U6r75!qSeh}t!GKX&EGtsB@j zGObIr(EH~-QUR=}S?6t2UcSX4+P(;BLwa`E16Sz#`LV0|z-{EJ>k2N7j<(X-Ukclw zBRfn*Q(sd`)lf|tc<gk~i$r=U5lyB>hkiWQVn%#V=cH|OxYdMfaYCj`^6ZAYZE<2d zTb_QH)Dh7SfeKeD3e)wj+dYV}ZCEiL9VDw!T0EL{T;`WfW~DaT8(AvTOhv$xPUX+U z=!$JvGv})0Kkma;z+~+qR|35Ar@{D1ii$NhjY!-(19V@fWW#CRats{2BBYi=CvA%i zfBvmgwU9wonNGn`dOk>y#Oh=DEggQ!g_M*%xOif?yhJ(%=_84!^$QAlgRIY(7;p;4 z&Sa^vo}i784Ux}g$I6k_n<_~V&axMlczAlUqQi%)MEoiloCHU?eba{s8(<2jQ_S(Z zj89`cA5e9;3X8G|0lT3eWan1=0pHdKGgt(Y&M2lJ?mOgf5!tY3IWvH2<lg;ikX+c4 z3M81r^()vHk$_M4lYonh`^O}`Lp(h=VF-&4=~U{hc^(MZZ$eDJoYrX{S&S66he)dR z+6jTL9Q>dwf@<3d5}MXx)^XN}=+0*#^phLfn9aAJtak{Sd(MQeX$}I4i4t1AV)|jz zN>${2O6B3dTnQ+auomD#V732<@KHZp!)@>2ai8t0_~rYKxJgyCFvh%m7mR;46Ihj> zbZArz7Mzo)k()~zGLdOW<R-CtNY&>pL8;_(i;i{tp?7I$eT}P_m+qlZ;s?I8T9nw} z@8I<EYOu5c3^a89(n5a^x8a~pG_rt1E8N62#?%dV_<7~}#dkd&9kEhyL>;bJbRLxD zjbZu@X(H$W9U&oxyAws-gZ`erzKk#0dx~-~QUH3`$O5n{I#|{Q$EBM{U2g*+KE1iv zu#5Pbkb~xb=@N`y$P1tK)5|6v-)j?6zISLwI5=j;*T-kfqMRfmC?*jqu*|8Llx_@6 zbZi0;-$pn1gg8uMi$o)6(9O`!?~t2|v4WdT$jP%3#1l~{A+qG&;^I$S0}uaMIy|5c zi%lznv%!qdUD|MM(~K=I)J@-ccwk^)Dt1DcegbU}fcXa?dO5-NOCfCK^Q)BCmj2%? z%(oE&hO3xJgHPt_h5vL-|I6F983@ob7tMG~*#Bas|3i(GV*tcX1;z|h^#8+^L{#>@ zzkd4ugEIM+K!YF$#<9E<YsAus{r!9t!ahiX&{>@U<PjV@=a)Y}`(r*t1SbkW97_C8 zfx;jNIP#LTAd~+~<M<FTs2~#H#;BCwQkNt+{`L8NPaq;-3{X7`#0@oi1ofQ79TU*) z7(T8zKg#=_Y&?lSxqf)n><Or&C(bUk@)dG5|3A07Q7kT?C06nD#lsa{ZGP9y%=;E7 zGryJw(IuWQrW^4$8rfL|+piAxkyp^O9MYW(t?1ix^Ss%HDh!Eew|DkU%hc3tGwX-X z@N`s|ODfnImzo_gdH8xQX~kipPp)^g6sJ!H<KedL0_L4#=TnBhUi1~u2B`v@m4=3f zva$nTw9<jqMfJBajf&Bq&c{}akB>uG06nv~T;k5nrDX1ys}bjZJ*r?<lvko^uhO?= z5oa=n(<UFVI`aHn!e(X3@5)qONnvzHBLFZsL_|chZrYn|_hzGe0B)wN0jRRsMT(W` zQ&NiW*Y~SL$EuAOUAM<HzO3F^fmB2ZpN&PvCQ!e&S=R|>wmFzt3T~>?c3&7sN)Ec8 zx0y67C(`Tk0q<ulF}hS|ajDP*<nRH?>F)m<Uk7#teL^07KD!%G$TZ6nO0%lO+6B{s zAK?>)E@7Ra5;;E{fZ=;>?d@-Z+=O@CXCO6rv_&`fw+8DqO`iSO&CV*di@aWWxTCVH zw+pK$_5KyYj@p2BJx|&oPp3p>RX!;V$zmjgl|D0iNWra}KoTs9{}&6M<9T{-FWPyj zOe!}&6>-k^@hx{cZ$d&eq6B>fU<9Zov+tTID$w=pou1Aql`Wemm&uH2H?{4GFG?ph zAs_*Ck^cNjm2TH!?%Jz`V1$<BT6M09%T)%8Uw)dxy49&9vYA*vb&1ZMl%8E(Nz^SJ zf3L;mpR0W*-eFY}y597MiX#`kkXm;eo<GKFrVtnIE<)EaeKN&QxC;sbXgqcHwInW^ zc0xWq)0zt!6soz22?pTA1Z>i;kK^9fz=)Kyvo}D8%woTR1{pr5NzXI+Q~iGwEUK`8 z|5b;vu%gR%<6Hl63x0aK;*_~A3mqdU9n*y$YW)Ha{aJ~1^*!K(cXe=bs-V9BESyWS z1?m;9`g(ZF#?s!CVzeUV`Q3~9&i8I$ax_>F5xEFzq-?@1<O5Je@(`THJ$=)ofJQhg z%vnw*Ga0QVuhB@w$x_6-Cx6RWI>rX4I4?LqV@*OC{8M-B!7oQ!)HV+~)xo0%=|M_` zq9UqvS$uSaqOp$cF@SB`7<~2hDJ!?0b3SM9>H9kEaUlQ{WWZyeCY4Q>$}+w-rU~AQ zM-{hMyo>k(Fp_%yR^)O(pDB(6smHDCtg70(#M%TLvmv2%hK5*M+mCwntGh;=!-?3G z_xFm0>s7*p0guirc~jutbt>h$K!QVEpWrhaPlv-_qG`Vn!Ok&rx~M@68wKSM#2UYT z7(qrl@9Q7cZfiq*y*~L4bu%y~S~8A|R;{wr6vxO}%T(d(|Li*8z|$Ub-k3t7u&y$j ziTTn`KJU~NviqEbAT&*+R2(9{Z}@%^Srlx}^+ogaq5+6h89z(oqUANVkSt-JaBg0# zr{LdQo=00zi}%nL+?xLku}Glk3!=}dW#9Vt*rxo#;?NYX!I~8+j@|s$kxK4mBRTnO zk^)I^;^4N@-mc^QE3bc49gwji#k~FzWBREBMb7C|B8!m8ohwg$adF{|gNc1jOAeSH z5=#x}Rwqo<1VAlpvFiX<Qr?AMB^C!P?!eSBxpe6iMldf^$>Bncj&{Ie3J4lHj%_Rz zh2>(dL<u)6Eny#CHSFVas*ozQceRGnjf|4#weep#cJwbL_59O&O{7C5gANY9`TME@ zB~D*Bb|RB~e)f<=FjicV@i7F%`*}q(H8r0#+T45f1dxpDxzD|x%<JxCXJ_Z?>S|?m z`p20HAeFH~YI53S{}W;Zq-6*I)cO7gvEpFg&a)qPL-70fPVvh1oU&}UBLm^evVLQ^ z8bAw|u9G7b!KM#ZdT#f75fVLM0F6wYARqv8k6g_Z0j_7<p_2uJ2ixZBSTh~JWGEGl zb(Lc1VU+A!Z3}2RR#Zonw%J51$HiCZPZ7lgjL@o6s3lcd?lXv4LE0_F>}~kzsfhqg z<rP&>&^%<)fbtfMzO`}1r(?Lud%qQfap6V~k<wAuw}6e^3g5y>nOy&X&z33!64f6Y zW2;iraVXEH1qs4Xx>DJGyTJH>9i#&5>2d*Twh{m}RP+#{QBzH@nYk2)e4Si#LRR<t z7mJU;mUD{Zsw1s<WMhiv$?Yvb@{5_6T%YnkCi1J=Eb_c|q~lsW6!At`lCrM>=xjS6 zB|l$jLSYMGw*UHd3eaR$8*SVI((<U28$*YG&#p%BKeY*{sht!eNGm(_5^mJ!sAL_M zJxcbSvLi)+{N;rR_9r*78kJyf-nki5d-OcD4i0t*yneFk=IKB*1t@wPiWj)t=<XX+ zgE^n*aD@W+8*#O8jWEGyz+$Kt7g^Kl`_`e#(EEI2<Aw=`|8;-#ONOgVu9VD!F3=KJ zZihzkenPwC#91n&C>sQ4R%)cjuSuj69?0FD$ZGWK;_JvXHchUSa<?jrfvg2FE@i}E zvT2MNirhlBw;M9;B#_<@K`;T#!TW0-Fo^<r3IOA)VP9?A-zx-pg>za=A(zBK$>JWp zPek=Hv2XXe!h_dq%20Hk+@-2jXTY#;t2YLVHX|FPBMPgctRE3w9AR%p3AQ7vysKUy z{+p(z=27QyHv&J8=aU04!Yb%@ZEbBR3VFG12cM-09m@a0tF8$Ehb7O(K-M*(4~BH9 zIy*7(XMa589Kfg06&V}^>y_&kfrT&y=nwLU*!j$bs#(W!UtjO83fMD(=2sa>aesnk zD!Gk^GFR}feSp$pB#wg=rpj_6blShY2Xeo<L|{g}s`8RHIzuVv9$ldV=(}KrorIRC zZ(^S&n)XvYU2wV^<ZbB<xdQK3oOc%2&%O81%_GR(&syBa(|!xhYAbkcB#wuBALw<> zYDD5eHS$V?_kI4XPg@G)DviCr=%yW*TTFYNBrdq!n65^i_7rcHCNUhMG$Z2;;~Ldq zn7^jp1d7YGJ=r~n#$9wvM)7?j?Ac0WvjO?C1tPxt<cxiHQa{gr5s2gboL->o+N5`$ zaq|&IfpoOpHgwLic&X8>uc1Tx`}}Hcf9-*#c@IRR$7xFW6;qw;`Po^@!-2tO6nsW` zMaNt7^kKkA^XeH8l$|oV_a}M6g#Q_{IS1e!bRXyijX9bT1&R})#Dlt~#ph3q>i<@` z;ehOkSLgj8xoqU-TKq$3NV#?D@-cg=jHMO7;|Dgs9_T&||GUBo3xTN`dPZ}3Sg<I0 z5&cRC<STTG8Apz`??PWc%F2eRnCjGwMu<-4Dx;IAZ$#5N$!XMr;6cW4&l(Ej#$O19 z|6pil3nuR?WHt4-j?1khc!=f$=o-v;a}N*<<7#F82{Gr^D)-SzHzAamysU`XU!nu3 z-ZLb_zcO`9>vOU<%&mGYP_tr^{6LAI6moku5!ojfUO+pRR~40^5Ppv%!XF+U+10c3 zM<6uQ3qckM+5}Y>6tt9-hDS+gQQdVbR9z@AN@_Z?rU2-JnadCTsNQ`P#-v}m*52N7 zGH^eId($jN$DHbG!FkSn`j^+&O(grB=Qk{^3EkwOphX1+rdM$jeg<jKWch0?CZ%bG z2jqYT{QUXz#}DYtady2f@HEk6X0~o%DkFf80pw)V57O{a$>!BHy3{W=6Pd?}FCX<b zv-|&WR(iZ5d*gyp%i?M-t?KwTZw?JdHDWvL{NC_F;|~vtwU5%*ds}-Iv{;Fm7_fAW z8!!qM-oK(c(9ta^;rNJwuwY&uG%0E6)*UZ5cXwQPaV4dhf+K~?nZaE<^)i)LpQX>- z(PLw>>9XQ*#8;F4sX-~?PX4;I?P(Nv*OU5Hm3d7&>Jc$0-03<QRXxc;jt20(xr1{p zSMsaQ))Y!qP*~>nZWE8#OM6<oV65rNxMOJZ0Yww(9S!LFxDkt`tXQ_aTQ)2$^zCv( zb|E04Gp09Sy8Pw?52DzfmM7#Lm7kWX;5rE<Y+tfKaUl%*ZYLSD_TQvzh_B8Znyh5m z*=Jnez#$27V!oh=`Rgzd;#{&;?hCm070&YT@jLgB6QlgC2?#8a8e;<x&F^Pg$iIVC zVX#<&`~hxjDM)*6#y(C0;AV6bJn!&!)7tnt|7}n93roqR-#(RRIp^BM%5)fCvKs36 z$E$s1xueOHl$1XSxKmsr5D^Zn+S=NPLZDI8)9*nF(aU>)Z-_b#rR#B*-aGj|fm#*F zR+P(`oJfL@15$OoU$*g?nV8xEw~_#it=zAM=4P%1g3Zy!THU46zA^QkNR<+$sF9)O z1ybUPi<|td_3}nBPNennKD>GM&*-oPnPsH3;zunV9l53)5f$H40b$fMea8a+K@?`9 zs>3^2fsWhY;!4l|q3$c&;@A>(6I_D3y9IZ54-UZ{g1b8ecXv+$fdqGVcXxMpmj-TS zX74$(&-n#6PkkV5RbAb^YOPnDLSc~v@%7o-eCNwDMl<7@2XuX9mOzgWw^<&$80fud zg>sB7_iGcMfwAKE;*EgW?n(`x@uZP=n7qK-{08{C!!{)!Xh1PEZ-Pd>3Xn8%l@$7@ zJ5B*>R3=%70cnl2FWHF%QC$hC|6WQ0a=fn&NcxG4BZGvwMyuMhuXmH4`T1U1ZEgyv zGH1<JmY$|1u(eFu<@d7H(Zm7ERE3o)IxaP@in{y2EO_v9&qo1jTi{Grm*(G|9UkU4 zt*R}3+R_}OAoLfj_3?RW{<v(HnUsRTnKm7);(j&S@Q^@k!%t6!8JmpsN$UV1G-=4y zEVNscs~ZT$+fEErD*&vW&{jNDg}rpS_J&osh`&N+0ex-&yMzc^=wwnra_igc^Fth( zY1ACL4q~~#$#4pd9ylJSEuZ7gzz^|AOa>k7s$YB$8-b0DjdpF1lq*+&uX=A{mZm3m zU<XUz8|Ct2RnFOBH8jDtN};?k`rG*1qo9L>L%sloC%1bm8L6J<;m@F;pmU#duLX=+ zt<#CJe9&(gh;EyZhCm1Pgt<tn6PWh#p$I3Bmr%=Qp*wRQtSjGXmVkS*zK&Q+9%Ki_ znT=#y=EG~z^cJJ@9P^Sx=O5gH{A6|AhZZn!?@1|88r(eh06P;!DF@U{ksu~GXvQL1 zXLqpumEwF(dwP%Y!rATivpTf*8l>P8^I<@|H4`QIoOgxC-P;3I6#|jpS*-MQL2{{9 zvfuow^mO6*>m<@-RyS2xxKoYkZ~={ix13oF&&~2%=!EDgG*LdIPMpUoLa5*U@?9cT zLU}s3<1yaZfRQ#40G2VVARE+IT6?qj-FDj;u@L=hUIR!bbXx^DG8EfJxL6%l%Lz;T zpT85l+%dBiwgNoTT()`7Tdu|purxfJ1hU9=<ILqMKTy`lhI62T#ig1vSk<<BG}sF3 zf%)2gDTkbGRVDCS)K12-qYQhYi@U@GfD*+|96t<jm!r)x!YIG(HR@uZEB`>SBeJS! zehEEpikWrO*H3dUs@w-CNX_hl{!QX{wPbM*r}gV05L2%Jd)~VhdU%fH_YgR0+O2Wl z1<$C6v`)*wpx5Ml2*j&7UF}H!G<(3aj$_#uc}p6a;C-|<2=oqeb*yP?BRqHJqM{V0 zrY>RR!HqB1q3nQ{hhg2B0Fqs}&>cw;ZYhJ|Nxzn}_kkK4FNx6W=Ukc^w3VFg&K$^2 z>aPIL=CPpieC?CQOF$Nch$jXr;Srh@pKR<0!#E+^DF9}^S_sw#ngDmhJ_#XKk`n$p z(@09io(a4_`Y0JBj}$6$yC;YJqG-ja+geoLI-QZY?ZhJmEq)LRXi4hF1#*eZOj>7u z)M~-b9-bbA(ZVfp=drLb(SMX7ex4V?D;A){MS+%-VZG!{x%uW-!cCD})@Aq;D=QGW z3togA;cu7+NH)`eF@Q9f2P|Vp=dCVmzr^@?3%^g#n!2H}#gMzWghqly!;~1u6k6(t zQ6j}WGWp5olB}5ls3h|XLDahug|LX&DcwYjP>X=*y_S`SP*YP3&Se{0L5>J%i^*ZC z0@Nr0XAzR)K%?3X#W>~;o~Nhy(mqu8<9xQl{#c420!!tjwfxx&KBYlxwi+TDePsXl zI)SAjnAA?IBz6FHaSjCI0u6Kieiv+fA_!1{gI`2a-Ob0D_+|Y7)9CWVkYXd?1O*HC zK=urGPiDdNM#B8Gc#agI&qRtI|4u~0!qW0--tR?y4DOaV*pT#VZw{008G3WS_VjLk zoH;vPZki)NUgqN%gzye8r(jZy4@b7QF`-O^x(-LLSh~%-G*_%S<&F)-EUQkA5lGHe zKAV2op%6QzwrjSN*gH9CNKdbfxB^W~OBUf@JPxcyaS>^~1JzZsHNpO!1;EnNtIb*^ zHD@9(p4qyNj5pul#o6Q&^n|7z#?#TkM>eZP5Btmf8xD+(xYo)!tS_F2;b-yz#YO^T zc$leyulnrB4yAL)ij?XZ!H><zT}<slEc}13D-33czgFYm<(1P9z4af%(Rr4tp@qZ# zuAZN_yf?sW@zV12lvV7Iz2s*-oM|(_CD9YU7B!FW%y@4UzNRtr?sA%RutbeSp;YPj z)@be)wg}IUjR|nr=%g4a$Kn>L_3rl^<)0s(pOBf4&u**YN5eVwQ~eHysJ}19)PiL& z{6e~b)2k;w%2y#+ku@6HCi@Tvwu5k)784V*w`Zy&aY=*lUVPWi?|B)T(0!xioygDk zhC$;3&nG}H)wfib!HG1_FrrqPCYGVG%<q>dy(8}Z1a1?v`O=AiUYQVA1g=}mKvMG< zfb1$7%mX>hdm^Jgm^MdzSc~?ojd%{mljMvOr@Q$@F~d3vbKe`Ew4C@UXVL(c98#ks zo=!GvSwUS2)$!nb9KpqL7yiZn9S?I^3Z}AmC%6xv`Oz8a`?S6<MeCK_Sy_B<zlktQ zHIyFLp!Oshi1RBMk%f@QlCpwCWbNEIZ_S@^jGz+jwrQ63@4iusj)MqA?IcP$AF?{L z7WA6Q$mN}O)jgHS;7D4i8}cjv2Yt;#dM~(AB50%~QL{|6;9U^hBBs{bXE{MP5SfcH zLdh!yb{G(HBDP3ZRo&D7!SG12-CoeuNM+jCO}(yr{qA!enKFTu(Kzn6H$_(OS$mo7 zm(MD0SxUf<Ef0WAhg>DaixYFwVewRUArLDSDBMtR5`dUIdi>NlT>gW6+#^M}eUWdq zwYSlnYaKVx=OWFJc|<>ggYF3UJ!>i}<EtBvCOCe+vk+q~`1b89?gyW0KqnxQ>#-po zw+Tp_MKM)+B9x$2jZiMRRAaW>K7AwpEys;@ZNSkqDsKjKV>YoQ8Giljh)McWgP7Z7 zD2WE3kM^9|;w~vwdE)P{i8}~F)u{v)es!WThsE>pj*Uc1|8&d^stN$Q8_p&o)-@;R zc5k*eqT}p-nGNK0qjl7+uVXnh)WJvJ1F$ffaw(tp=IfRi;nPlErXZcouA)U|cBROE zjP?6%JpfkiUv)5BdAI}`C{Q*+?Wxn7GLTLr30GEE1NEqbu=f>wWTQwZ3r=?i%chhE z2w_`|`gZ5fSHev{EC!-cDx5Je+Hrd?Bqar%nVVTiKOd@u9AH~X6j1PIqpQReP<r|3 zI?JC8l)0tl73X>$g?3wi94-3<@xXfm^o-Rh6h0@Jl?^34(w8fX<KCJ<Tk9l$9r*FD zakXIx`QlmH=;-;`y>yci&C!(YU<N!S;5rxbw&>ImgF<_2#PbR@+xkQ^4J#MbhSVJS zjXAY2R;o)rm6Lhs9S)f~{qiSAluIhp({Ly@V|&&-zq^tHWuUp0UbYTErRE6jzFa@; zdNfW-5Tx*<UoUojoE8FLOrLu|h9m8E(=kF<Egu*$(oBULW<c)k0Jx_?Xu~@}=*9AR zW#_J)P)Zrxt23gxR2iNN6`zQo9vRIKs0lNn5%JwmYv&bTy?5dz4FWa5;@_EI0r@Z- zHn@-jgb|GB0}9}<S<J>z5iC}7C7t>;0G}KoWe2Ed0#}7Mfy7Z|HMNkt5!&*WfI~*W zk(M_x41`XH`EuB;MQI;`9<K*2uaAn$m9J;ZeK9a2YDNH&8%Yo#l`{wf6w*Wj-f7t4 zTsv30!-B)9d^J<W%@b!g-k(?f?e@p6)JVOQ3`Dk5pV_#VfSH4I%>iCB>|3O8IQse7 zN)Yu8E^^wU@>17JE~ar-JhwS!_eb|1-!rGF3N$z{>WOFAm??0hsR@tm?;Y|Cxktj7 zp`eS*_v?k_v%<@|khQ0rXU|uZns32N2a~E#3IMmok((_Q&F9URdu>+MH@~|vaHg2z zvA3nRqgHK(1g}`DlLh(m`~)zauW><vc}_`zyfCL+4R4(UZb{&%pF}*R93oV*7o<-b z?#qmB*M=>hkKQz+tEqT}QwSFIq}Ri%e4qD5X>%9TJ=X7^TRF#Wq5$-pmzGw8E=m2< z1#79twVWP1-(%Bp+Q?FalceP0<jM7z==+pXkIK^ll$0##fl;}E@$&U5NG~$@@f`+D z$1&~Gd+3(ejt%utJzj?~g=y}xcR7TG_k>+eI(8V0V5G!gq)cpJX4kc7qQ&vb;T_Ci zX%L#WN$H_<RAkg-G4H^I3{xkR>I^^jS{S0+c%Jr9^Q0?HBvl~AaiAc-M?v<Fmk<I% ztX=#16n&VfuFl|occOR>NhVuIWCc$Cgd!Wo<+EQQ)eWSYU1OH*=xMRZ9jBG%3h@KC zY=Q~v2qPC-hw}SA$rU9?su23o2kbqc$fA7Mj5_eb)v`hX7j)o6-+ON>jj*pFCCpD% zZT{>Ndl<q!vSApFo|Y$S7wvmp<X4z5A!pQyk9m_GdY@_kby&cW{k?H*Ly*Iaa~WX( zZ}p$sq_j`~6VG_0CB^@~$1LkB#3qhp37D7=#6LD-9#|=4iG2^H(tq3xJZd8f4LAZb z{7SgL_Ux}DO^gor`0}c>$j1KZ&&x>Qzi(b0YvxzTTS5&gd@mOo1|0dnww{UW3a#1r z2}a>RkNXkOarj%fsO;JObC#g0$X`bQJe!B8<)>?psx}7Zzup|Sl#mk)^pxTQI*dEG zaI7FY_qTa0YZs6E8CQq^pLi+A|8<N%(!bILO?tcjXtISqZg!om@iRIU%Usl1-}Nw; z>7|E<L*#TFAC+0upSP&bC^V&zZ~^~O>geNRVx{+MxrLj9-{y|$oQXF}QeS6USEc&F zO24q6?W{~RR$xj+JG0$rw`aIKY^JsT_hE-V;f0HvLfdA@2_!sk3?cv3j(#BFeM53_ zpT0BmnNEBXoHsJq``G;n<Jg+iq<l=doZCTOKAY2C1%3;KPZ=K1r#?PD$>ptT^@jwK zpRRWBY-Mw}2G3oI+&6sHb#-5dRM8mQ`IO7*i?0tdu)H#Hv3z%n#h!G_whD7B8R?~Q z1%3PaU&k<sh@^39-=_}O0vaUX-=EZL+0R^_HwQrAF%Tb8?frOlT-JP6!>{_IuC*xb zua6@N608b2Cb-&LVks>XKa%pqF*FrzL1_{5ykd`>TOHHH*#lILU*xwfWo+TZ+03*0 zT8dGc-(!pY;kLHP+lAolG}$J4mGCa)$q13d*@ni><^;}<U|9RKKu)gNjq!0$Q10<! z)HJh{5xIifnD^5sqo*#+`3QBr$_4z$_Sey;(ad-X6h&wvPbRm)DUaM2>qDpU+d8)9 zWv#+h$Kw;;bK8~5*5W3f1X+J1sf_{s+>L(E&U@duc5iOY+sXt9>IGF*X|99IATWP% z{b$dytzO9-_kgr1Gj(1S<rL6#ebKsSb@7h?Y~y42kP>-GFX_t5TEqUkoTVd^^;YQA z=Arrdw#<)3Z$B4j%7IV<WEgD!W!vUc?e(WQfIbAGe%NZl|Nim<S*55!WM@g@GQshF zu<};YDo7Yhdb6&ZNh3oX^Ju)qy!@|Ah;AzNg{|}Jdsi<O)00zsIaqh|WsJ5*cTawe zW$w3YTC$P%w$xVVx2?KMpq;X#Ck8i7C$3!P=ElrSB*D9|Ssd+lkHqHRgDG(bQqnXU zwL?eFq|na+opn<QD<;EC(m&<0muk#X-@~CL7Pj&`0mFZbcjRi!3zOUAR#I12L)3>z zR?NJuv|a8Ng#Iw~$0J(P=GLQVP9s^<>RH^}L?BA-@xT?Xs;>`I&ncRpnc1R?+|7!X zdGz_tvC0}2)?&J#7M~CKcN8mot-<cc$Zo@)9@8&B%3DrP)k4AF<49wU`uFto07?Wt z8XDMU>f4r`1epT)YydRE#lo7Oo5LJsS6}_lFY*=hJ#1IIxWpwDRfW0tQCrg(Vts7e zW>3w70d*e{%q}Ij+5XQG>LD#Y5*u}w=2fFMhqZ>tf-k2fw#S!o`F68ABh9H8sQR;W z%WKEA+OqQ_W4bKNFFU)@P3T;tKMw34#Izo~Ru5UtxTdGFe_Ke1igtcO`F2fU-yDzc zUE9|izZNEm3>a?E;q(clrQ;vw0-;;8324L<qrW`y5rFylgscHMu_4|9R^B2*`O;%I z&f9cxBp+XbtZDT;ne7%?NCOGW$(Y06<+kN)OYo5HC$)w@KB}3m?B%}Zmo{*jT_&J8 zMlO{vKq0VW|K5xzlY~)7XQ4pN`}=DX)&WCGEX)-kuQsFVG8l9-R7bcXOjXdo&GP9b ztvZD3L+i3(k5)r_+x7QS4DF9v{XcuY>ug!4Bylw_T9dX?A-DM2`U+cu?)4YmJRL-5 z*w07Qe>wEWWVeIP6AE)hyf#7>THN%tFHz`}Li!?;i1Zd#H({E(^=gI7$1(A&PP#ZS zt~?MH1X71F@g%(!d~^?;iX+Qf36G7)p&{jyg(l9+fr*c9`>X106DRS>p}7^e{a2a{ zKdS=$(>{c=xs-5q9cf|H%w&ro2;@Y+W4buAUaRr~`Sg~n{<jeE`Y*Svg<9Rz((5ZL zpb1f=RqyLjY^1#WPembr4KraKG$<24SPFw=!ehzek`G40-1w@S_qwn9EAz_znlf>2 zjW{+5KFR7iTFWb+6sE{^5f02_rl#UZ4TzIry_$q<o<>r!Ih*+HH@0FajB+I4c=dOg zy+UGLnAJEBA#m2FCXcneY`2b%hBXDPv{XovY9YtlRFY$&Z_s40H2!L>X*gIq2J!Qk zg@o+<AO#_%4~8&L%ui>tStu)rzN`s?46mYz#xgkV=-%-;+4MlV4MlTanW4wmJ9}80 z?#*NhtSD&>Np})PZkvnM&$qm&orWM>w_k{B>u4kv%I8eAm~ldN+>wQvt-Lx!1vNlW z$)yQ-Zf7Ng%=%s+$mepyk545Hs^Gevayi-HtYz77;I&H&3}UiB6zjiO_C{btVlfh~ zDJNzA=X`0B88q(aoM+c|eO5$A{bsj+c>xz5w@P@n*?Jzr4xXu3(*3&Ds+;o;4aQDa z@oR5+?R+TKK?1CWvn$cM(&EHvtD~u{rp$QWa<PUw<(REFzY$Z<ceX)`ZAd_#Z$!JK zgJk`CL2Z)5PG`si^0(n7s~TrYhV@Eg^-ZIhHWI-|25Y^=_}OavGksCH!@Rs&-AI}M zcp$1wEV)7!*Xx#yrf<PX@P7Ii?9^nJhs*wB4%-2J-}grWwF(+%Q?rVGH@#SfH06_< zPwf7!pr2T@^JT#{PbG_z^DT;3Bh#zNstX@bBD~IDeC(WLrq6UsNYOHZM7`Ils{Pzz zt$PVaL&HvStXd*&<)^M|51H|Dk7GIICR{vjA7FT*lFJgxxz(<BPD)jHzMywVPw!S; zW=$Ojh9uDP`fG}~iR<|qQ0MhBCBxYI3fPrTPk}YJHPFE50R&0zMW~Gt1qpqvhwykI z+CjJ)af^0YqJI(fBFd)}734rZGCpO_?6p3<k05&T|3m{uYEKEnlyMFl)Ic6R^3sV+ z3#m0CdQ|7Y{T%3{0Y4_zo3t+t`|$F<T#)I3tYhak7%Os`!xYw_#{B%?>H+NX(jnw} z(@SXrqe4sexAb{6oq$@KfxF!UlyKT}u!EaHC$?<xU}aqe&cwE{jY!wY)}4RdP6{$& znr#00C1gNn+YHw;HfCUd94r0k@~TNKZyNPLS29U^?k!b#&xbGvnuFUAGgL|n6jKD{ zJt8BWtpOoByN-QUHHuASc_VjEJlr)?_~pDB1T_C|<DrI_d)P>@)f3d)SRHi>)_o&F z#%d&9+*a@9Q-*s#vZSGLC+GLOKM3s4Y#Z(s($m1EZWtErrH}TFt56_s6m;_Y&&xwR zQz>8J>=^*|Efy^YQyg)VGtk6RvnPQ(wAs<k$ARApGBNeb@@J}Iuu-TS&K~Y`Krrnd zRz6oLm4_3*OP6*KEM!ahz%N3<-;#BzEVd7g$%&CHU7StA90`}Q>6t`&>40~|w#?kb zotZ+-tz9@RdyJv%2}n;h`|<@H6VuV#%xoVB#!I2s#uThk-%CnPo}HQ5+1+hD_ud8w zrKP1MhwVNL42*d0`*c7DeC3oa=qJF5*)}&ne}8vZHGeRp{Pg<rEY0L~canrsMLmBg zBnaN0#O8K=k>Oo6eAG%&ZNKr>ruy_|%eUuuGv)882GL@r`Ot4a6`j2ALBF`KE`B%# zMMp5dJdwu4kc%bBjJ&R-CZSJjyeFHp=iHicf7VG$>2#;VT3^*BkYZsl*%r0v6eW;0 z>6&v+|9n3QJ)Fin?*6SF3#(Z>1Ay0!(;o7@$DF~`%L|8hH<fXBQ0!0kyb6kcfFx<( zf6n?2WkI|-pn(F01Zgf-(6cnXbqR$K`{B~!19C9<%VA<R#~ooLL1|jZ9D^<YSkmVi z;=xO)FA>B?0Sevh%qy$%(#&0y#q5q$q&{DwKcRnffQeTQ6wMQu`TW(T;OFJwd&2>F zDzhMkF<kv_=E};=L=)J1pKkcjf}g>R;jbzd4GC@I+%7!KOfl%}QrZ{jKy%)1>VTPF zvr@2YR|(4}a@d;icUota1gfK}Fe29(;4XfP$|&iS7xf!V0uL)|OHE@JBihE-4}s7} zJa4y4+*GQ8`TDK(dkUFDttFa$GWuGcueU$JJ!2=CW4fP^L<JQDU)Y>NG!desfU1sK zz)H3+s-uV9dJin1x4r;b+^;CA9wM#wJnlda%;GZ;+i|vla+kXgSqLy3fB<0_9Eq<3 zn2V@Iybdb(POY{V;96d|wLcRCAjgMC@sV))5M*^|{~W~|)`C*FZu2^<@c`c}9$9#b z_I<m*T|KyeOBE*4f5UisZ63F<-o+~?@&Hw?rKA+-HhbdXAqhMXXCQ|XQw9=WN96hz zdAzlS*eu8JU1HCc02&rhaw-ok?LBByz!-hu*YjpSk<~(7t;hM=lvxgCo5OtB)PC-1 zQQ6{KDzpagMOK~Nui#wo8oOWIG}-^T0HjfMJgkeWRKu~NPHmkiocs!aX2dkA^C+D6 zJNAHdx>Dh|K3lX!icf<$*Whtbcj+I7KE9uGU8yqwC4_I9r05Gwbbtb$Ig|aYCm*7B zOYvkfs}u2MxyGgJ+jQqvjuEVXTetd5_WT&BxMZ3%?1l|qG2J7-99(e;F*@)OXf5{i z)1AM`>dD;0!nZm6ZSJDWNrfa>zkffktC$v_{mZ0Y{xmfr*dFw7iG8MMn|6h4(c&NS zU1kTzGI}<oa<8%t-BdJxdnlO}#f}kBa0BA%2qhGx>t(z4?3)KH>$MgSAiH;yWYevF zWMriItcjMJd-2e@?RHKZ`-Q`1DQIcu7_0q39BylsV_wgT{t8&w0)qcE3c=^a@vCI5 z*39Tv0@U{)4l?6={E3xqA4fA6-NOBVt{Ym@9l#HdbvMgxokE~`+9H1vfy3=@us-co zbU)*qPsR0io`paiVU<4IQaA0hxl>YXy2s(%+?Z0lwBlNPXdFq&SJBu~)jDs=VTgxq zuUG@2IaPkMXlCXM+HR?hw0Dz>rkLZt-OEhx>IM<G$;b@*zhHU4g+5%JfOg;9d3mkY zbOf)_a%E{gF5PVVnFL?l;INv*CX`>BFM`JXCUfE%9)VE8vSOR1#yo@B;1Z&?{WUM( zn#;0gfKPApigz0qd|~%hspdc5h2Ll$xD};U(e0cOcvx~$$G4EVOS(S2B`eLTSY_Ob z>l23pit7#)ZP#^>kh1!c_*YPQT~Y3sRGETj3-H_?D3d`o4_5m-n}|(~ExUNB3XDTi zpwnfO?`>(_2&;?))<OvKLtGq@ordh!O?i2v{H<;8;Pn;8WWT{5!w1=uzNFHcDL?T_ zW?m;6uYa^5O*s6ZJ-mEj$bD;i7>2#0FDxwV;laK1Aw4usG}fba4QTXx1v(YQ3M;=q zF`aE1%d&v@9EjfG7b63?PGw~=;Lt_Fq)KkTguwv97>!DPhq^^bDX@Wr81gZKN<>Qw z*C>I_x<`@0GSOn>As#pWiH{#_VV^NO=OsC^@TBOF+7_<8?V&gDHmp9D{-gVgl(BZX zosabN+R~Vv4v&wgYZvUCuTxaGOxpaXScS;9J(FpV>YIWH%yPk-hr6RJa%js7h4UmY z(04mMp6pvlXe}&-@*=Y>YSU*g-X-1JTZ&@!`<dCz%|o}_lg4|uXkNE3W7++D1TE`N zKIPUiGoLY0gxDxC&=nJdK%g_X(&?N#4lm^@41JPNf?U}Ys4vl%3pW*o)bc`Sb#tsr z*M{RN0rm^=A4UJdY5-j4$&$x2cSY4Wf_c?ZPgpm!xvsml^~Y^#!rl|2TRrDjSQI>6 zVat`1l`nka+xT-Dne1epDCweUCpA3QdnIDfMORb57d3Dh(^F=5FUj_^)UK<f4;P@; zv;B~B;>YG)>(7%#qo<t`Dt#wx<m@%Hb=um`%50A=XKI?$)0pUaVY?yEf#eDXlaf+% zew}vlEb+>R%~?%l<%Hba*f?WXeKj>q-%Ku#rd8W5pqu%~u@R9-z&Z#)*!=Um#)XLq z`(6z77CWCc?`t`TO;vT>;I1fhLALLP;P~dSVi0#XHwHh&QmdwBpZ5v|NKiMncs{QO zt5%He+MHZ6O_K;x*dFHB&}fFASRP(ph#VH1?G^Mbo9$ENe%qnPRN<F_4-uh_V42z` zC@%PtkXHU-PKS>@67Cz5V2@!XM**Eg*%^u2$q0q$41q65iO$z0lS2uP)j`Dn+n}X% z23KWa9S;L7uY-=Tf^KPfF>6awpy7~dQlu*Qh3Br-TZb4|+j*J`4<EDLh9`H(Y}0xc zt~`fG)B<Xd*%?(~miWJ+FoO{kA!t2b-nVPoNT$W_)7B+umK>AxI_%unc&T#zH=#k> zNDMDq(I&WEnY{<@x6^Rv@a=ijP{+B;MC2nu6g<Wu#du}rFVC?)OjeT@Z{D|x-H&L~ z-H|w2Bkm2Szbt$AZRGXfj?EL%0}UOVkf5Y_%E6J;-47tR<PHxHW%1U48Vw*yPcd-e zQsayJdYcbG%3lK1khcKQ9MGq+w|N~NY!9^F#*b(!{ZVBc6S%GTm&OkGgT<E1vWPAD z?6KyOIDLo(tWgqRd(@;{(n58hs0Z?>R+Rl&K=P#Lz8P$;1enq+@p%0G8S<>|=C6j# zqk<l3D%0i$gQ`b%rNfx9UZta}Ywtb7&B`4NaUm~*X|rd+xCa`t8b3dNVoRkbg<1bB zp%;}JSNXKz0FBgIW=2eL{pN&8Pw00^Q38=uZBH`&yzNxwgvBU~!*Q~JlA5mSJ{nrE z`h>DWdWx`~DN;g;b>-sHYhq>$4tP#}dS+%w4cflqhfBW_s=NEt3caJ=+=aqN)K}*m zY#|_s*)fhmGW$l;o&IwV8IekE_isqkIP0BE*93c3N_HBm+1qi<5pSx(&?ozovO^6n zfn+T7@6Tx)p&fw<g6Mar`Rh1V^(@@T#TyMt6`>@pbdKLr8-Gw=hiA`fP~FYC%6^J9 zJ5_WeHb=szf^iVPh6<FS%;zn^{1BKl=8qp!t|eJs-taq9>MAQI`{v_o0U?ncAMQ5Q zK}}}tY0T->Fr!%kQBjm;z)i{{IVvmw<JpvOMPOV4RVbayXm84Sbgn+n%j>2-N;ld- z#Pj2R?i&z!M@+1lp9Deo@fzqTf^MH48u~<Oa|xCStatdI4)WZ80r4sCg~KQ=I{d-E zQ~_mZ@C*$GlRw1i<hzSFdV71(4O=j8Yw+0T5WnEsSX4*;G~sefWI@$wi<>9vxzn3n z<E3Q`9!{VgK=#$q)G2iLdGk>5&}!K>@WDd-fuiH!I=sfisn!$4)nDy(ythMPM`QQW z+>17lA`^R@d7N?U3k+AIOYOXtOWjJ7mEYe%w<W=wXw-YKqQ=w<aVM%|+@0-GD3^vJ zR!8Z3t6`Qy(0aXUroc(=^%fN7k;|wFCJGo+7RQ>7<cyNEI2Ro(S!E9nVwAUuP)cvZ zCBXHu*2m2&dhqaEuw*;Ws!mRxCC-SPlkmZQQ)<U^x`XyW+WfVj2NeN;DjiZ(8~gl0 zZPj;#A}XE%;Fp+I^&2iB^Z~ObOh|S2PnQg$lgyc6IG<qJ#R{YAQFaTt958!*&>2;9 zhCgT%=}Lo%ZMCMPZ%|tt4N5z%vDx9@g+dhCcx$b6NPoo_XZa9Y?U=}oe9Og5A0sU{ zAa5!(IwIF66*f$*2Jfwg6|ImNkCro1aQYc`LR)BilV-@Y#W`v5tHu$sT|)vXjOItE zWnznpt4g5*fXks$5V*^D|AnwXy!%Gd2ys)lpuq>1Qf$p6VlhrS1cIrT7+o_L>M%?? zPSw($r#CstBk;>uCAAQT+V9R^QtzNYJ8%E4GodTo7oX6f)V1}C<?LAgjuoZQTZ${^ z{+jR1po;0cAF(~vT{iynGjdy1!`?_5N}(wwLq=Q#_%C53BBc`FZRPEB@RiNGOSp*P z0u$rVebU`63Y>X?Vu-|$)CoFjscb=p<gC0n@1+|YlJ;>`_0UfvnpHhts$JDp7q_<I z%G>(lv$B*mW`HlU15crjANX*%@#h^~eb7qbihUj#O?a=Vh8Ca03Kz>JMfV32xuzpa zZEx?c1_vL113M=)OIC}nAg49UHSC}F$T`>5$fp<^AzoMa$ShiTa0Qf9Vrxt&r=`j- z|Hcg0C??hxMMII?c%jbMqj_unLSG5HREF=ZfA&rIqtTRiHh5{0lsT9cFH~wE3Sxl7 zbG^<Qi9j3BurG|mK8%pgtH!bXbw<4rVMg9|xV-@H@Bz#JdrPQd_s4E=FSh+|17-JL zwRyA*bHeUyoT@fxf5@Fl@psDx<;%mx%nI5FF<^vpt2y(Cv+I0*Su-2y0+|}ubLIMe zfGqTlo?QwOJG>2O-GP#}FQEMm0<ISF#0FPSU{qp<$7!TY=<p;$2h#4X0_F4FGg<`` zF*IV9C4Z)JQHo%cD((b`q1~O9(M0*MWUC?%!b@ewe=au$VrX%}_F}nh2St2|(D;Z? z7<C6lDVAjarmd*opRHL3QclYMYyRNREGNW}XZ&=NtGqp1%tfK|iS8e)&ZL6SIZ4#3 zQ$FsWAIpDl9XKS!!tN3kA65Q`H2OD(6Xpvb+mn7VpZ}hs{12%__J>r0Q8W4YkDUMn z6R@AiA+a}ZWrV*jn8diSK_Vo%<5<z`D%pQNOM?@X4GPi1`jQ%m&4uU6U%h|PQb6IF zdm{Zy?B;;9$G;EITM?|V5Q;`M;3DwH{N>*dGlk7^eKQ_GRq6h-2jV3db40_MY?N8a ze^OOK5M!tS##{}d_2<L>$0k&R5=R_W$NJ;xA2<IQL^J_R);&e282kU63yweM!d2ok z(f_`OOXknHKsCqs_vi9o9NVM>K(H;1g&qB$d(6TCf=v^w)aWlj+#m`W=JwX7Bc<-f zeLk73?$$FjLE#`U<?c23b4`<Hi~s#Pr<$?o_lXKle(VowYHD|PcZgnNL-yFP?x0S9 zPLK*IWzg3x<h^pa5b`Q7FA&{#vzOX;FW=4aYIBPz{<bwv^L~xy<kB6yGj7$Ew-7^V z^WW!F>C-zL<l`D`Iw~SD2{?g<+N<3_gv^*a>r!jHT+G}ZzQJxtZnuWYVN(un8_c|y zHiHACb9|TQK$WV5t<$;A?<H&6&nIh3a=WFHxXqL%<YFv1pkLolUKWoft5CB%1}jCS z`HrTEmvg+Y1`BgMErI$96B834n!b8b-qAF4py$-}4ale<<h0c>HQhr-+Jt`mBS=f{ z>+1uUm*U$7B57h|_p`?Rh2^z1Jq-=>N)0V-?Q<XtA24mmWpObxGoxHS0{x7aE`ZcR zf|kR2!T#;#{(QALb!BU7D?}qYKFwO!!C1A~%Sa3KTdl1q@4j76@ap@bN1uN=f#rhQ zVw1bblj4;}alVDF!}V2H)OT`$VT%9({ln_w*fNjFbH|_P7_+u38lV{!n_fv6ZTy&= zC!kG>f1$?A?W#w5Zeii;vd>O9Rnc54N^1P3tW4zR+0`Y_hxpKApxFhRb1Wwf`{q$b zT~Ot9XxKE9p5z?6QfjN3=;9e%&u=3L&>&c5f%bg6-aq}<&*@y|olAXlfw8OYm5bAL zDdu&c_E6x<T$gQZd-o~lhtN*NTtV!ks|PP1VU|v~`=jq6%GLq?#&d-nspT!T`%m24 zENJ-tiu&5+<GZ}3zqe^a6EV24F}PMHf=!!E)ffQF@R`)N^`Kt87!hTOkBf(Q0PYT~ zue_r1!$`($6*`!pt}Gy=OxZIh>ajVaJaIK$Ji8vT4IMY^aYYa%MyOxKkTJ2a#?zR$ zp8%KQ$%&Z2qqo4)@vUdPHeqbPxffB-(lRd(kCcL}f`Wp!b|%Um+V8p9S#k2_rsn48 zcZQ#F8skwN%y+t}(iP(_lUwl#2k!>r$?&~^%7c^T`g1_87=v#F@$utFsmJd~J_j3} zfqiR0aLhQMOa+ADg-bJWKV)QN^nGqs^Onc~IB1@s9drfEU~!{)NV+F)Zp~#BJR)hQ zNExJkFF*s)!~W$E6{yY)I4RtB#grk(z1v8OP#<8^M=2v0zwC7H4zgS2?<Z`=%7n<| zv@T$9r@)gZa49<{L43;bXez8TotjnGKB+5<h{!;&!_sjlaS^?R<Rf&}{bXpUqc+zW zN)&N;%=@&J@wGp@+4&IoKVsW+Ce`Z%8B)Kg2`TPyjt|Gq^=xZ(Wql*J-{TcPG2YG0 zic8Btm-jgse*_c)djB)BU}t^pw#<po{G1g3wa4pHh~K9V!Gyk?$Rc0ceX-^wve2a6 zv7)VI7UPV?6FgU2Gl*!Z;3}o)ZmkWHIg%;_dR5x5ruX(CW(aAV=>DXw>4+nB)Tqft zxU*y>31A*MTs(?-b=-3FKc}J8IWCm$1<W*PHV!7TqWL`yb-vVQ18YRW1!cX}vzThl zGcCQe?1PS=u*NA<4nN03G1)LA+ftz!IjP9!nW`@E+w29|5lXb5IxO_ioRdc}YuaP9 z8Z0EyW0y@^1?<nqe4P_!4AgY83L5%THEj)p$4GiS{QFghi}!X6g~gF0poJfok2HCU zC10`}Lc9GtZGgoK8l~*RTzwmW1O&_d5vqy;YI6yRKEQ{un3R+Rlngin`WlVRv!-JP z=lN*`!DtTc3WH9dFQd+67&FT8pb`LkBN^KoT3YZGCdd6=um8kOhZs(?&TOQ%gG_ps z0XYsZTjrtjSJ!dqHA&UI`{VQ)!NbKBxNjW)Sy*VC3pD2OFv!o&&H@6UhMOrFSV`ZM z�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%0AT8!Ag4n_`K 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 Bhi?D? literal 0 HcmV?d00001 diff --git a/public/js/ff/index.js b/public/js/ff/index.js index ba6c9993e1..1cd119c383 100644 --- a/public/js/ff/index.js +++ b/public/js/ff/index.js @@ -8,7 +8,7 @@ * See the LICENSE file for details. */ -/** global: Tour, showTour, accountFrontpageUri, billCount, accountExpenseUri, accountRevenueUri */ +/** global: Tour, showTour, accountFrontpageUri, token, billCount, accountExpenseUri, accountRevenueUri */ $(function () { "use strict"; @@ -34,7 +34,7 @@ $(function () { function endTheTour() { "use strict"; - $.post('json/end-tour'); + $.post('json/end-tour', {_token: token}); } diff --git a/resources/views/javascript/variables.twig b/resources/views/javascript/variables.twig index 8d188af209..5fe2a12f60 100644 --- a/resources/views/javascript/variables.twig +++ b/resources/views/javascript/variables.twig @@ -26,4 +26,5 @@ var frac_digits = {{ localeconv.frac_digits }}; var noDataForChart = '{{ trans('firefly.no_data_for_chart')|escape }}'; var showFullList = '{{ trans('firefly.show_full_list') }}'; var showOnlyTop = '{{ trans('firefly.show_only_top',{number:listLength}) }}'; -var accountingConfig = {{ accounting|json_encode|raw }}; \ No newline at end of file +var accountingConfig = {{ accounting|json_encode|raw }}; +var token = '{{ csrf_token() }}'; \ No newline at end of file From 3e510bd3f60a9813c1bae0f3da03b8778c916cbc Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 5 Feb 2017 08:26:54 +0100 Subject: [PATCH 638/709] This fixes #549 --- app/Http/Controllers/AccountController.php | 28 ++++++--------- app/Http/Controllers/Admin/UserController.php | 5 ++- app/Http/Controllers/AttachmentController.php | 9 +++-- app/Http/Controllers/BillController.php | 22 ++++-------- app/Http/Controllers/BudgetController.php | 34 ++++++------------ app/Http/Controllers/CategoryController.php | 22 ++++-------- app/Http/Controllers/Controller.php | 36 ++++++++++++++++++- app/Http/Controllers/CurrencyController.php | 20 ++++------- app/Http/Controllers/PiggyBankController.php | 28 ++++----------- app/Http/Controllers/RuleController.php | 17 ++++----- app/Http/Controllers/RuleGroupController.php | 15 ++++---- app/Http/Controllers/TagController.php | 14 ++++---- .../Transaction/MassController.php | 9 +++-- .../Transaction/SingleController.php | 21 ++++------- .../Transaction/SplitController.php | 8 ++--- 15 files changed, 118 insertions(+), 170 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index b3b007cb9e..e68ee7f8bd 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -34,7 +34,6 @@ use Navigation; use Preferences; use Session; use Steam; -use URL; use View; /** @@ -86,7 +85,7 @@ class AccountController extends Controller // put previous url in session if not redirect from store (not "create another"). if (session('accounts.create.fromStore') !== true) { - Session::put('accounts.create.url', URL::previous()); + $this->rememberPreviousUri('accounts.create.uri'); } Session::forget('accounts.create.fromStore'); Session::flash('gaEventCategory', 'accounts'); @@ -110,7 +109,7 @@ class AccountController extends Controller unset($accountList[$account->id]); // put previous url in session - Session::put('accounts.delete.url', URL::previous()); + $this->rememberPreviousUri('accounts.delete.uri'); Session::flash('gaEventCategory', 'accounts'); Session::flash('gaEventAction', 'delete-' . $typeName); @@ -126,24 +125,17 @@ class AccountController extends Controller */ public function destroy(Request $request, ARI $repository, Account $account) { - $type = $account->accountType->type; - $typeName = config('firefly.shortNamesByFullName.' . $type); - $name = $account->name; - $accountId = $account->id; - $moveTo = $repository->find(intval($request->get('move_account_before_delete'))); + $type = $account->accountType->type; + $typeName = config('firefly.shortNamesByFullName.' . $type); + $name = $account->name; + $moveTo = $repository->find(intval($request->get('move_account_before_delete'))); $repository->destroy($account, $moveTo); Session::flash('success', strval(trans('firefly.' . $typeName . '_deleted', ['name' => $name]))); Preferences::mark(); - $uri = session('accounts.delete.url'); - if (!(strpos($uri, sprintf('accounts/show/%s', $accountId)) === false)) { - // uri would point back to account - $uri = route('accounts.index', [$typeName]); - } - - return redirect($uri); + return redirect($this->getPreviousUri('accounts.delete.uri')); } /** @@ -168,7 +160,7 @@ class AccountController extends Controller // put previous url in session if not redirect from store (not "return_to_edit"). if (session('accounts.edit.fromUpdate') !== true) { - Session::put('accounts.edit.url', URL::previous()); + $this->rememberPreviousUri('accounts.edit.uri'); } Session::forget('accounts.edit.fromUpdate'); @@ -360,7 +352,7 @@ class AccountController extends Controller } // redirect to previous URL. - return redirect(session('accounts.create.url')); + return redirect($this->getPreviousUri('accounts.create.uri')); } /** @@ -386,7 +378,7 @@ class AccountController extends Controller } // redirect to previous URL. - return redirect(session('accounts.edit.url')); + return redirect($this->getPreviousUri('accounts.edit.uri')); } diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 9fcb8b9a99..818c12c2f6 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -20,7 +20,6 @@ use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; use Preferences; use Session; -use URL; use View; /** @@ -57,7 +56,7 @@ class UserController extends Controller { // put previous url in session if not redirect from store (not "return_to_edit"). if (session('users.edit.fromUpdate') !== true) { - Session::put('users.edit.url', URL::previous()); + $this->rememberPreviousUri('users.edit.uri'); } Session::forget('users.edit.fromUpdate'); @@ -156,7 +155,7 @@ class UserController extends Controller } // redirect to previous URL. - return redirect(session('users.edit.url')); + return redirect($this->getPreviousUri('users.edit.uri')); } diff --git a/app/Http/Controllers/AttachmentController.php b/app/Http/Controllers/AttachmentController.php index 2b154defea..a6532309f1 100644 --- a/app/Http/Controllers/AttachmentController.php +++ b/app/Http/Controllers/AttachmentController.php @@ -22,7 +22,6 @@ use Illuminate\Http\Response as LaravelResponse; use Preferences; use Response; use Session; -use URL; use View; /** @@ -63,7 +62,7 @@ class AttachmentController extends Controller $subTitle = trans('firefly.delete_attachment', ['name' => $attachment->filename]); // put previous url in session - Session::put('attachments.delete.url', URL::previous()); + $this->rememberPreviousUri('attachments.delete.uri'); Session::flash('gaEventCategory', 'attachments'); Session::flash('gaEventAction', 'delete-attachment'); @@ -85,7 +84,7 @@ class AttachmentController extends Controller Session::flash('success', strval(trans('firefly.attachment_deleted', ['name' => $name]))); Preferences::mark(); - return redirect(session('attachments.delete.url')); + return redirect($this->getPreviousUri('attachments.delete.uri')); } /** @@ -131,7 +130,7 @@ class AttachmentController extends Controller // put previous url in session if not redirect from store (not "return_to_edit"). if (session('attachments.edit.fromUpdate') !== true) { - Session::put('attachments.edit.url', URL::previous()); + $this->rememberPreviousUri('attachments.edit.uri'); } Session::forget('attachments.edit.fromUpdate'); @@ -181,7 +180,7 @@ class AttachmentController extends Controller } // redirect to previous URL. - return redirect(session('attachments.edit.url')); + return redirect($this->getPreviousUri('attachments.edit.uri')); } diff --git a/app/Http/Controllers/BillController.php b/app/Http/Controllers/BillController.php index 0642dd260b..916cc7c406 100644 --- a/app/Http/Controllers/BillController.php +++ b/app/Http/Controllers/BillController.php @@ -66,7 +66,7 @@ class BillController extends Controller // put previous url in session if not redirect from store (not "create another"). if (session('bills.create.fromStore') !== true) { - Session::put('bills.create.url', URL::previous()); + $this->rememberPreviousUri('bills.create.uri'); } Session::forget('bills.create.fromStore'); Session::flash('gaEventCategory', 'bills'); @@ -83,7 +83,7 @@ class BillController extends Controller public function delete(Bill $bill) { // put previous url in session - Session::put('bills.delete.url', URL::previous()); + $this->rememberPreviousUri('bills.delete.uri'); Session::flash('gaEventCategory', 'bills'); Session::flash('gaEventAction', 'delete'); $subTitle = trans('firefly.delete_bill', ['name' => $bill->name]); @@ -99,20 +99,13 @@ class BillController extends Controller */ public function destroy(BillRepositoryInterface $repository, Bill $bill) { - $name = $bill->name; - $billId = $bill->id; + $name = $bill->name; $repository->destroy($bill); Session::flash('success', strval(trans('firefly.deleted_bill', ['name' => $name]))); Preferences::mark(); - $uri = session('bills.delete.url'); - if (!(strpos($uri, sprintf('bills/show/%s', $billId)) === false)) { - // uri would point back to bill - $uri = route('bills.index'); - } - - return redirect($uri); + return redirect($this->getPreviousUri('bills.delete.uri')); } /** @@ -130,7 +123,7 @@ class BillController extends Controller // put previous url in session if not redirect from store (not "return_to_edit"). if (session('bills.edit.fromUpdate') !== true) { - Session::put('bills.edit.url', URL::previous()); + $this->rememberPreviousUri('bills.edit.uri'); } Session::forget('bills.edit.fromUpdate'); Session::flash('gaEventCategory', 'bills'); @@ -249,7 +242,7 @@ class BillController extends Controller } // redirect to previous URL. - return redirect(session('bills.create.url')); + return redirect($this->getPreviousUri('bills.create.uri')); } @@ -275,8 +268,7 @@ class BillController extends Controller return redirect(route('bills.edit', [$bill->id]))->withInput(['return_to_edit' => 1]); } - // redirect to previous URL. - return redirect(session('bills.edit.url')); + return redirect($this->getPreviousUri('bills.edit.uri')); } diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index 0d8ce65937..1f2d510dcb 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -30,7 +30,6 @@ use Illuminate\Support\Collection; use Preferences; use Response; use Session; -use URL; use View; /** @@ -94,7 +93,7 @@ class BudgetController extends Controller { // put previous url in session if not redirect from store (not "create another"). if (session('budgets.create.fromStore') !== true) { - Session::put('budgets.create.url', URL::previous()); + $this->rememberPreviousUri('budgets.create.uri'); } Session::forget('budgets.create.fromStore'); Session::flash('gaEventCategory', 'budgets'); @@ -114,7 +113,7 @@ class BudgetController extends Controller $subTitle = trans('firefly.delete_budget', ['name' => $budget->name]); // put previous url in session - Session::put('budgets.delete.url', URL::previous()); + $this->rememberPreviousUri('budgets.delete.uri'); Session::flash('gaEventCategory', 'budgets'); Session::flash('gaEventAction', 'delete'); @@ -129,21 +128,12 @@ class BudgetController extends Controller public function destroy(Budget $budget) { - $name = $budget->name; - $budgetId = $budget->id; + $name = $budget->name; $this->repository->destroy($budget); - - Session::flash('success', strval(trans('firefly.deleted_budget', ['name' => e($name)]))); Preferences::mark(); - $uri = session('budgets.delete.url'); - if (!(strpos($uri, sprintf('budgets/show/%s', $budgetId)) === false)) { - // uri would point back to budget - $uri = route('budgets.index'); - } - - return redirect($uri); + return redirect($this->getPreviousUri('budgets.delete.uri')); } /** @@ -157,7 +147,7 @@ class BudgetController extends Controller // put previous url in session if not redirect from store (not "return_to_edit"). if (session('budgets.edit.fromUpdate') !== true) { - Session::put('budgets.edit.url', URL::previous()); + $this->rememberPreviousUri('budgets.edit.uri'); } Session::forget('budgets.edit.fromUpdate'); Session::flash('gaEventCategory', 'budgets'); @@ -278,9 +268,9 @@ class BudgetController extends Controller throw new FireflyException('This budget limit is not part of this budget.'); } - $page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page')); - $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); - $subTitle = trans( + $page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page')); + $pageSize = intval(Preferences::get('transactionPageSize', 50)->data); + $subTitle = trans( 'firefly.budget_in_period', [ 'name' => $budget->name, 'start' => $budgetLimit->start_date->formatLocalized($this->monthAndDayFormat), @@ -325,9 +315,7 @@ class BudgetController extends Controller return redirect(route('budgets.create'))->withInput(); } - // redirect to previous URL. - return redirect(session('budgets.create.url')); - + return redirect($this->getPreviousUri('budgets.create.uri')); } /** @@ -351,9 +339,7 @@ class BudgetController extends Controller return redirect(route('budgets.edit', [$budget->id]))->withInput(['return_to_edit' => 1]); } - // redirect to previous URL. - return redirect(session('budgets.edit.url')); - + return redirect($this->getPreviousUri('budgets.edit.uri')); } /** diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index 2a52505818..e74786199c 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -26,7 +26,6 @@ use Illuminate\Support\Collection; use Navigation; use Preferences; use Session; -use URL; use View; /** @@ -61,7 +60,7 @@ class CategoryController extends Controller public function create() { if (session('categories.create.fromStore') !== true) { - Session::put('categories.create.url', URL::previous()); + $this->rememberPreviousUri('categories.create.uri'); } Session::forget('categories.create.fromStore'); Session::flash('gaEventCategory', 'categories'); @@ -81,7 +80,7 @@ class CategoryController extends Controller $subTitle = trans('firefly.delete_category', ['name' => $category->name]); // put previous url in session - Session::put('categories.delete.url', URL::previous()); + $this->rememberPreviousUri('categories.delete.uri'); Session::flash('gaEventCategory', 'categories'); Session::flash('gaEventAction', 'delete'); @@ -98,20 +97,13 @@ class CategoryController extends Controller public function destroy(CategoryRepositoryInterface $repository, Category $category) { - $name = $category->name; - $categoryId = $category->id; + $name = $category->name; $repository->destroy($category); Session::flash('success', strval(trans('firefly.deleted_category', ['name' => e($name)]))); Preferences::mark(); - $uri = session('categories.delete.url'); - if (!(strpos($uri, sprintf('categories/show/%s', $categoryId)) === false)) { - // uri would point back to category - $uri = route('categories.index'); - } - - return redirect($uri); + return redirect($this->getPreviousUri('categories.delete.uri')); } /** @@ -125,7 +117,7 @@ class CategoryController extends Controller // put previous url in session if not redirect from store (not "return_to_edit"). if (session('categories.edit.fromUpdate') !== true) { - Session::put('categories.edit.url', URL::previous()); + $this->rememberPreviousUri('categories.edit.uri'); } Session::forget('categories.edit.fromUpdate'); Session::flash('gaEventCategory', 'categories'); @@ -311,9 +303,7 @@ class CategoryController extends Controller return redirect(route('categories.edit', [$category->id])); } - // redirect to previous URL. - return redirect(session('categories.edit.url')); - + return redirect($this->getPreviousUri('categories.edit.uri')); } /** diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 0904e4bbbc..d2233d75fb 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -23,6 +23,7 @@ use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Routing\Controller as BaseController; use Session; +use URL; use View; /** @@ -56,7 +57,6 @@ class Controller extends BaseController View::share('DEMO_PASSWORD', env('DEMO_PASSWORD', '')); // translations: - $this->middleware( function ($request, $next) { $this->monthFormat = (string)trans('config.month'); @@ -69,6 +69,32 @@ class Controller extends BaseController } + /** + * Functionality: + * + * - If the $identifier contains the word "delete" then a remembered uri with the text "/show/" in it will not be returned but instead the index (/) + * will be returned. + * - If the remembered uri contains "javascript/" the remembered uri will not be returned but instead the index (/) will be returned. + * + * @param string $identifier + * + * @return string + */ + protected function getPreviousUri(string $identifier): string + { + $uri = strval(session($identifier)); + // 1 (see above): + if (!(strpos($identifier, 'delete') === false) && !(strpos($uri, '/show/') === false)) { + $uri = route('index'); + } + + // 2 (see above) + if (!(strpos($uri, 'javascript') === false)) { + $uri = route('index'); + } + + return $uri; + } /** * @param TransactionJournal $journal @@ -102,4 +128,12 @@ class Controller extends BaseController return redirect(route('index')); } + /** + * @param string $identifier + */ + protected function rememberPreviousUri(string $identifier) + { + Session::put($identifier, URL::previous()); + } + } diff --git a/app/Http/Controllers/CurrencyController.php b/app/Http/Controllers/CurrencyController.php index 2b01c3043c..01cb6abd3e 100644 --- a/app/Http/Controllers/CurrencyController.php +++ b/app/Http/Controllers/CurrencyController.php @@ -20,7 +20,6 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use Log; use Preferences; use Session; -use URL; use View; /** @@ -60,7 +59,7 @@ class CurrencyController extends Controller // put previous url in session if not redirect from store (not "create another"). if (session('currencies.create.fromStore') !== true) { - Session::put('currencies.create.url', URL::previous()); + $this->rememberPreviousUri('currencies.create.uri'); } Session::forget('currencies.create.fromStore'); Session::flash('gaEventCategory', 'currency'); @@ -105,7 +104,7 @@ class CurrencyController extends Controller // put previous url in session - Session::put('currencies.delete.url', URL::previous()); + $this->rememberPreviousUri('currencies.delete.uri'); Session::flash('gaEventCategory', 'currency'); Session::flash('gaEventAction', 'delete'); $subTitle = trans('form.delete_currency', ['name' => $currency->name]); @@ -131,7 +130,7 @@ class CurrencyController extends Controller $repository->destroy($currency); Session::flash('success', trans('firefly.deleted_currency', ['name' => $currency->name])); - return redirect(session('currencies.delete.url')); + return redirect($this->getPreviousUri('currencies.delete.uri')); } /** @@ -147,7 +146,7 @@ class CurrencyController extends Controller // put previous url in session if not redirect from store (not "return_to_edit"). if (session('currencies.edit.fromUpdate') !== true) { - Session::put('currencies.edit.url', URL::previous()); + $this->rememberPreviousUri('currencies.edit.uri'); } Session::forget('currencies.edit.fromUpdate'); Session::flash('gaEventCategory', 'currency'); @@ -188,7 +187,7 @@ class CurrencyController extends Controller if (!auth()->user()->hasRole('owner')) { Log::error('User ' . auth()->user()->id . ' is not admin, but tried to store a currency.'); - return redirect(session('currencies.create.url')); + return redirect($this->getPreviousUri('currencies.create.uri')); } $data = $request->getCurrencyData(); @@ -201,10 +200,7 @@ class CurrencyController extends Controller return redirect(route('currencies.create'))->withInput(); } - // redirect to previous URL. - return redirect(session('currencies.create.url')); - - + return redirect($this->getPreviousUri('currencies.create.uri')); } /** @@ -230,8 +226,6 @@ class CurrencyController extends Controller return redirect(route('currencies.edit', [$currency->id])); } - // redirect to previous URL. - return redirect(session('currencies.edit.url')); - + return redirect($this->getPreviousUri('currencies.edit.uri')); } } diff --git a/app/Http/Controllers/PiggyBankController.php b/app/Http/Controllers/PiggyBankController.php index 47357c5bc0..b74905c682 100644 --- a/app/Http/Controllers/PiggyBankController.php +++ b/app/Http/Controllers/PiggyBankController.php @@ -27,7 +27,6 @@ use Preferences; use Response; use Session; use Steam; -use URL; use View; /** @@ -116,7 +115,7 @@ class PiggyBankController extends Controller // put previous url in session if not redirect from store (not "create another"). if (session('piggy-banks.create.fromStore') !== true) { - Session::put('piggy-banks.create.url', URL::previous()); + $this->rememberPreviousUri('piggy-banks.create.uri'); } Session::forget('piggy-banks.create.fromStore'); Session::flash('gaEventCategory', 'piggy-banks'); @@ -135,7 +134,7 @@ class PiggyBankController extends Controller $subTitle = trans('firefly.delete_piggy_bank', ['name' => $piggyBank->name]); // put previous url in session - Session::put('piggy-banks.delete.url', URL::previous()); + $this->rememberPreviousUri('piggy-banks.delete.uri'); Session::flash('gaEventCategory', 'piggy-banks'); Session::flash('gaEventAction', 'delete'); @@ -152,16 +151,9 @@ class PiggyBankController extends Controller { Session::flash('success', strval(trans('firefly.deleted_piggy_bank', ['name' => e($piggyBank->name)]))); Preferences::mark(); - $piggyBankId = $piggyBank->id; $repository->destroy($piggyBank); - $uri = session('piggy-banks.delete.url'); - if (!(strpos($uri, sprintf('piggy-banks/show/%s', $piggyBankId)) === false)) { - // uri would point back to piggy bank - $uri = route('piggy-banks.index'); - } - - return redirect($uri); + return redirect($this->getPreviousUri('piggy-banks.delete.uri')); } /** @@ -197,7 +189,7 @@ class PiggyBankController extends Controller // put previous url in session if not redirect from store (not "return_to_edit"). if (session('piggy-banks.edit.fromUpdate') !== true) { - Session::put('piggy-banks.edit.url', URL::previous()); + $this->rememberPreviousUri('piggy-banks.edit.uri'); } Session::forget('piggy-banks.edit.fromUpdate'); @@ -404,9 +396,7 @@ class PiggyBankController extends Controller return redirect(route('piggy-banks.create'))->withInput(); } - - // redirect to previous URL. - return redirect(session('piggy-banks.create.url')); + return redirect($this->getPreviousUri('piggy-banks.edit.uri')); } /** @@ -430,12 +420,6 @@ class PiggyBankController extends Controller return redirect(route('piggy-banks.edit', [$piggyBank->id])); } - - // redirect to previous URL. - return redirect(session('piggy-banks.edit.url')); - - + return redirect($this->getPreviousUri('piggy-banks.edit.uri')); } - - } diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 8e0e466933..2eee1da8a0 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -26,7 +26,6 @@ use Illuminate\Http\Request; use Preferences; use Response; use Session; -use URL; use View; /** @@ -88,7 +87,7 @@ class RuleController extends Controller // put previous url in session if not redirect from store (not "create another"). if (session('rules.create.fromStore') !== true) { - Session::put('rules.create.url', URL::previous()); + $this->rememberPreviousUri('rules.create.uri'); } Session::forget('rules.create.fromStore'); Session::flash('gaEventCategory', 'rules'); @@ -111,7 +110,7 @@ class RuleController extends Controller $subTitle = trans('firefly.delete_rule', ['title' => $rule->title]); // put previous url in session - Session::put('rules.delete.url', URL::previous()); + $this->rememberPreviousUri('rules.delete.uri'); Session::flash('gaEventCategory', 'rules'); Session::flash('gaEventAction', 'delete-rule'); @@ -135,8 +134,7 @@ class RuleController extends Controller Session::flash('success', trans('firefly.deleted_rule', ['title' => $title])); Preferences::mark(); - - return redirect(session('rules.delete.url')); + return redirect($this->getPreviousUri('rules.delete.uri')); } /** @@ -181,7 +179,7 @@ class RuleController extends Controller // put previous url in session if not redirect from store (not "return_to_edit"). if (session('rules.edit.fromUpdate') !== true) { - Session::put('rules.edit.url', URL::previous()); + $this->rememberPreviousUri('rules.edit.uri'); } Session::forget('rules.edit.fromUpdate'); Session::flash('gaEventCategory', 'rules'); @@ -263,9 +261,7 @@ class RuleController extends Controller return redirect(route('rules.create', [$ruleGroup]))->withInput(); } - // redirect to previous URL. - return redirect(session('rules.create.url')); - + return redirect($this->getPreviousUri('rules.create.uri')); } /** @@ -350,8 +346,7 @@ class RuleController extends Controller return redirect(route('rules.edit', [$rule->id]))->withInput(['return_to_edit' => 1]); } - // redirect to previous URL. - return redirect(session('rules.edit.url')); + return redirect($this->getPreviousUri('rules.edit.uri')); } private function createDefaultRule() diff --git a/app/Http/Controllers/RuleGroupController.php b/app/Http/Controllers/RuleGroupController.php index ee0cb84c26..e0bc8b7476 100644 --- a/app/Http/Controllers/RuleGroupController.php +++ b/app/Http/Controllers/RuleGroupController.php @@ -25,7 +25,6 @@ use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use Illuminate\Http\Request; use Preferences; use Session; -use URL; use View; /** @@ -63,7 +62,7 @@ class RuleGroupController extends Controller // put previous url in session if not redirect from store (not "create another"). if (session('rule-groups.create.fromStore') !== true) { - Session::put('rule-groups.create.url', URL::previous()); + $this->rememberPreviousUri('rule-groups.create.uri'); } Session::forget('rule-groups.create.fromStore'); Session::flash('gaEventCategory', 'rules'); @@ -86,7 +85,7 @@ class RuleGroupController extends Controller unset($ruleGroupList[$ruleGroup->id]); // put previous url in session - Session::put('rule-groups.delete.url', URL::previous()); + $this->rememberPreviousUri('rule-groups.delete.uri'); Session::flash('gaEventCategory', 'rules'); Session::flash('gaEventAction', 'delete-rule-group'); @@ -112,8 +111,7 @@ class RuleGroupController extends Controller Session::flash('success', strval(trans('firefly.deleted_rule_group', ['title' => $title]))); Preferences::mark(); - - return redirect(session('rule-groups.delete.url')); + return redirect($this->getPreviousUri('rule-groups.delete.uri')); } /** @@ -141,7 +139,7 @@ class RuleGroupController extends Controller // put previous url in session if not redirect from store (not "return_to_edit"). if (session('rule-groups.edit.fromUpdate') !== true) { - Session::put('rule-groups.edit.url', URL::previous()); + $this->rememberPreviousUri('rule-groups.edit.uri'); } Session::forget('rule-groups.edit.fromUpdate'); Session::flash('gaEventCategory', 'rules'); @@ -225,8 +223,7 @@ class RuleGroupController extends Controller return redirect(route('rule-groups.create'))->withInput(); } - // redirect to previous URL. - return redirect(session('rule-groups.create.url')); + return redirect($this->getPreviousUri('rule-groups.create.uri')); } /** @@ -271,7 +268,7 @@ class RuleGroupController extends Controller } // redirect to previous URL. - return redirect(session('rule-groups.edit.url')); + return redirect($this->getPreviousUri('rule-groups.edit.uri')); } } diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index 260adc97e3..bdb3160ea7 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -26,7 +26,6 @@ use Illuminate\Support\Collection; use Navigation; use Preferences; use Session; -use URL; use View; /** @@ -98,7 +97,7 @@ class TagController extends Controller } // put previous url in session if not redirect from store (not "create another"). if (session('tags.create.fromStore') !== true) { - Session::put('tags.create.url', URL::previous()); + $this->rememberPreviousUri('tags.create.uri'); } Session::forget('tags.create.fromStore'); Session::flash('gaEventCategory', 'tags'); @@ -117,7 +116,7 @@ class TagController extends Controller $subTitle = trans('breadcrumbs.delete_tag', ['tag' => e($tag->tag)]); // put previous url in session - Session::put('tags.delete.url', URL::previous()); + $this->rememberPreviousUri('tags.delete.uri'); Session::flash('gaEventCategory', 'tags'); Session::flash('gaEventAction', 'delete'); @@ -138,7 +137,7 @@ class TagController extends Controller Session::flash('success', strval(trans('firefly.deleted_tag', ['tag' => e($tagName)]))); Preferences::mark(); - return redirect(route('tags.index')); + return redirect($this->getPreviousUri('tags.delete.uri')); } /** @@ -174,7 +173,7 @@ class TagController extends Controller // put previous url in session if not redirect from store (not "return_to_edit"). if (session('tags.edit.fromUpdate') !== true) { - Session::put('tags.edit.url', URL::previous()); + $this->rememberPreviousUri('tags.edit.uri'); } Session::forget('tags.edit.fromUpdate'); Session::flash('gaEventCategory', 'tags'); @@ -289,8 +288,7 @@ class TagController extends Controller return redirect(route('tags.create'))->withInput(); } - // redirect to previous URL. - return redirect(session('tags.create.url')); + return redirect($this->getPreviousUri('tags.create.uri')); } @@ -316,7 +314,7 @@ class TagController extends Controller } // redirect to previous URL. - return redirect(session('tags.edit.url')); + return redirect($this->getPreviousUri('tags.edit.uri')); } /** diff --git a/app/Http/Controllers/Transaction/MassController.php b/app/Http/Controllers/Transaction/MassController.php index d139f0f414..5a61b060f4 100644 --- a/app/Http/Controllers/Transaction/MassController.php +++ b/app/Http/Controllers/Transaction/MassController.php @@ -25,7 +25,6 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use Illuminate\Support\Collection; use Preferences; use Session; -use URL; use View; /** @@ -63,7 +62,7 @@ class MassController extends Controller $subTitle = trans('firefly.mass_delete_journals'); // put previous url in session - Session::put('transactions.mass-delete.url', URL::previous()); + $this->rememberPreviousUri('transactions.mass-delete.uri'); Session::flash('gaEventCategory', 'transactions'); Session::flash('gaEventAction', 'mass-delete'); @@ -104,7 +103,7 @@ class MassController extends Controller Session::flash('success', trans('firefly.mass_deleted_transactions_success', ['amount' => $count])); // redirect to previous URL: - return redirect(session('transactions.mass-delete.url')); + return redirect($this->getPreviousUri('transactions.mass-delete.uri')); } @@ -150,7 +149,7 @@ class MassController extends Controller } // put previous url in session - Session::put('transactions.mass-edit.url', URL::previous()); + $this->rememberPreviousUri('transactions.mass-edit.uri'); Session::flash('gaEventCategory', 'transactions'); Session::flash('gaEventAction', 'mass-edit'); @@ -236,7 +235,7 @@ class MassController extends Controller Session::flash('success', trans('firefly.mass_edited_transactions_success', ['amount' => $count])); // redirect to previous URL: - return redirect(session('transactions.mass-edit.url')); + return redirect($this->getPreviousUri('transactions.mass-edit.uri')); } } diff --git a/app/Http/Controllers/Transaction/SingleController.php b/app/Http/Controllers/Transaction/SingleController.php index 9c006617bd..c031443e1c 100644 --- a/app/Http/Controllers/Transaction/SingleController.php +++ b/app/Http/Controllers/Transaction/SingleController.php @@ -31,7 +31,6 @@ use Log; use Preferences; use Session; use Steam; -use URL; use View; /** @@ -140,8 +139,7 @@ class SingleController extends Controller // put previous url in session if not redirect from store (not "create another"). if (session('transactions.create.fromStore') !== true) { - $url = URL::previous(); - Session::put('transactions.create.url', $url); + $this->rememberPreviousUri('transactions.create.uri'); } Session::forget('transactions.create.fromStore'); Session::flash('gaEventCategory', 'transactions'); @@ -172,7 +170,7 @@ class SingleController extends Controller $subTitle = trans('firefly.delete_' . $what, ['description' => $journal->description]); // put previous url in session - Session::put('transactions.delete.url', URL::previous()); + $this->rememberPreviousUri('transactions.delete.uri'); Session::flash('gaEventCategory', 'transactions'); Session::flash('gaEventAction', 'delete-' . $what); @@ -200,13 +198,7 @@ class SingleController extends Controller Preferences::mark(); - $uri = session('transactions.delete.url'); - if (!(strpos($uri, sprintf('transactions/show/%s', $journalId)) === false)) { - // uri would point back to transaction - $uri = route('transactions.index', [strtolower($type)]); - } - - return redirect($uri); + return redirect($this->getPreviousUri('transactions.delete.uri')); } /** @@ -275,7 +267,7 @@ class SingleController extends Controller // put previous url in session if not redirect from store (not "return_to_edit"). if (session('transactions.edit.fromUpdate') !== true) { - Session::put('transactions.edit.url', URL::previous()); + $this->rememberPreviousUri('transactions.edit.uri'); } Session::forget('transactions.edit.fromUpdate'); @@ -336,7 +328,7 @@ class SingleController extends Controller // redirect to previous URL. - return redirect(session('transactions.create.url')); + return redirect($this->getPreviousUri('transactions.create.uri')); } @@ -386,7 +378,6 @@ class SingleController extends Controller } // redirect to previous URL. - return redirect(session('transactions.edit.url')); - + return redirect($this->getPreviousUri('transactions.edit.uri')); } } diff --git a/app/Http/Controllers/Transaction/SplitController.php b/app/Http/Controllers/Transaction/SplitController.php index 91b35ddfce..e667cb5f1b 100644 --- a/app/Http/Controllers/Transaction/SplitController.php +++ b/app/Http/Controllers/Transaction/SplitController.php @@ -30,7 +30,6 @@ use Log; use Preferences; use Session; use Steam; -use URL; use View; /** @@ -107,7 +106,7 @@ class SplitController extends Controller // put previous url in session if not redirect from store (not "return_to_edit"). if (session('transactions.edit-split.fromUpdate') !== true) { - Session::put('transactions.edit-split.url', URL::previous()); + $this->rememberPreviousUri('transactions.edit-split.uri'); } Session::forget('transactions.edit-split.fromUpdate'); @@ -139,7 +138,7 @@ class SplitController extends Controller $data = $this->arrayFromInput($request); $journal = $repository->updateSplitJournal($journal, $data); /** @var array $files */ - $files = $request->hasFile('attachments') ? $request->file('attachments') : null; + $files = $request->hasFile('attachments') ? $request->file('attachments') : null; // save attachments: $this->attachments->saveAttachmentsForModel($journal, $files); @@ -163,8 +162,7 @@ class SplitController extends Controller } // redirect to previous URL. - return redirect(session('transactions.edit-split.url')); - + return redirect($this->getPreviousUri('transactions.edit-split.uri')); } /** From 959a1a08f01b907f488cdf56faae3f415d0836e1 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 5 Feb 2017 08:27:23 +0100 Subject: [PATCH 639/709] =?UTF-8?q?Tell=20view=20whether=20we=E2=80=99re?= =?UTF-8?q?=20in=20Sandstorm.IO=20mode.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Middleware/Sandstorm.php | 3 +++ resources/views/layout/default.twig | 4 ++-- resources/views/partials/menu-sidebar.twig | 7 +++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/Http/Middleware/Sandstorm.php b/app/Http/Middleware/Sandstorm.php index ff4093cede..92225ceef5 100644 --- a/app/Http/Middleware/Sandstorm.php +++ b/app/Http/Middleware/Sandstorm.php @@ -15,6 +15,7 @@ use Auth; use Closure; use FireflyIII\User; use Illuminate\Http\Request; +use View; /** * Class Sandstorm @@ -37,6 +38,7 @@ class Sandstorm { // is in Sandstorm environment? $sandstorm = intval(getenv('SANDSTORM')) === 1; + View::share('SANDSTORM', $sandstorm); if (!$sandstorm) { return $next($request); } @@ -55,6 +57,7 @@ class Sandstorm 'password' => str_random(16), ] ); + } diff --git a/resources/views/layout/default.twig b/resources/views/layout/default.twig index fc86a04dce..08f211925c 100644 --- a/resources/views/layout/default.twig +++ b/resources/views/layout/default.twig @@ -72,13 +72,13 @@ </span> </li> - <!-- Notifications: style can be found in dropdown.less --> - <!-- User Account: style can be found in dropdown.less --> + {% if not SANDSTORM %} <li class="dropdown user user-menu"> <span style="cursor:default;color:#fff;padding: 15px;display: block;line-height: 20px;"> <span class="hidden-xs">{{ Auth.user.email }}</span> </span> </li> + {% endif %} <li id="sidebar-toggle"> <a href="#" data-toggle="control-sidebar"><i class="fa fa-plus-circle"></i></a> </li> diff --git a/resources/views/partials/menu-sidebar.twig b/resources/views/partials/menu-sidebar.twig index 8de86ca538..c5c6210177 100644 --- a/resources/views/partials/menu-sidebar.twig +++ b/resources/views/partials/menu-sidebar.twig @@ -136,6 +136,7 @@ </li> <!-- options and preferences --> + <li id="option-menu" class="{{ activeRoutePartial('admin') }} {{ activeRoutePartial('profile') }} {{ activeRoutePartial('preferences') }} {{ activeRoutePartial('currency') }} treeview"> <a href="#"> @@ -147,9 +148,11 @@ </a> <ul class="treeview-menu"> + {% if not SANDSTORM %} <li class="{{ activeRoutePartial('profile') }}"> <a class="{{ activeRouteStrict('profile.index') }}" href="{{ route('profile.index') }}"><i class="fa fa-user fa-fw"></i> {{ 'profile'|_ }}</a> </li> + {% endif %} <li class="{{ activeRoutePartial('preferences') }}"> <a class="{{ activeRouteStrict('preferences.index') }}" href="{{ route('preferences.index') }}"><i class="fa fa-gear fa-fw"></i> {{ 'preferences'|_ }}</a> </li> @@ -171,12 +174,12 @@ <!-- other options --> - + {% if not SANDSTORM %} <li> <a href="{{ route('logout') }}"> <i class="fa fa-sign-out fa-fw"></i> <span>{{ 'logout'|_ }}</span> </a> </li> - + {% endif %} </ul> From 353db6c4a5f5afec675575dc4367a8c43b283b98 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 5 Feb 2017 08:35:17 +0100 Subject: [PATCH 640/709] Updated composer.lock file. --- composer.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/composer.lock b/composer.lock index 26f2d42877..9e6e22bf15 100644 --- a/composer.lock +++ b/composer.lock @@ -518,16 +518,16 @@ }, { "name": "doctrine/dbal", - "version": "v2.5.10", + "version": "v2.5.11", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "fc376f7a61498e18520cd6fa083752a4ca08072b" + "reference": "1b1effbddbdc0f40d1c8f849f44bcddac4f52a48" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/fc376f7a61498e18520cd6fa083752a4ca08072b", - "reference": "fc376f7a61498e18520cd6fa083752a4ca08072b", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/1b1effbddbdc0f40d1c8f849f44bcddac4f52a48", + "reference": "1b1effbddbdc0f40d1c8f849f44bcddac4f52a48", "shasum": "" }, "require": { @@ -585,7 +585,7 @@ "persistence", "queryobject" ], - "time": "2017-01-23T23:17:10+00:00" + "time": "2017-02-04T21:20:13+00:00" }, { "name": "doctrine/inflector", @@ -3880,16 +3880,16 @@ }, { "name": "phpunit/phpunit", - "version": "5.7.9", + "version": "5.7.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "69f832b87c731d5cacad7f91948778fe98335fdd" + "reference": "bf0804199f516fe80ffcc48ac6d4741c49baeb6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/69f832b87c731d5cacad7f91948778fe98335fdd", - "reference": "69f832b87c731d5cacad7f91948778fe98335fdd", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bf0804199f516fe80ffcc48ac6d4741c49baeb6e", + "reference": "bf0804199f516fe80ffcc48ac6d4741c49baeb6e", "shasum": "" }, "require": { @@ -3906,11 +3906,11 @@ "phpunit/php-text-template": "~1.2", "phpunit/php-timer": "^1.0.6", "phpunit/phpunit-mock-objects": "^3.2", - "sebastian/comparator": "~1.2.2", + "sebastian/comparator": "^1.2.4", "sebastian/diff": "~1.2", "sebastian/environment": "^1.3.4 || ^2.0", "sebastian/exporter": "~2.0", - "sebastian/global-state": "^1.0 || ^2.0", + "sebastian/global-state": "^1.1", "sebastian/object-enumerator": "~2.0", "sebastian/resource-operations": "~1.0", "sebastian/version": "~1.0|~2.0", @@ -3958,7 +3958,7 @@ "testing", "xunit" ], - "time": "2017-01-28T06:14:33+00:00" + "time": "2017-02-04T09:03:53+00:00" }, { "name": "phpunit/phpunit-mock-objects", From 5e596a9cb7d36279070b5cac8979a92f53bb5e94 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 5 Feb 2017 08:47:37 +0100 Subject: [PATCH 641/709] Remove speedtrap from oh-unit file. --- composer.json | 3 +-- composer.lock | 52 +-------------------------------------------------- phpunit.xml | 4 ---- 3 files changed, 2 insertions(+), 57 deletions(-) diff --git a/composer.json b/composer.json index f89c9630e8..76c2816122 100755 --- a/composer.json +++ b/composer.json @@ -68,8 +68,7 @@ "symfony/css-selector": "3.1.*", "symfony/dom-crawler": "3.1.*", "barryvdh/laravel-debugbar": "2.*", - "barryvdh/laravel-ide-helper": "2.*", - "johnkary/phpunit-speedtrap": "^1.0" + "barryvdh/laravel-ide-helper": "2.*" }, "autoload": { "classmap": [ diff --git a/composer.lock b/composer.lock index 9e6e22bf15..0792641fa5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "5f1001d0b797b78e769d42adf358620d", + "content-hash": "098af9634cba1c6d47c25ef1d4803367", "packages": [ { "name": "bacon/bacon-qr-code", @@ -3207,56 +3207,6 @@ ], "time": "2015-05-11T14:41:42+00:00" }, - { - "name": "johnkary/phpunit-speedtrap", - "version": "v1.0.1", - "source": { - "type": "git", - "url": "https://github.com/johnkary/phpunit-speedtrap.git", - "reference": "76a26f8a903a9434608cdad2b41c40cd134ea326" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/johnkary/phpunit-speedtrap/zipball/76a26f8a903a9434608cdad2b41c40cd134ea326", - "reference": "76a26f8a903a9434608cdad2b41c40cd134ea326", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "3.7.*|~4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "JohnKary": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "John Kary", - "email": "john@johnkary.net" - } - ], - "description": "Find slow tests in your PHPUnit test suite", - "homepage": "https://github.com/johnkary/phpunit-speedtrap", - "keywords": [ - "phpunit", - "profile", - "slow" - ], - "time": "2015-09-13T19:01:00+00:00" - }, { "name": "maximebf/debugbar", "version": "1.13.1", diff --git a/phpunit.xml b/phpunit.xml index d439428075..9f026f875f 100755 --- a/phpunit.xml +++ b/phpunit.xml @@ -18,10 +18,6 @@ </testsuite> </testsuites> - <listeners> - <listener class="JohnKary\PHPUnit\Listener\SpeedTrapListener"/> - </listeners> - <filter> <whitelist addUncoveredFilesFromWhitelist="true"> <directory suffix=".php">./app</directory> From 646b65918dc8ec3307d9dfcba12220dae8899016 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 5 Feb 2017 15:41:23 +0100 Subject: [PATCH 642/709] =?UTF-8?q?Remove=20tests=20since=20they=E2=80=99v?= =?UTF-8?q?e=20been=20changed=20in=20Laravel=205.4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/ExampleTest.php | 27 -- .../Controllers/AccountControllerTest.php | 224 -------------- .../Admin/ConfigurationControllerTest.php | 75 ----- .../Controllers/Admin/HomeControllerTest.php | 44 --- .../Controllers/Admin/UserControllerTest.php | 68 ----- .../Controllers/AttachmentControllerTest.php | 121 -------- .../Controllers/Auth/LoginControllerTest.php | 64 ---- .../Auth/TwoFactorControllerTest.php | 70 ----- .../Controllers/BillControllerTest.php | 179 ----------- .../Controllers/BudgetControllerTest.php | 283 ------------------ .../Controllers/CategoryControllerTest.php | 257 ---------------- .../Chart/AccountControllerTest.php | 154 ---------- .../Controllers/Chart/BillControllerTest.php | 56 ---- .../Chart/BudgetControllerTest.php | 103 ------- .../Chart/CategoryControllerTest.php | 148 --------- .../Chart/CategoryReportControllerTest.php | 82 ----- .../Chart/PiggyBankControllerTest.php | 41 --- .../Chart/ReportControllerTest.php | 61 ---- .../Controllers/CurrencyControllerTest.php | 140 --------- .../Controllers/ExportControllerTest.php | 111 ------- .../Controllers/HelpControllerTest.php | 45 --- .../Controllers/HomeControllerTest.php | 96 ------ .../Controllers/ImportControllerTest.php | 177 ----------- .../Controllers/JsonControllerTest.php | 158 ---------- .../Controllers/NewUserControllerTest.php | 55 ---- .../Controllers/PiggyBankControllerTest.php | 241 --------------- .../Popup/ReportControllerTest.php | 149 --------- .../Controllers/PreferencesControllerTest.php | 87 ------ .../Controllers/ProfileControllerTest.php | 97 ------ .../Report/AccountControllerTest.php | 42 --- .../Report/BalanceControllerTest.php | 41 --- .../Report/BudgetControllerTest.php | 51 ---- .../Report/CategoryControllerTest.php | 61 ---- .../Report/OperationsControllerTest.php | 62 ---- .../Controllers/ReportControllerTest.php | 105 ------- .../Controllers/RuleControllerTest.php | 228 -------------- .../Controllers/RuleGroupControllerTest.php | 173 ----------- .../Controllers/SearchControllerTest.php | 49 --- .../Controllers/TagControllerTest.php | 135 --------- .../Transaction/ConvertControllerTest.php | 121 -------- .../Transaction/MassControllerTest.php | 115 ------- .../Transaction/SingleControllerTest.php | 141 --------- .../Transaction/SplitControllerTest.php | 86 ------ .../Controllers/TransactionControllerTest.php | 89 ------ tests/unit/Models/AccountTest.php | 83 ----- tests/unit/Models/TransactionTypeTest.php | 56 ---- 46 files changed, 5051 deletions(-) delete mode 100644 tests/ExampleTest.php delete mode 100644 tests/acceptance/Controllers/AccountControllerTest.php delete mode 100644 tests/acceptance/Controllers/Admin/ConfigurationControllerTest.php delete mode 100644 tests/acceptance/Controllers/Admin/HomeControllerTest.php delete mode 100644 tests/acceptance/Controllers/Admin/UserControllerTest.php delete mode 100644 tests/acceptance/Controllers/AttachmentControllerTest.php delete mode 100644 tests/acceptance/Controllers/Auth/LoginControllerTest.php delete mode 100644 tests/acceptance/Controllers/Auth/TwoFactorControllerTest.php delete mode 100644 tests/acceptance/Controllers/BillControllerTest.php delete mode 100644 tests/acceptance/Controllers/BudgetControllerTest.php delete mode 100644 tests/acceptance/Controllers/CategoryControllerTest.php delete mode 100644 tests/acceptance/Controllers/Chart/AccountControllerTest.php delete mode 100644 tests/acceptance/Controllers/Chart/BillControllerTest.php delete mode 100644 tests/acceptance/Controllers/Chart/BudgetControllerTest.php delete mode 100644 tests/acceptance/Controllers/Chart/CategoryControllerTest.php delete mode 100644 tests/acceptance/Controllers/Chart/CategoryReportControllerTest.php delete mode 100644 tests/acceptance/Controllers/Chart/PiggyBankControllerTest.php delete mode 100644 tests/acceptance/Controllers/Chart/ReportControllerTest.php delete mode 100644 tests/acceptance/Controllers/CurrencyControllerTest.php delete mode 100644 tests/acceptance/Controllers/ExportControllerTest.php delete mode 100644 tests/acceptance/Controllers/HelpControllerTest.php delete mode 100644 tests/acceptance/Controllers/HomeControllerTest.php delete mode 100644 tests/acceptance/Controllers/ImportControllerTest.php delete mode 100644 tests/acceptance/Controllers/JsonControllerTest.php delete mode 100644 tests/acceptance/Controllers/NewUserControllerTest.php delete mode 100644 tests/acceptance/Controllers/PiggyBankControllerTest.php delete mode 100644 tests/acceptance/Controllers/Popup/ReportControllerTest.php delete mode 100644 tests/acceptance/Controllers/PreferencesControllerTest.php delete mode 100644 tests/acceptance/Controllers/ProfileControllerTest.php delete mode 100644 tests/acceptance/Controllers/Report/AccountControllerTest.php delete mode 100644 tests/acceptance/Controllers/Report/BalanceControllerTest.php delete mode 100644 tests/acceptance/Controllers/Report/BudgetControllerTest.php delete mode 100644 tests/acceptance/Controllers/Report/CategoryControllerTest.php delete mode 100644 tests/acceptance/Controllers/Report/OperationsControllerTest.php delete mode 100644 tests/acceptance/Controllers/ReportControllerTest.php delete mode 100644 tests/acceptance/Controllers/RuleControllerTest.php delete mode 100644 tests/acceptance/Controllers/RuleGroupControllerTest.php delete mode 100644 tests/acceptance/Controllers/SearchControllerTest.php delete mode 100644 tests/acceptance/Controllers/TagControllerTest.php delete mode 100644 tests/acceptance/Controllers/Transaction/ConvertControllerTest.php delete mode 100644 tests/acceptance/Controllers/Transaction/MassControllerTest.php delete mode 100644 tests/acceptance/Controllers/Transaction/SingleControllerTest.php delete mode 100644 tests/acceptance/Controllers/Transaction/SplitControllerTest.php delete mode 100644 tests/acceptance/Controllers/TransactionControllerTest.php delete mode 100644 tests/unit/Models/AccountTest.php delete mode 100644 tests/unit/Models/TransactionTypeTest.php diff --git a/tests/ExampleTest.php b/tests/ExampleTest.php deleted file mode 100644 index 74a0a460d6..0000000000 --- a/tests/ExampleTest.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php -/** - * ExampleTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -/** - * Class ExampleTest - */ -class ExampleTest extends TestCase -{ - /** - * A basic functional test example. - * - * @return void - */ - public function testBasicExample() - { - $this->visit('/') - ->see('Firefly'); - } -} diff --git a/tests/acceptance/Controllers/AccountControllerTest.php b/tests/acceptance/Controllers/AccountControllerTest.php deleted file mode 100644 index e42a5a8d1a..0000000000 --- a/tests/acceptance/Controllers/AccountControllerTest.php +++ /dev/null @@ -1,224 +0,0 @@ -<?php -/** - * AccountControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ -use Carbon\Carbon; -use FireflyIII\Helpers\Collector\JournalCollectorInterface; -use FireflyIII\Models\Account; -use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Account\AccountTaskerInterface; -use Illuminate\Pagination\LengthAwarePaginator; -use Illuminate\Support\Collection; - - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-11-20 at 07:15:07. - */ -class AccountControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers FireflyIII\Http\Controllers\AccountController::create - */ - public function testCreate() - { - $this->be($this->user()); - $this->call('GET', route('accounts.create', ['asset'])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers FireflyIII\Http\Controllers\AccountController::delete - */ - public function testDelete() - { - $this->be($this->user()); - $this->call('GET', route('accounts.delete', [1])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers FireflyIII\Http\Controllers\AccountController::destroy - */ - public function testDestroy() - { - $this->session(['accounts.delete.url' => 'http://localhost/accounts/show/1']); - - $repository = $this->mock(AccountRepositoryInterface::class); - $repository->shouldReceive('find')->withArgs([0])->once()->andReturn(new Account); - $repository->shouldReceive('destroy')->andReturn(true); - - $this->be($this->user()); - $this->call('post', route('accounts.destroy', [1])); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } - - /** - * @covers FireflyIII\Http\Controllers\AccountController::edit - */ - public function testEdit() - { - $this->be($this->user()); - $this->call('GET', route('accounts.edit', [1])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers FireflyIII\Http\Controllers\AccountController::index - * @covers FireflyIII\Http\Controllers\AccountController::isInArray - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testIndex(string $range) - { - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('GET', route('accounts.index', ['asset'])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers FireflyIII\Http\Controllers\AccountController::show - * @covers FireflyIII\Http\Controllers\AccountController::periodEntries - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testShow(string $range) - { - $date = new Carbon; - $this->session(['start' => $date, 'end' => clone $date]); - - $tasker = $this->mock(AccountTaskerInterface::class); - $tasker->shouldReceive('amountOutInPeriod')->withAnyArgs()->andReturn('-1'); - $tasker->shouldReceive('amountInInPeriod')->withAnyArgs()->andReturn('1'); - - // mock repository: - $repository = $this->mock(AccountRepositoryInterface::class); - $repository->shouldReceive('oldestJournalDate')->andReturn(clone $date); - $repository->shouldReceive('getAccountsByType')->andReturn(new Collection); - - - $collector = $this->mock(JournalCollectorInterface::class); - $collector->shouldReceive('setAccounts')->andReturnSelf(); - $collector->shouldReceive('setRange')->andReturnSelf(); - $collector->shouldReceive('setLimit')->andReturnSelf(); - $collector->shouldReceive('setPage')->andReturnSelf(); - $collector->shouldReceive('getPaginatedJournals')->andReturn(new LengthAwarePaginator([], 0, 10)); - - - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('GET', route('accounts.show', [1])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers FireflyIII\Http\Controllers\AccountController::showAll - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testShowAll(string $range) - { - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('GET', route('accounts.show.all', [1])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers FireflyIII\Http\Controllers\AccountController::showByDate - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testShowByDate(string $range) - { - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('GET', route('accounts.show.date', [1, '2016-01-01'])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers FireflyIII\Http\Controllers\AccountController::store - */ - public function testStore() - { - $this->session(['accounts.create.url' => 'http://localhost']); - $this->be($this->user()); - $data = [ - 'name' => 'new account ' . rand(1000, 9999), - 'what' => 'asset', - ]; - - $this->call('post', route('accounts.store', ['asset']), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - - // list should have this new account. - $this->call('GET', route('accounts.index', ['asset'])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - $this->see($data['name']); - } - - /** - * @covers FireflyIII\Http\Controllers\AccountController::update - */ - public function testUpdate() - { - $this->session(['accounts.edit.url' => 'http://localhost']); - $this->be($this->user()); - $data = [ - 'name' => 'updated account ' . rand(1000, 9999), - 'active' => 1, - 'what' => 'asset', - ]; - - $this->call('post', route('accounts.update', [1]), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - - // list should have this new account. - $this->call('GET', route('accounts.index', ['asset'])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - $this->see($data['name']); - } -} diff --git a/tests/acceptance/Controllers/Admin/ConfigurationControllerTest.php b/tests/acceptance/Controllers/Admin/ConfigurationControllerTest.php deleted file mode 100644 index b212b656fd..0000000000 --- a/tests/acceptance/Controllers/Admin/ConfigurationControllerTest.php +++ /dev/null @@ -1,75 +0,0 @@ -<?php -/** - * ConfigurationControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Admin; - -use FireflyIII\Models\Configuration; -use FireflyIII\Support\Facades\FireflyConfig; -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-07 at 18:50:31. - */ -class ConfigurationControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - - } - - /** - * @covers \FireflyIII\Http\Controllers\Admin\ConfigurationController::index - */ - public function testIndex() - { - $this->be($this->user()); - - $falseConfig = new Configuration; - $falseConfig->data = false; - - $trueConfig = new Configuration; - $trueConfig->data = true; - - FireflyConfig::shouldReceive('get')->withArgs(['single_user_mode', true])->once()->andReturn($trueConfig); - FireflyConfig::shouldReceive('get')->withArgs(['is_demo_site', false])->times(2)->andReturn($falseConfig); - - $this->call('GET', route('admin.configuration.index')); - $this->assertResponseStatus(200); - - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\Admin\ConfigurationController::postIndex - */ - public function testPostIndex() - { - $falseConfig = new Configuration; - $falseConfig->data = false; - - FireflyConfig::shouldReceive('get')->withArgs(['is_demo_site', false])->once()->andReturn($falseConfig); - FireflyConfig::shouldReceive('set')->withArgs(['single_user_mode', false])->once(); - FireflyConfig::shouldReceive('set')->withArgs(['is_demo_site', false])->once(); - - $this->be($this->user()); - $this->call('POST', route('admin.configuration.index.post')); - $this->assertSessionHas('success'); - $this->assertResponseStatus(302); - } -} diff --git a/tests/acceptance/Controllers/Admin/HomeControllerTest.php b/tests/acceptance/Controllers/Admin/HomeControllerTest.php deleted file mode 100644 index 8a2ccefb60..0000000000 --- a/tests/acceptance/Controllers/Admin/HomeControllerTest.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php -/** - * HomeControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Admin; - -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-07 at 18:50:31. - */ -class HomeControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\Admin\HomeController::index - */ - public function testIndex() - { - $this->be($this->user()); - $this->call('GET', route('admin.index')); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - -} diff --git a/tests/acceptance/Controllers/Admin/UserControllerTest.php b/tests/acceptance/Controllers/Admin/UserControllerTest.php deleted file mode 100644 index b5e246bc4d..0000000000 --- a/tests/acceptance/Controllers/Admin/UserControllerTest.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php -/** - * UserControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Admin; - -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-07 at 18:50:31. - */ -class UserControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\Admin\UserController::edit - */ - public function testEdit() - { - $this->be($this->user()); - $this->call('GET', route('admin.users.edit', [1])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\Admin\UserController::index - */ - public function testIndex() - { - $this->be($this->user()); - $this->call('GET', route('admin.users')); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\Admin\UserController::show - */ - public function testShow() - { - $this->be($this->user()); - $this->call('GET', route('admin.users.edit', [1])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - -} diff --git a/tests/acceptance/Controllers/AttachmentControllerTest.php b/tests/acceptance/Controllers/AttachmentControllerTest.php deleted file mode 100644 index a2fdbc3d07..0000000000 --- a/tests/acceptance/Controllers/AttachmentControllerTest.php +++ /dev/null @@ -1,121 +0,0 @@ -<?php -/** - * AttachmentControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ -use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface; - - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:38. - */ -class AttachmentControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\AttachmentController::delete - */ - public function testDelete() - { - $this->be($this->user()); - $this->call('GET', route('attachments.delete', [1])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\AttachmentController::destroy - */ - public function testDestroy() - { - $this->session(['attachments.delete.url' => 'http://localhost']); - - $repository = $this->mock(AttachmentRepositoryInterface::class); - $repository->shouldReceive('destroy')->andReturn(true); - $this->be($this->user()); - $this->call('post', route('attachments.destroy', [1])); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } - - /** - * @covers \FireflyIII\Http\Controllers\AttachmentController::download - */ - public function testDownload() - { - $repository = $this->mock(AttachmentRepositoryInterface::class); - $repository->shouldReceive('exists')->once()->andReturn(true); - $repository->shouldReceive('getContent')->once()->andReturn('This is attachment number one.'); - - $this->be($this->user()); - $this->call('GET', route('attachments.download', [1])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('This is attachment number one.'); - } - - /** - * @covers \FireflyIII\Http\Controllers\AttachmentController::edit - */ - public function testEdit() - { - $this->be($this->user()); - $this->call('GET', route('attachments.edit', [1])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\AttachmentController::preview - */ - public function testPreview() - { - $this->be($this->user()); - $this->call('GET', route('attachments.preview', [1])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\AttachmentController::update - */ - public function testUpdate() - { - $this->session(['attachments.edit.url' => 'http://localhost']); - $data = [ - 'title' => 'Some updated title ' . rand(1000, 9999), - 'notes' => '', - 'description' => '', - ]; - - $this->be($this->user()); - $this->call('post', route('attachments.update', [1]), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - - // view should be updated - $this->be($this->user()); - $this->call('GET', route('attachments.edit', [1])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - $this->see($data['title']); - } - -} diff --git a/tests/acceptance/Controllers/Auth/LoginControllerTest.php b/tests/acceptance/Controllers/Auth/LoginControllerTest.php deleted file mode 100644 index 5125dccb98..0000000000 --- a/tests/acceptance/Controllers/Auth/LoginControllerTest.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php -/** - * LoginControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Auth; - -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-07 at 18:50:32. - */ -class LoginControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\Auth\LoginController::login - */ - public function testLogin() - { - $this->visit('/login') - ->type('thegrumpydictator@gmail.com', 'email') - ->type('james', 'password') - ->press('Sign In') - ->seePageIs('/') - ->see('thegrumpydictator@gmail.com'); - } - - /** - * @covers \FireflyIII\Http\Controllers\Auth\LoginController::logout - */ - public function testLogout() - { - $this->visit('/logout') - ->seePageIs('/login') - ->see('Sign in to start your session'); - } - - /** - * @covers \FireflyIII\Http\Controllers\Auth\LoginController::showLoginForm - */ - public function testShowLoginForm() - { - $this->visit('/') - ->seePageIs('/login') - ->see('Sign in to start your session'); - } -} diff --git a/tests/acceptance/Controllers/Auth/TwoFactorControllerTest.php b/tests/acceptance/Controllers/Auth/TwoFactorControllerTest.php deleted file mode 100644 index 5661e5823c..0000000000 --- a/tests/acceptance/Controllers/Auth/TwoFactorControllerTest.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php -/** - * TwoFactorControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Auth; - -use FireflyIII\Models\Preference; -use FireflyIII\Support\Facades\Preferences; -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-07 at 18:50:32. - */ -class TwoFactorControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\Auth\TwoFactorController::index - */ - public function testIndex() - { - $this->be($this->user()); - - $falsePreference = new Preference; - $falsePreference->data = true; - $secretPreference = new Preference; - $secretPreference->data = 'BlablaSeecret'; - Preferences::shouldReceive('get')->withArgs(['twoFactorAuthEnabled', false])->andReturn($falsePreference); - Preferences::shouldReceive('get')->withArgs(['twoFactorAuthSecret', null])->andReturn($secretPreference); - Preferences::shouldReceive('get')->withArgs(['twoFactorAuthSecret'])->andReturn($secretPreference); - $this->call('get', route('two-factor.index')); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Auth\TwoFactorController::lostTwoFactor - */ - public function testLostTwoFactor() - { - $this->be($this->user()); - - $truePreference = new Preference; - $truePreference->data = true; - $secretPreference = new Preference; - $secretPreference->data = 'BlablaSeecret'; - Preferences::shouldReceive('get')->withArgs(['twoFactorAuthEnabled', false])->andReturn($truePreference); - Preferences::shouldReceive('get')->withArgs(['twoFactorAuthSecret', null])->andReturn($secretPreference); - Preferences::shouldReceive('get')->withArgs(['twoFactorAuthSecret'])->andReturn($secretPreference); - $this->call('get', route('two-factor.lost')); - $this->assertResponseStatus(200); - } - -} diff --git a/tests/acceptance/Controllers/BillControllerTest.php b/tests/acceptance/Controllers/BillControllerTest.php deleted file mode 100644 index 60a8388697..0000000000 --- a/tests/acceptance/Controllers/BillControllerTest.php +++ /dev/null @@ -1,179 +0,0 @@ -<?php -/** - * BillControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ -use FireflyIII\Repositories\Bill\BillRepositoryInterface; -use Illuminate\Support\Collection; - - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:40. - */ -class BillControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\BillController::create - */ - public function testCreate() - { - $this->be($this->user()); - $this->call('GET', route('bills.create')); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\BillController::delete - */ - public function testDelete() - { - $this->be($this->user()); - $this->call('GET', route('bills.delete', [1])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\BillController::destroy - */ - public function testDestroy() - { - $repository = $this->mock(BillRepositoryInterface::class); - $repository->shouldReceive('destroy')->andReturn(true); - - $this->session(['bills.delete.url' => 'http://localhost']); - $this->be($this->user()); - $this->call('post', route('bills.destroy', [1])); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } - - /** - * @covers \FireflyIII\Http\Controllers\BillController::edit - */ - public function testEdit() - { - $this->be($this->user()); - $this->call('GET', route('bills.edit', [1])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\BillController::index - */ - public function testIndex() - { - $this->be($this->user()); - $this->call('GET', route('bills.index')); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\BillController::rescan - */ - public function testRescan() - { - $repository = $this->mock(BillRepositoryInterface::class); - $repository->shouldReceive('getPossiblyRelatedJournals')->once()->andReturn(new Collection); - $this->be($this->user()); - $this->call('GET', route('bills.rescan', [1])); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } - - /** - * @covers \FireflyIII\Http\Controllers\BillController::show - */ - public function testShow() - { - $this->be($this->user()); - $this->call('GET', route('bills.show', [1])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\BillController::store - */ - public function testStore() - { - $data = [ - 'name' => 'New Bill ' . rand(1000, 9999), - 'match' => 'some words', - 'amount_min' => '100', - 'amount_currency_id_amount_min' => 1, - 'amount_currency_id_amount_max' => 1, - 'skip' => 0, - 'amount_max' => '100', - 'date' => '2016-01-01', - 'repeat_freq' => 'monthly', - ]; - $this->session(['bills.create.url' => 'http://localhost']); - $this->be($this->user()); - $this->call('post', route('bills.store'), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - - // list must be updated - $this->be($this->user()); - $this->call('GET', route('bills.index')); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - $this->see($data['name']); - } - - /** - * @covers \FireflyIII\Http\Controllers\BillController::update - */ - public function testUpdate() - { - $data = [ - 'name' => 'Updated Bill ' . rand(1000, 9999), - 'match' => 'some more words', - 'amount_min' => '100', - 'amount_currency_id_amount_min' => 1, - 'amount_currency_id_amount_max' => 1, - 'skip' => 0, - 'amount_max' => '100', - 'date' => '2016-01-01', - 'repeat_freq' => 'monthly', - ]; - $this->session(['bills.edit.url' => 'http://localhost']); - $this->be($this->user()); - $this->call('post', route('bills.update', [1]), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - - // list must be updated - $this->be($this->user()); - $this->call('GET', route('bills.index')); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - $this->see($data['name']); - } - -} diff --git a/tests/acceptance/Controllers/BudgetControllerTest.php b/tests/acceptance/Controllers/BudgetControllerTest.php deleted file mode 100644 index 25f0f3b91a..0000000000 --- a/tests/acceptance/Controllers/BudgetControllerTest.php +++ /dev/null @@ -1,283 +0,0 @@ -<?php -/** - * BudgetControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ -use Carbon\Carbon; -use FireflyIII\Helpers\Collector\JournalCollectorInterface; -use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; -use Illuminate\Pagination\LengthAwarePaginator; -use Illuminate\Support\Collection; - - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:40. - */ -class BudgetControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\BudgetController::amount - */ - public function testAmount() - { - $data = [ - 'amount' => 200, - ]; - $this->be($this->user()); - $this->call('post', route('budgets.amount', [1]), $data); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\BudgetController::create - */ - public function testCreate() - { - $this->be($this->user()); - $this->call('GET', route('budgets.create')); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\BudgetController::delete - */ - public function testDelete() - { - $this->be($this->user()); - $this->call('GET', route('budgets.delete', [1])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\BudgetController::destroy - */ - public function testDestroy() - { - $this->session(['budgets.delete.url' => 'http://localhost']); - - $repository = $this->mock(BudgetRepositoryInterface::class); - $repository->shouldReceive('destroy')->andReturn(true); - - $this->be($this->user()); - $this->call('post', route('budgets.destroy', [1])); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } - - /** - * @covers \FireflyIII\Http\Controllers\BudgetController::edit - */ - public function testEdit() - { - $this->be($this->user()); - $this->call('GET', route('budgets.edit', [1])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\BudgetController::index - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testIndex(string $range) - { - $repository = $this->mock(BudgetRepositoryInterface::class); - $repository->shouldReceive('cleanupBudgets'); - $repository->shouldReceive('getActiveBudgets')->andReturn(new Collection); - $repository->shouldReceive('getInactiveBudgets')->andReturn(new Collection); - $repository->shouldReceive('getAvailableBudget')->andReturn('100.123'); - - - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('GET', route('budgets.index')); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\BudgetController::noBudget - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testNoBudget(string $range) - { - $date = new Carbon(); - $this->session(['start' => $date, 'end' => clone $date]); - - $collector = $this->mock(JournalCollectorInterface::class); - $collector->shouldReceive('setAllAssetAccounts')->andReturnSelf(); - $collector->shouldReceive('setRange')->andReturnSelf(); - $collector->shouldReceive('setLimit')->andReturnSelf(); - $collector->shouldReceive('setPage')->andReturnSelf(); - $collector->shouldReceive('withoutBudget')->andReturnSelf(); - $collector->shouldReceive('withCategoryInformation')->andReturnSelf(); - $collector->shouldReceive('getPaginatedJournals')->andReturn(new LengthAwarePaginator([], 0, 10)); - - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('GET', route('budgets.no-budget')); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\BudgetController::postUpdateIncome - */ - public function testPostUpdateIncome() - { - $data = [ - 'amount' => '200', - ]; - $this->be($this->user()); - $this->call('post', route('budgets.income.post'), $data); - $this->assertResponseStatus(302); - } - - /** - * @covers \FireflyIII\Http\Controllers\BudgetController::show - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testShow(string $range) - { - $date = new Carbon(); - $date->subDay(); - $this->session(['first' => $date]); - - // mock account repository - $accountRepository = $this->mock(AccountRepositoryInterface::class); - $accountRepository->shouldReceive('getAccountsByType')->andReturn(new Collection); - - - // mock budget repository - $budgetRepository = $this->mock(BudgetRepositoryInterface::class); - $budgetRepository->shouldReceive('getBudgetLimits')->andReturn(new Collection); - $budgetRepository->shouldReceive('spentInPeriod')->andReturn('1'); - // 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('withCategoryInformation')->andReturnSelf(); - $collector->shouldReceive('getPaginatedJournals')->andReturn(new LengthAwarePaginator([], 0, 10)); - - - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('GET', route('budgets.show', [1])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\BudgetController::showByBudgetLimit() - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testShowByBudgetLimit(string $range) - { - // 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('withCategoryInformation')->andReturnSelf(); - $collector->shouldReceive('getPaginatedJournals')->andReturn(new LengthAwarePaginator([], 0, 10)); - - - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('GET', route('budgets.show.limit', [1, 1])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\BudgetController::store - */ - public function testStore() - { - $this->session(['budgets.create.url' => 'http://localhost']); - - $data = [ - 'name' => 'New Budget ' . rand(1000, 9999), - ]; - $this->be($this->user()); - $this->call('post', route('budgets.store'), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } - - /** - * @covers \FireflyIII\Http\Controllers\BudgetController::update - */ - public function testUpdate() - { - $this->session(['budgets.edit.url' => 'http://localhost']); - - $data = [ - 'name' => 'Updated Budget ' . rand(1000, 9999), - 'active' => 1, - ]; - $this->be($this->user()); - $this->call('post', route('budgets.update', [1]), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } - - /** - * @covers \FireflyIII\Http\Controllers\BudgetController::updateIncome - */ - public function testUpdateIncome() - { - // must be in list - $this->be($this->user()); - $this->call('GET', route('budgets.income', [1])); - $this->assertResponseStatus(200); - } - -} diff --git a/tests/acceptance/Controllers/CategoryControllerTest.php b/tests/acceptance/Controllers/CategoryControllerTest.php deleted file mode 100644 index da294075d4..0000000000 --- a/tests/acceptance/Controllers/CategoryControllerTest.php +++ /dev/null @@ -1,257 +0,0 @@ -<?php -/** - * CategoryControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ -use Carbon\Carbon; -use FireflyIII\Helpers\Collector\JournalCollectorInterface; -use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Category\CategoryRepositoryInterface; -use Illuminate\Pagination\LengthAwarePaginator; -use Illuminate\Support\Collection; - - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:40. - */ -class CategoryControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\CategoryController::create - */ - public function testCreate() - { - $this->be($this->user()); - $this->call('GET', route('categories.create')); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\CategoryController::delete - */ - public function testDelete() - { - $this->be($this->user()); - $this->call('GET', route('categories.delete', [1])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\CategoryController::destroy - */ - public function testDestroy() - { - $this->session(['categories.delete.url' => 'http://localhost']); - $repository = $this->mock(CategoryRepositoryInterface::class); - $repository->shouldReceive('destroy')->andReturn(true); - - $this->be($this->user()); - $this->call('post', route('categories.destroy', [1])); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } - - /** - * @covers \FireflyIII\Http\Controllers\CategoryController::edit - */ - public function testEdit() - { - $this->be($this->user()); - $this->call('GET', route('categories.edit', [1])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\CategoryController::index - */ - public function testIndex() - { - $this->be($this->user()); - $this->call('GET', route('categories.index')); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\CategoryController::noCategory - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testNoCategory(string $range) - { - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('GET', route('categories.no-category')); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\CategoryController::show - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testShow(string $range) - { - - $collector = $this->mock(JournalCollectorInterface::class); - $accRepository = $this->mock(AccountRepositoryInterface::class); - $catRepository = $this->mock(CategoryRepositoryInterface::class); - - $accRepository->shouldReceive('getAccountsByType')->once()->andReturn(new Collection); - $catRepository->shouldReceive('firstUseDate')->once()->andReturn(new Carbon); - - // collector stuff: - $collector->shouldReceive('setPage')->andReturnSelf()->once(); - $collector->shouldReceive('setLimit')->andReturnSelf()->once(); - $collector->shouldReceive('setAllAssetAccounts')->andReturnSelf()->once(); - $collector->shouldReceive('setRange')->andReturnSelf()->once(); - $collector->shouldReceive('withBudgetInformation')->andReturnSelf()->once(); - $collector->shouldReceive('setCategory')->andReturnSelf()->once(); - $collector->shouldReceive('getPaginatedJournals')->andReturn(new LengthAwarePaginator([], 0, 10))->once(); - - // more repos stuff: - $catRepository->shouldReceive('spentInPeriod')->andReturn('0'); - $catRepository->shouldReceive('earnedInPeriod')->andReturn('0'); - - - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('GET', route('categories.show', [1])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\CategoryController::showAll - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testShowAll(string $range) - { - $collector = $this->mock(JournalCollectorInterface::class); - - // collector stuff: - $collector->shouldReceive('setPage')->andReturnSelf()->once(); - $collector->shouldReceive('setLimit')->andReturnSelf()->once(); - $collector->shouldReceive('setAllAssetAccounts')->andReturnSelf()->once(); - $collector->shouldReceive('withBudgetInformation')->andReturnSelf()->once(); - $collector->shouldReceive('setCategory')->andReturnSelf()->once(); - $collector->shouldReceive('getPaginatedJournals')->andReturn(new LengthAwarePaginator([], 0, 10))->once(); - - - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('GET', route('categories.show.all', [1])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\CategoryController::showByDate - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testShowByDate(string $range) - { - $collector = $this->mock(JournalCollectorInterface::class); - - // collector stuff: - $collector->shouldReceive('setPage')->andReturnSelf()->once(); - $collector->shouldReceive('setLimit')->andReturnSelf()->once(); - $collector->shouldReceive('setAllAssetAccounts')->andReturnSelf()->once(); - $collector->shouldReceive('setRange')->andReturnSelf()->once(); - $collector->shouldReceive('withBudgetInformation')->andReturnSelf()->once(); - $collector->shouldReceive('setCategory')->andReturnSelf()->once(); - $collector->shouldReceive('getPaginatedJournals')->andReturn(new LengthAwarePaginator([], 0, 10))->once(); - - // mock category repository - $repository = $this->mock(CategoryRepositoryInterface::class); - $repository->shouldReceive('firstUseDate')->once()->andReturn(new Carbon); - $repository->shouldReceive('spentInPeriod')->andReturn('-1'); - $repository->shouldReceive('earnedInPeriod')->andReturn('1'); - - - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('GET', route('categories.show.date', [1, '2015-01-01'])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\CategoryController::store - */ - public function testStore() - { - $this->session(['categories.create.url' => 'http://localhost']); - - $data = [ - 'name' => 'New Category ' . rand(1000, 9999), - ]; - $this->be($this->user()); - $this->call('post', route('categories.store'), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - - // must be in list - $this->be($this->user()); - $this->call('GET', route('categories.index')); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - $this->see($data['name']); - } - - /** - * @covers \FireflyIII\Http\Controllers\CategoryController::update - */ - public function testUpdate() - { - $this->session(['categories.edit.url' => 'http://localhost']); - - $data = [ - 'name' => 'Updated Category ' . rand(1000, 9999), - 'active' => 1, - ]; - $this->be($this->user()); - $this->call('post', route('categories.update', [1]), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - - // must be in list - $this->be($this->user()); - $this->call('GET', route('categories.index')); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - $this->see($data['name']); - } - -} diff --git a/tests/acceptance/Controllers/Chart/AccountControllerTest.php b/tests/acceptance/Controllers/Chart/AccountControllerTest.php deleted file mode 100644 index ab19fe167b..0000000000 --- a/tests/acceptance/Controllers/Chart/AccountControllerTest.php +++ /dev/null @@ -1,154 +0,0 @@ -<?php -/** - * AccountControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Chart; - -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-07 at 18:50:33. - */ -class AccountControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\AccountController::expenseAccounts - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testExpenseAccounts(string $range) - { - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('get', route('chart.account.expense')); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\AccountController::expenseBudget - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testExpenseBudget(string $range) - { - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('get', route('chart.account.expense-budget', [1, '20120101', '20120131'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\AccountController::expenseCategory - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testExpenseCategory(string $range) - { - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('get', route('chart.account.expense-category', [1, '20120101', '20120131'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\AccountController::frontpage - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testFrontpage(string $range) - { - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('get', route('chart.account.frontpage')); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\AccountController::incomeCategory - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testIncomeCategory(string $range) - { - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('get', route('chart.account.income-category', [1, '20120101', '20120131'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\AccountController::period - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testPeriod(string $range) - { - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('get', route('chart.account.period', [1, '2012-01-01'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\AccountController::report - */ - public function testReport() - { - $this->be($this->user()); - $this->call('get', route('chart.account.report', ['1', '20120101', '20120131'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\AccountController::revenueAccounts - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testRevenueAccounts(string $range) - { - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('get', route('chart.account.revenue')); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\AccountController::single - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testSingle(string $range) - { - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('get', route('chart.account.single', [1])); - $this->assertResponseStatus(200); - } - -} diff --git a/tests/acceptance/Controllers/Chart/BillControllerTest.php b/tests/acceptance/Controllers/Chart/BillControllerTest.php deleted file mode 100644 index 657c934f28..0000000000 --- a/tests/acceptance/Controllers/Chart/BillControllerTest.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** - * BillControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Chart; - -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:40. - */ -class BillControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\BillController::frontpage - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testFrontpage(string $range) - { - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('get', route('chart.bill.frontpage')); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\BillController::single - */ - public function testSingle() - { - $this->be($this->user()); - $this->call('get', route('chart.bill.single', [1])); - $this->assertResponseStatus(200); - } - -} diff --git a/tests/acceptance/Controllers/Chart/BudgetControllerTest.php b/tests/acceptance/Controllers/Chart/BudgetControllerTest.php deleted file mode 100644 index dd9b49586b..0000000000 --- a/tests/acceptance/Controllers/Chart/BudgetControllerTest.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php -/** - * BudgetControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Chart; - -use Carbon\Carbon; -use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:40. - */ -class BudgetControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\BudgetController::budget - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testBudget(string $range) - { - $budgetRepository = $this->mock(BudgetRepositoryInterface::class); - $budgetRepository->shouldReceive('firstUseDate')->andReturn(new Carbon('2015-01-01')); - $budgetRepository->shouldReceive('spentInPeriod')->andReturn('-100'); - - - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('get', route('chart.budget.budget', [1])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\BudgetController::budgetLimit - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testBudgetLimit(string $range) - { - $budgetRepository = $this->mock(BudgetRepositoryInterface::class); - $budgetRepository->shouldReceive('spentInPeriod')->andReturn('-100'); - - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('get', route('chart.budget.budget-limit', [1,1])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\BudgetController::frontpage - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testFrontpage(string $range) - { - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('get', route('chart.budget.frontpage')); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\BudgetController::period - */ - public function testPeriod() - { - $this->be($this->user()); - $this->call('get', route('chart.budget.period', [1,'1','20120101','20120131'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\BudgetController::periodNoBudget - */ - public function testPeriodNoBudget() - { - $this->be($this->user()); - $this->call('get', route('chart.budget.period.no-budget', ['1','20120101','20120131'])); - $this->assertResponseStatus(200); - } -} diff --git a/tests/acceptance/Controllers/Chart/CategoryControllerTest.php b/tests/acceptance/Controllers/Chart/CategoryControllerTest.php deleted file mode 100644 index 06af5d366c..0000000000 --- a/tests/acceptance/Controllers/Chart/CategoryControllerTest.php +++ /dev/null @@ -1,148 +0,0 @@ -<?php -/** - * CategoryControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Chart; - -use Carbon\Carbon; -use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Category\CategoryRepositoryInterface; -use Illuminate\Support\Collection; -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:40. - */ -class CategoryControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\CategoryController::all - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testAll(string $range) - { - $catRepository = $this->mock(CategoryRepositoryInterface::class); - - $catRepository->shouldReceive('spentInPeriod')->andReturn('0'); - $catRepository->shouldReceive('earnedInPeriod')->andReturn('0'); - $catRepository->shouldReceive('firstUseDate')->andReturn(new Carbon); - - - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('get', route('chart.category.all', [1])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\CategoryController::currentPeriod - * @covers \FireflyIII\Http\Controllers\Chart\CategoryController::makePeriodChart - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testCurrentPeriod(string $range) - { - // this is actually for makePeriodChart - $accountRepository = $this->mock(AccountRepositoryInterface::class); - $categoryRepository = $this->mock(CategoryRepositoryInterface::class); - $account = $this->user()->accounts()->where('account_type_id', 5)->first(); - $accountRepository->shouldReceive('getAccountsByType')->andReturn(new Collection([$account])); - $categoryRepository->shouldReceive('spentInPeriod')->andReturn('0'); - $categoryRepository->shouldReceive('earnedInPeriod')->andReturn('0'); - - - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('get', route('chart.category.current', [1])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\CategoryController::frontpage - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testFrontpage(string $range) - { - $accountRepository = $this->mock(AccountRepositoryInterface::class); - $categoryRepository = $this->mock(CategoryRepositoryInterface::class); - $category = $this->user()->categories()->first(); - $account = $this->user()->accounts()->where('account_type_id', 5)->first(); - // get one category - $categoryRepository->shouldReceive('getCategories')->andReturn(new Collection([$category])); - // get one account - $accountRepository->shouldReceive('getAccountsByType')->andReturn(new Collection([$account])); - // always return zero - $categoryRepository->shouldReceive('spentInPeriod')->andReturn('0'); - $categoryRepository->shouldReceive('spentInPeriodWithoutCategory')->andReturn('0'); - - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('get', route('chart.category.frontpage', [1])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\CategoryController::reportPeriod - */ - public function testReportPeriod() - { - $this->be($this->user()); - $this->call('get', route('chart.category.period', [1, '1', '20120101', '20120131'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\CategoryController::reportPeriodNoCategory - */ - public function testReportPeriodNoCategory() - { - $this->be($this->user()); - $this->call('get', route('chart.category.period.no-category', ['1', '20120101', '20120131'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\CategoryController::specificPeriod - * @covers \FireflyIII\Http\Controllers\Chart\CategoryController::makePeriodChart - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testSpecificPeriod(string $range) - { - $accountRepository = $this->mock(AccountRepositoryInterface::class); - $categoryRepository = $this->mock(CategoryRepositoryInterface::class); - $account = $this->user()->accounts()->where('account_type_id', 5)->first(); - $accountRepository->shouldReceive('getAccountsByType')->andReturn(new Collection([$account])); - $categoryRepository->shouldReceive('spentInPeriod')->andReturn('0'); - $categoryRepository->shouldReceive('earnedInPeriod')->andReturn('0'); - - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('get', route('chart.category.specific', ['1', '2012-01-01'])); - $this->assertResponseStatus(200); - } -} diff --git a/tests/acceptance/Controllers/Chart/CategoryReportControllerTest.php b/tests/acceptance/Controllers/Chart/CategoryReportControllerTest.php deleted file mode 100644 index ba948c67ae..0000000000 --- a/tests/acceptance/Controllers/Chart/CategoryReportControllerTest.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php -/** - * CategoryReportControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Chart; - -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:40. - */ -class CategoryReportControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\CategoryReportController::accountExpense - */ - public function testAccountExpense() - { - $this->be($this->user()); - $this->call('get', route('chart.category.account-expense', ['1', '1', '20120101', '20120131', 0])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\CategoryReportController::accountIncome - */ - public function testAccountIncome() - { - $this->be($this->user()); - $this->call('get', route('chart.category.account-income', ['1', '1', '20120101', '20120131', 0])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\CategoryReportController::categoryExpense - */ - public function testCategoryExpense() - { - $this->be($this->user()); - $this->call('get', route('chart.category.category-expense', ['1', '1', '20120101', '20120131', 0])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\CategoryReportController::categoryIncome - */ - public function testCategoryIncome() - { - $this->be($this->user()); - $this->call('get', route('chart.category.category-income', ['1', '1', '20120101', '20120131', 0])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\CategoryReportController::mainChart - */ - public function testMainChart() - { - $this->be($this->user()); - $this->call('get', route('chart.category.main', ['1', '1', '20120101', '20120131'])); - $this->assertResponseStatus(200); - } - -} diff --git a/tests/acceptance/Controllers/Chart/PiggyBankControllerTest.php b/tests/acceptance/Controllers/Chart/PiggyBankControllerTest.php deleted file mode 100644 index f368213e00..0000000000 --- a/tests/acceptance/Controllers/Chart/PiggyBankControllerTest.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php -/** - * PiggyBankControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Chart; - -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:41. - */ -class PiggyBankControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\PiggyBankController::history - */ - public function testHistory() - { - $this->be($this->user()); - $this->call('get', route('chart.piggy-bank.history', [1])); - $this->assertResponseStatus(200); - } -} diff --git a/tests/acceptance/Controllers/Chart/ReportControllerTest.php b/tests/acceptance/Controllers/Chart/ReportControllerTest.php deleted file mode 100644 index 7b782367bc..0000000000 --- a/tests/acceptance/Controllers/Chart/ReportControllerTest.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php -/** - * ReportControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Chart; - -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:41. - */ -class ReportControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\ReportController::netWorth - */ - public function testNetWorth() - { - $this->be($this->user()); - $this->call('get', route('chart.report.net-worth', [1, '20120101', '20120131'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\ReportController::operations - */ - public function testOperations() - { - $this->be($this->user()); - $this->call('get', route('chart.report.operations', [1, '20120101', '20120131'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Chart\ReportController::sum - */ - public function testSum() - { - $this->be($this->user()); - $this->call('get', route('chart.report.sum', [1, '20120101', '20120131'])); - $this->assertResponseStatus(200); - } -} diff --git a/tests/acceptance/Controllers/CurrencyControllerTest.php b/tests/acceptance/Controllers/CurrencyControllerTest.php deleted file mode 100644 index 36d14bacde..0000000000 --- a/tests/acceptance/Controllers/CurrencyControllerTest.php +++ /dev/null @@ -1,140 +0,0 @@ -<?php -/** - * CurrencyControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; - - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:41. - */ -class CurrencyControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\CurrencyController::create - */ - public function testCreate() - { - $this->be($this->user()); - $this->call('GET', route('currencies.create')); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\CurrencyController::defaultCurrency - */ - public function testDefaultCurrency() - { - $this->be($this->user()); - $this->call('GET', route('currencies.default', [1])); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } - - /** - * @covers \FireflyIII\Http\Controllers\CurrencyController::delete - */ - public function testDelete() - { - $this->be($this->user()); - $this->call('GET', route('currencies.delete', [2])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\CurrencyController::destroy - */ - public function testDestroy() - { - $this->session(['currencies.delete.url' => 'http://localhost']); - $repository = $this->mock(CurrencyRepositoryInterface::class); - $repository->shouldReceive('canDeleteCurrency')->andReturn(true); - $repository->shouldReceive('destroy')->andReturn(true); - - $this->be($this->user()); - $this->call('post', route('currencies.destroy', [1])); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } - - /** - * @covers \FireflyIII\Http\Controllers\CurrencyController::edit - */ - public function testEdit() - { - $this->be($this->user()); - $this->call('GET', route('currencies.edit', [2])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\CurrencyController::index - */ - public function testIndex() - { - $this->be($this->user()); - $this->call('GET', route('currencies.index')); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\CurrencyController::store - */ - public function testStore() - { - $this->session(['currencies.create.url' => 'http://localhost']); - $data = [ - 'name' => 'XX', - 'code' => 'XXX', - 'symbol' => 'x', - 'decimal_places' => 2, - ]; - $this->be($this->user()); - $this->call('post', route('currencies.store'), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } - - /** - * @covers \FireflyIII\Http\Controllers\CurrencyController::update - */ - public function testUpdate() - { - $this->session(['currencies.edit.url' => 'http://localhost']); - $data = [ - 'name' => 'XA', - 'code' => 'XAX', - 'symbol' => 'a', - 'decimal_places' => 2, - ]; - $this->be($this->user()); - $this->call('post', route('currencies.update', [2]), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } -} diff --git a/tests/acceptance/Controllers/ExportControllerTest.php b/tests/acceptance/Controllers/ExportControllerTest.php deleted file mode 100644 index 39a7bd4ebc..0000000000 --- a/tests/acceptance/Controllers/ExportControllerTest.php +++ /dev/null @@ -1,111 +0,0 @@ -<?php -/** - * ExportControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ -use Carbon\Carbon; -use FireflyIII\Export\Processor; -use FireflyIII\Export\ProcessorInterface; -use FireflyIII\Models\ExportJob; -use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface; -use Illuminate\Support\Collection; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:41. - */ -class ExportControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\ExportController::download - */ - public function testDownload() - { - $repository = $this->mock(ExportJobRepositoryInterface::class); - $repository->shouldReceive('exists')->once()->andReturn(true); - $repository->shouldReceive('getContent')->once()->andReturn('Some content beep boop'); - - $this->be($this->user()); - $this->call('GET', route('export.download', ['testExport'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\ExportController::getStatus - */ - public function testGetStatus() - { - $this->be($this->user()); - $this->call('GET', route('export.status', ['testExport'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\ExportController::index - */ - public function testIndex() - { - $this->be($this->user()); - $this->call('GET', route('export.index')); - $this->assertResponseStatus(200); - - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\ExportController::postIndex - */ - public function testPostIndex() - { - $this->session( - ['first' => new Carbon('2014-01-01')] - ); - - - $data = [ - - 'export_start_range' => '2015-01-01', - 'export_end_range' => '2015-01-21', - 'exportFormat' => 'csv', - 'accounts' => [1], - 'job' => 'testExport', - ]; - - $accountRepository = $this->mock(AccountRepositoryInterface::class); - $accountRepository->shouldReceive('getAccountsById')->withArgs([$data['accounts']])->andReturn(new Collection); - - $processor = $this->mock(ProcessorInterface::class); - $processor->shouldReceive('collectJournals')->once(); - $processor->shouldReceive('convertJournals')->once(); - $processor->shouldReceive('exportJournals')->once(); - $processor->shouldReceive('createZipFile')->once(); - - $repository = $this->mock(ExportJobRepositoryInterface::class); - $repository->shouldReceive('changeStatus')->andReturn(true); - $repository->shouldReceive('findByKey')->andReturn(new ExportJob); - - $this->be($this->user()); - - $this->call('post', route('export.export'), $data); - $this->assertResponseStatus(200); - $this->see('ok'); - } - -} diff --git a/tests/acceptance/Controllers/HelpControllerTest.php b/tests/acceptance/Controllers/HelpControllerTest.php deleted file mode 100644 index 9acadb6482..0000000000 --- a/tests/acceptance/Controllers/HelpControllerTest.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php -/** - * HelpControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ -use FireflyIII\Helpers\Help\HelpInterface; - - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:41. - */ -class HelpControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\HelpController::show - */ - public function testShow() - { - $help = $this->mock(HelpInterface::class); - $help->shouldReceive('hasRoute')->andReturn(true)->once(); - $help->shouldReceive('inCache')->andReturn(false)->once(); - $help->shouldReceive('getFromGithub')->andReturn('Help content here.')->once(); - $help->shouldReceive('putInCache')->once(); - - $this->be($this->user()); - $this->call('GET', route('help.show', ['index'])); - $this->assertResponseStatus(200); - } -} diff --git a/tests/acceptance/Controllers/HomeControllerTest.php b/tests/acceptance/Controllers/HomeControllerTest.php deleted file mode 100644 index 5babd78d5c..0000000000 --- a/tests/acceptance/Controllers/HomeControllerTest.php +++ /dev/null @@ -1,96 +0,0 @@ -<?php - -/** - * HomeControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ -class HomeControllerTest extends TestCase -{ - - /** - * @covers FireflyIII\Http\Controllers\HomeController::dateRange - * @covers FireflyIII\Http\Controllers\HomeController::__construct - */ - public function testDateRange() - { - - $this->be($this->user()); - - $args = [ - 'start' => '2012-01-01', - 'end' => '2012-04-01', - ]; - - $this->call('POST', route('daterange'), $args); - $this->assertResponseStatus(200); - $this->assertSessionHas('warning', '91 days of data may take a while to load.'); - } - - /** - * @covers FireflyIII\Http\Controllers\HomeController::displayError - */ - public function testDisplayError() - { - $this->be($this->user()); - $this->call('GET', route('error')); - $this->assertResponseStatus(500); - } - - /** - * @covers FireflyIII\Http\Controllers\HomeController::flush - */ - public function testFlush() - { - $this->be($this->user()); - $this->call('GET', route('flush')); - $this->assertResponseStatus(302); - } - - /** - * @covers FireflyIII\Http\Controllers\HomeController::index - * @covers FireflyIII\Http\Controllers\Controller::__construct - * @dataProvider dateRangeProvider - * - * @param $range - */ - public function testIndex(string $range) - { - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('GET', route('index')); - $this->assertResponseStatus(200); - } - - /** - * @covers FireflyIII\Http\Controllers\HomeController::routes - * @dataProvider dateRangeProvider - * - * @param string $range - */ - public function testRoutes(string $range) - { - $this->be($this->user()); - $this->changeDateRange($this->user(), $range); - $this->call('GET', route('all-routes')); - $this->assertResponseStatus(200); - } - - /** - * @covers FireflyIII\Http\Controllers\HomeController::testFlash - */ - public function testTestFlash() - { - $this->be($this->user()); - $this->call('GET', route('test-flash')); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - $this->assertSessionHas('info'); - $this->assertSessionHas('warning'); - $this->assertSessionHas('error'); - } -} diff --git a/tests/acceptance/Controllers/ImportControllerTest.php b/tests/acceptance/Controllers/ImportControllerTest.php deleted file mode 100644 index 4c4c4dda3d..0000000000 --- a/tests/acceptance/Controllers/ImportControllerTest.php +++ /dev/null @@ -1,177 +0,0 @@ -<?php -/** - * ImportControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ -use FireflyIII\Import\Setup\CsvSetup; -use Illuminate\Http\UploadedFile; -use FireflyIII\Import\ImportProcedureInterface; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:41. - */ -class ImportControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\ImportController::complete - */ - public function testComplete() - { - $this->be($this->user()); - $this->call('GET', route('import.complete', ['complete'])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\ImportController::configure - */ - public function testConfigure() - { - $this->be($this->user()); - $this->call('GET', route('import.configure', ['configure'])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\ImportController::download - */ - public function testDownload() - { - $this->be($this->user()); - $this->call('GET', route('import.download', ['configure'])); - $this->assertResponseStatus(200); - $this->see('[]'); - } - - /** - * @covers \FireflyIII\Http\Controllers\ImportController::finished - */ - public function testFinished() - { - $this->be($this->user()); - $this->call('GET', route('import.finished', ['finished'])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\ImportController::index - */ - public function testIndex() - { - $this->be($this->user()); - $this->call('GET', route('import.index')); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\ImportController::json - */ - public function testJson() - { - $this->be($this->user()); - $this->call('GET', route('import.json', ['configure'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\ImportController::postConfigure - */ - public function testPostConfigure() - { - $importer = $this->mock(CsvSetup::class); - $importer->shouldReceive('setJob')->once(); - $importer->shouldReceive('saveImportConfiguration')->once(); - - $data = []; - $this->be($this->user()); - $this->call('post', route('import.process-configuration', ['p-configure']), $data); - $this->assertResponseStatus(302); - $this->assertRedirectedToRoute('import.settings', ['p-configure']); - } - - /** - * @covers \FireflyIII\Http\Controllers\ImportController::postSettings - */ - public function testPostSettings() - { - $importer = $this->mock(CsvSetup::class); - $importer->shouldReceive('setJob')->once(); - $importer->shouldReceive('storeSettings')->once(); - $data = []; - $this->be($this->user()); - $this->call('post', route('import.post-settings', ['p-settings']), $data); - $this->assertResponseStatus(302); - $this->assertRedirectedToRoute('import.settings', ['p-settings']); - } - - /** - * @covers \FireflyIII\Http\Controllers\ImportController::settings - */ - public function testSettings() - { - $importer = $this->mock(CsvSetup::class); - $importer->shouldReceive('setJob')->once(); - $importer->shouldReceive('requireUserSettings')->once()->andReturn(false); - $this->be($this->user()); - $this->call('get', route('import.settings', ['settings'])); - $this->assertResponseStatus(302); - $this->assertRedirectedToRoute('import.complete', ['settings']); - } - - /** - * @covers \FireflyIII\Http\Controllers\ImportController::start - */ - public function testStart() - { - /** @var ImportProcedureInterface $procedure */ - $procedure = $this->mock(ImportProcedureInterface::class); - - $procedure->shouldReceive('runImport'); - - $this->be($this->user()); - $this->call('post', route('import.start', ['complete'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\ImportController::status - * Implement testStatus(). - */ - public function testStatus() - { - // complete - $this->be($this->user()); - $this->call('get', route('import.status', ['complete'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\ImportController::upload - */ - public function testUpload() - { - $path = resource_path('stubs/csv.csv'); - $file = new UploadedFile($path, 'upload.csv', filesize($path), 'text/csv', null, true); - $this->call('POST', route('import.upload'), [], [], ['import_file' => $file], ['Accept' => 'application/json']); - $this->assertResponseStatus(302); - } -} diff --git a/tests/acceptance/Controllers/JsonControllerTest.php b/tests/acceptance/Controllers/JsonControllerTest.php deleted file mode 100644 index bcd30afada..0000000000 --- a/tests/acceptance/Controllers/JsonControllerTest.php +++ /dev/null @@ -1,158 +0,0 @@ -<?php -/** - * JsonControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:42. - */ -class JsonControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\JsonController::action - */ - public function testAction() - { - $this->be($this->user()); - $this->call('get', route('json.action')); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\JsonController::boxBillsPaid - */ - public function testBoxBillsPaid() - { - $this->be($this->user()); - $this->call('get', route('json.box.paid')); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\JsonController::boxBillsUnpaid - */ - public function testBoxBillsUnpaid() - { - $this->be($this->user()); - $this->call('get', route('json.box.unpaid')); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\JsonController::boxIn - */ - public function testBoxIn() - { - $this->be($this->user()); - $this->call('get', route('json.box.in')); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\JsonController::boxOut - */ - public function testBoxOut() - { - $this->be($this->user()); - $this->call('get', route('json.box.out')); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\JsonController::categories - */ - public function testCategories() - { - $this->be($this->user()); - $this->call('get', route('json.categories')); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\JsonController::endTour - */ - public function testEndTour() - { - $this->be($this->user()); - $this->call('post', route('json.end-tour')); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\JsonController::expenseAccounts - */ - public function testExpenseAccounts() - { - $this->be($this->user()); - $this->call('get', route('json.expense-accounts')); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\JsonController::revenueAccounts - */ - public function testRevenueAccounts() - { - $this->be($this->user()); - $this->call('get', route('json.revenue-accounts')); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\JsonController::tags - */ - public function testTags() - { - $this->be($this->user()); - $this->call('get', route('json.tags')); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\JsonController::tour - */ - public function testTour() - { - $this->be($this->user()); - $this->call('get', route('json.tour')); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\JsonController::transactionJournals - */ - public function testTransactionJournals() - { - $this->be($this->user()); - $this->call('get', route('json.transaction-journals', ['deposit'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\JsonController::trigger - */ - public function testTrigger() - { - $this->be($this->user()); - $this->call('get', route('json.trigger')); - $this->assertResponseStatus(200); - } -} diff --git a/tests/acceptance/Controllers/NewUserControllerTest.php b/tests/acceptance/Controllers/NewUserControllerTest.php deleted file mode 100644 index 7c98ec5583..0000000000 --- a/tests/acceptance/Controllers/NewUserControllerTest.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php -/** - * NewUserControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:42. - */ -class NewUserControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\NewUserController::index - */ - public function testIndex() - { - $this->be($this->emptyUser()); - $this->call('get', route('new-user.index')); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\NewUserController::submit - */ - public function testSubmit() - { - $data = [ - 'bank_name' => 'New bank', - 'bank_balance' => 100, - ]; - $this->be($this->emptyUser()); - $this->call('post', route('new-user.submit'), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } - -} diff --git a/tests/acceptance/Controllers/PiggyBankControllerTest.php b/tests/acceptance/Controllers/PiggyBankControllerTest.php deleted file mode 100644 index d480600d21..0000000000 --- a/tests/acceptance/Controllers/PiggyBankControllerTest.php +++ /dev/null @@ -1,241 +0,0 @@ -<?php -/** - * PiggyBankControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ -use FireflyIII\Models\PiggyBank; -use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; - - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:42. - */ -class PiggyBankControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\PiggyBankController::add - */ - public function testAdd() - { - $this->be($this->user()); - $this->call('get', route('piggy-banks.add', [1])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\PiggyBankController::addMobile - */ - public function testAddMobile() - { - $this->be($this->user()); - $this->call('get', route('piggy-banks.add-money-mobile', [1])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\PiggyBankController::create - */ - public function testCreate() - { - $this->be($this->user()); - $this->call('get', route('piggy-banks.create')); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\PiggyBankController::delete - */ - public function testDelete() - { - $this->be($this->user()); - $this->call('get', route('piggy-banks.delete', [1])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\PiggyBankController::destroy - */ - public function testDestroy() - { - $repository = $this->mock(PiggyBankRepositoryInterface::class); - $repository->shouldReceive('destroy')->andReturn(true); - - $this->session(['piggy-banks.delete.url' => 'http://localhost']); - $this->be($this->user()); - $this->call('post', route('piggy-banks.destroy', [2])); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - $this->assertRedirectedToRoute('index'); - } - - /** - * @covers \FireflyIII\Http\Controllers\PiggyBankController::edit - */ - public function testEdit() - { - $this->be($this->user()); - $this->call('get', route('piggy-banks.edit', [1])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\PiggyBankController::index - */ - public function testIndex() - { - $this->be($this->user()); - $this->call('get', route('piggy-banks.index')); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\PiggyBankController::order - */ - public function testOrder() - { - $this->be($this->user()); - $this->call('post', route('piggy-banks.order', [1, 2])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\PiggyBankController::postAdd - */ - public function testPostAdd() - { - $data = ['amount' => '1.123']; - $this->be($this->user()); - $this->call('post', route('piggy-banks.add', [1]), $data); - $this->assertResponseStatus(302); - $this->assertRedirectedToRoute('piggy-banks.index'); - $this->assertSessionHas('success'); - } - - /** - * Add the exact amount to fill a piggy bank - * - * @covers \FireflyIII\Http\Controllers\PiggyBankController::postAdd - */ - public function testPostAddExact() - { - // find a piggy with current amount = 0. - $piggy = PiggyBank::leftJoin('piggy_bank_repetitions', 'piggy_bank_repetitions.piggy_bank_id', '=', 'piggy_banks.id') - ->where('currentamount', 0) - ->first(['piggy_banks.id', 'targetamount']); - - - $data = ['amount' => strval($piggy->targetamount)]; - $this->be($this->user()); - $this->call('post', route('piggy-banks.add', [$piggy->id]), $data); - $this->assertResponseStatus(302); - $this->assertRedirectedToRoute('piggy-banks.index'); - $this->assertSessionHas('success'); - } - - /** - * @covers \FireflyIII\Http\Controllers\PiggyBankController::postRemove - */ - public function testPostRemove() - { - $data = ['amount' => '1.123']; - $this->be($this->user()); - $this->call('post', route('piggy-banks.remove', [1]), $data); - $this->assertResponseStatus(302); - $this->assertRedirectedToRoute('piggy-banks.index'); - $this->assertSessionHas('success'); - } - - /** - * @covers \FireflyIII\Http\Controllers\PiggyBankController::remove - */ - public function testRemove() - { - $this->be($this->user()); - $this->call('get', route('piggy-banks.remove', [1])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\PiggyBankController::removeMobile - */ - public function testRemoveMobile() - { - $this->be($this->user()); - $this->call('get', route('piggy-banks.remove-money-mobile', [1])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\PiggyBankController::show - */ - public function testShow() - { - $this->be($this->user()); - $this->call('get', route('piggy-banks.show', [1])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\PiggyBankController::store - */ - public function testStore() - { - $this->session(['piggy-banks.create.url' => 'http://localhost']); - $data = [ - 'name' => 'Piggy ' . rand(999, 10000), - 'targetamount' => '100.123', - 'account_id' => 2, - 'amount_currency_id_targetamount' => 1, - - ]; - $this->be($this->user()); - $this->call('post', route('piggy-banks.store'), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - $this->assertRedirectedToRoute('index'); - } - - /** - * @covers \FireflyIII\Http\Controllers\PiggyBankController::update - */ - public function testUpdate() - { - $this->session(['piggy-banks.edit.url' => 'http://localhost']); - $data = [ - 'name' => 'Updated Piggy ' . rand(999, 10000), - 'targetamount' => '100.123', - 'account_id' => 2, - 'amount_currency_id_targetamount' => 1, - - ]; - $this->be($this->user()); - $this->call('post', route('piggy-banks.update', [3]), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - $this->assertRedirectedToRoute('index'); - } - -} diff --git a/tests/acceptance/Controllers/Popup/ReportControllerTest.php b/tests/acceptance/Controllers/Popup/ReportControllerTest.php deleted file mode 100644 index e601cc167e..0000000000 --- a/tests/acceptance/Controllers/Popup/ReportControllerTest.php +++ /dev/null @@ -1,149 +0,0 @@ -<?php -/** - * ReportControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Popup; - -use Carbon\Carbon; -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:42. - */ -class ReportControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\Popup\ReportController::general - */ - public function testBalanceAmount() - { - - $this->be($this->user()); - $arguments = [ - 'attributes' => [ - 'location' => 'balance-amount', - 'startDate' => Carbon::now()->startOfMonth()->format('Ymd'), - 'endDate' => Carbon::now()->endOfMonth()->format('Ymd'), - 'accounts' => 1, - 'accountId' => 1, - 'categoryId' => 1, - 'budgetId' => 1, - 'role' => 3, // diff role, is complicated. - ], - ]; - $uri = route('popup.general') . '?' . http_build_query($arguments); - $this->call('get', $uri); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Popup\ReportController::general - */ - public function testBudgetSpentAmount() - { - - $this->be($this->user()); - $arguments = [ - 'attributes' => [ - 'location' => 'budget-spent-amount', - 'startDate' => Carbon::now()->startOfMonth()->format('Ymd'), - 'endDate' => Carbon::now()->endOfMonth()->format('Ymd'), - 'accounts' => 1, - 'accountId' => 1, - 'categoryId' => 1, - 'budgetId' => 1, - ], - ]; - $uri = route('popup.general') . '?' . http_build_query($arguments); - $this->call('get', $uri); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Popup\ReportController::general - */ - public function testCategoryEntry() - { - - $this->be($this->user()); - $arguments = [ - 'attributes' => [ - 'location' => 'category-entry', - 'startDate' => Carbon::now()->startOfMonth()->format('Ymd'), - 'endDate' => Carbon::now()->endOfMonth()->format('Ymd'), - 'accounts' => 1, - 'accountId' => 1, - 'categoryId' => 1, - 'budgetId' => 1, - ], - ]; - $uri = route('popup.general') . '?' . http_build_query($arguments); - $this->call('get', $uri); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Popup\ReportController::general - */ - public function testExpenseEntry() - { - - $this->be($this->user()); - $arguments = [ - 'attributes' => [ - 'location' => 'expense-entry', - 'startDate' => Carbon::now()->startOfMonth()->format('Ymd'), - 'endDate' => Carbon::now()->endOfMonth()->format('Ymd'), - 'accounts' => 1, - 'accountId' => 1, - 'categoryId' => 1, - 'budgetId' => 1, - ], - ]; - $uri = route('popup.general') . '?' . http_build_query($arguments); - $this->call('get', $uri); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Popup\ReportController::general - */ - public function testIncomeEntry() - { - - $this->be($this->user()); - $arguments = [ - 'attributes' => [ - 'location' => 'income-entry', - 'startDate' => Carbon::now()->startOfMonth()->format('Ymd'), - 'endDate' => Carbon::now()->endOfMonth()->format('Ymd'), - 'accounts' => 1, - 'accountId' => 1, - 'categoryId' => 1, - 'budgetId' => 1, - ], - ]; - $uri = route('popup.general') . '?' . http_build_query($arguments); - $this->call('get', $uri); - $this->assertResponseStatus(200); - } - -} diff --git a/tests/acceptance/Controllers/PreferencesControllerTest.php b/tests/acceptance/Controllers/PreferencesControllerTest.php deleted file mode 100644 index 782453bd0d..0000000000 --- a/tests/acceptance/Controllers/PreferencesControllerTest.php +++ /dev/null @@ -1,87 +0,0 @@ -<?php -/** - * PreferencesControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:42. - */ -class PreferencesControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\PreferencesController::code - */ - public function testCode() - { - $this->be($this->user()); - $this->call('get', route('preferences.code')); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\PreferencesController::deleteCode - */ - public function testDeleteCode() - { - $this->be($this->user()); - $this->call('get', route('preferences.delete-code')); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - $this->assertSessionHas('info'); - $this->assertRedirectedToRoute('preferences.index'); - } - - /** - * @covers \FireflyIII\Http\Controllers\PreferencesController::index - */ - public function testIndex() - { - $this->be($this->user()); - $this->call('get', route('preferences.index')); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\PreferencesController::postIndex - */ - public function testPostIndex() - { - $data = [ - 'fiscalYearStart' => '2016-01-01', - 'frontPageAccounts' => [], - 'viewRange' => '1M', - 'customFiscalYear' => 0, - 'showDepositsFrontpage' => 0, - 'transactionPageSize' => 100, - 'twoFactorAuthEnabled' => 0, - 'language' => 'en_US', - 'tj' => [], - ]; - - $this->be($this->user()); - $this->call('post', route('preferences.update'), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - $this->assertRedirectedToRoute('preferences.index'); - } -} diff --git a/tests/acceptance/Controllers/ProfileControllerTest.php b/tests/acceptance/Controllers/ProfileControllerTest.php deleted file mode 100644 index f11a4556a0..0000000000 --- a/tests/acceptance/Controllers/ProfileControllerTest.php +++ /dev/null @@ -1,97 +0,0 @@ -<?php -/** - * ProfileControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ -use FireflyIII\Repositories\User\UserRepositoryInterface; - - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:42. - */ -class ProfileControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\ProfileController::changePassword - */ - public function testChangePassword() - { - $this->be($this->user()); - $this->call('get', route('profile.change-password')); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\ProfileController::deleteAccount - */ - public function testDeleteAccount() - { - $this->be($this->user()); - $this->call('get', route('profile.delete-account')); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\ProfileController::index - */ - public function testIndex() - { - $this->be($this->user()); - $this->call('get', route('profile.index')); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\ProfileController::postChangePassword - */ - public function testPostChangePassword() - { - $repository = $this->mock(UserRepositoryInterface::class); - $repository->shouldReceive('changePassword'); - - $data = [ - 'current_password' => 'james', - 'new_password' => 'james2', - 'new_password_confirmation' => 'james2', - ]; - $this->be($this->user()); - $this->call('post', route('profile.change-password.post'), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } - - /** - * @covers \FireflyIII\Http\Controllers\ProfileController::postDeleteAccount - */ - public function testPostDeleteAccount() - { - $repository = $this->mock(UserRepositoryInterface::class); - $repository->shouldReceive('destroy'); - $data = [ - 'password' => 'james', - ]; - $this->be($this->user()); - $this->call('post', route('profile.delete-account.post'), $data); - $this->assertResponseStatus(302); - $this->assertRedirectedToRoute('index'); - } -} diff --git a/tests/acceptance/Controllers/Report/AccountControllerTest.php b/tests/acceptance/Controllers/Report/AccountControllerTest.php deleted file mode 100644 index 40a1968bcf..0000000000 --- a/tests/acceptance/Controllers/Report/AccountControllerTest.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php -/** - * AccountControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Report; - -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:42. - */ -class AccountControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\Report\AccountController::general - */ - public function testGeneral() - { - $this->be($this->user()); - $this->call('get', route('report-data.account.general', ['1', '20120101', '20120131'])); - $this->assertResponseStatus(200); - } - -} diff --git a/tests/acceptance/Controllers/Report/BalanceControllerTest.php b/tests/acceptance/Controllers/Report/BalanceControllerTest.php deleted file mode 100644 index aadfa39ec7..0000000000 --- a/tests/acceptance/Controllers/Report/BalanceControllerTest.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php -/** - * BalanceControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Report; - -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:42. - */ -class BalanceControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\Report\BalanceController::general - */ - public function testGeneral() - { - $this->be($this->user()); - $this->call('get', route('report-data.balance.general', ['1', '20120101', '20120131'])); - $this->assertResponseStatus(200); - } -} diff --git a/tests/acceptance/Controllers/Report/BudgetControllerTest.php b/tests/acceptance/Controllers/Report/BudgetControllerTest.php deleted file mode 100644 index e13c2bf47c..0000000000 --- a/tests/acceptance/Controllers/Report/BudgetControllerTest.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php -/** - * BudgetControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Report; - -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:43. - */ -class BudgetControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\Report\BudgetController::general - */ - public function testGeneral() - { - $this->be($this->user()); - $this->call('get', route('report-data.budget.general', ['1', '20120101', '20120131'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Report\BudgetController::period - */ - public function testPeriod() - { - $this->be($this->user()); - $this->call('get', route('report-data.budget.period', ['1', '20120101', '20120131'])); - $this->assertResponseStatus(200); - } -} diff --git a/tests/acceptance/Controllers/Report/CategoryControllerTest.php b/tests/acceptance/Controllers/Report/CategoryControllerTest.php deleted file mode 100644 index 44f12f599d..0000000000 --- a/tests/acceptance/Controllers/Report/CategoryControllerTest.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php -/** - * CategoryControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Report; - -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:43. - */ -class CategoryControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\Report\CategoryController::expenses - */ - public function testExpenses() - { - $this->be($this->user()); - $this->call('get', route('report-data.category.expenses', ['1', '20120101', '20120131'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Report\CategoryController::income - */ - public function testIncome() - { - $this->be($this->user()); - $this->call('get', route('report-data.category.income', ['1', '20120101', '20120131'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Report\CategoryController::operations - */ - public function testOperations() - { - $this->be($this->user()); - $this->call('get', route('report-data.category.operations', ['1', '20120101', '20120131'])); - $this->assertResponseStatus(200); - } -} diff --git a/tests/acceptance/Controllers/Report/OperationsControllerTest.php b/tests/acceptance/Controllers/Report/OperationsControllerTest.php deleted file mode 100644 index 027bc2a0e9..0000000000 --- a/tests/acceptance/Controllers/Report/OperationsControllerTest.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php -/** - * OperationsControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Report; - -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:43. - */ -class OperationsControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\Report\OperationsController::expenses - */ - public function testExpenses() - { - $this->be($this->user()); - $this->call('get', route('report-data.operations.expenses', ['1', '20120101', '20120131'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Report\OperationsController::income - */ - public function testIncome() - { - $this->be($this->user()); - $this->call('get', route('report-data.operations.income', ['1', '20120101', '20120131'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\Report\OperationsController::operations - */ - public function testOperations() - { - $this->be($this->user()); - $this->call('get', route('report-data.operations.operations', ['1', '20120101', '20120131'])); - $this->assertResponseStatus(200); - } - -} diff --git a/tests/acceptance/Controllers/ReportControllerTest.php b/tests/acceptance/Controllers/ReportControllerTest.php deleted file mode 100644 index d975bb4581..0000000000 --- a/tests/acceptance/Controllers/ReportControllerTest.php +++ /dev/null @@ -1,105 +0,0 @@ -<?php -/** - * ReportControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ -use FireflyIII\Generator\Report\Audit\MonthReportGenerator as AuditGenerator; -use FireflyIII\Generator\Report\ReportGeneratorInterface; - - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:43. - */ -class ReportControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\ReportController::auditReport - */ - public function testAuditReport() - { - $this->be($this->user()); - $this->call('get', route('reports.report.audit', [1, '20160101', '20160131'])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\ReportController::budgetReport - */ - public function testBudgetReport() - { - $this->be($this->user()); - $this->call('get', route('reports.report.budget', [1, 1, '20160101', '20160131'])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\ReportController::categoryReport - */ - public function testCategoryReport() - { - $this->be($this->user()); - $this->call('get', route('reports.report.category', [1, 1, '20160101', '20160131'])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\ReportController::defaultReport - */ - public function testDefaultReport() - { - $this->be($this->user()); - $this->call('get', route('reports.report.default', [1, '20160101', '20160131'])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\ReportController::index - */ - public function testIndex() - { - $this->be($this->user()); - $this->call('get', route('reports.index')); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\ReportController::options - */ - public function testOptions() - { - $this->be($this->user()); - $this->call('get', route('reports.options', ['default'])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\ReportController::postIndex - */ - public function testPostIndex() - { - $this->be($this->user()); - $this->call('post', route('reports.index.post')); - $this->assertResponseStatus(302); - } -} diff --git a/tests/acceptance/Controllers/RuleControllerTest.php b/tests/acceptance/Controllers/RuleControllerTest.php deleted file mode 100644 index 41ad37571c..0000000000 --- a/tests/acceptance/Controllers/RuleControllerTest.php +++ /dev/null @@ -1,228 +0,0 @@ -<?php -/** - * RuleControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ -use FireflyIII\Models\Rule; -use FireflyIII\Repositories\Rule\RuleRepositoryInterface; - - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:43. - */ -class RuleControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\RuleController::create - */ - public function testCreate() - { - $this->be($this->user()); - $this->call('get', route('rules.create', [1])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\RuleController::delete - */ - public function testDelete() - { - $this->be($this->user()); - $this->call('get', route('rules.delete', [1])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\RuleController::destroy - */ - public function testDestroy() - { - $repository = $this->mock(RuleRepositoryInterface::class); - $repository->shouldReceive('destroy'); - - $this->session(['rules.delete.url' => 'http://localhost']); - $this->be($this->user()); - $this->call('post', route('rules.destroy', [1])); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - $this->assertRedirectedToRoute('index'); - } - - /** - * @covers \FireflyIII\Http\Controllers\RuleController::down - */ - public function testDown() - { - $this->be($this->user()); - $this->call('get', route('rules.down', [1])); - $this->assertResponseStatus(302); - $this->assertRedirectedToRoute('rules.index'); - } - - /** - * @covers \FireflyIII\Http\Controllers\RuleController::edit - */ - public function testEdit() - { - $this->be($this->user()); - $this->call('get', route('rules.edit', [1])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\RuleController::index - */ - public function testIndex() - { - $this->be($this->user()); - $this->call('get', route('rules.index')); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\RuleController::reorderRuleActions - */ - public function testReorderRuleActions() - { - $data = [ - 'triggers' => [1, 2, 3], - ]; - - $repository = $this->mock(RuleRepositoryInterface::class); - $repository->shouldReceive('reorderRuleActions'); - - $this->be($this->user()); - $this->call('post', route('rules.reorder-actions', [1]), $data); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\RuleController::reorderRuleTriggers - */ - public function testReorderRuleTriggers() - { - $data = [ - 'triggers' => [1, 2, 3], - ]; - - $repository = $this->mock(RuleRepositoryInterface::class); - $repository->shouldReceive('reorderRuleTriggers'); - - $this->be($this->user()); - $this->call('post', route('rules.reorder-triggers', [1]), $data); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\RuleController::store - */ - public function testStore() - { - $this->session(['rules.create.url' => 'http://localhost']); - $data = [ - 'rule_group_id' => 1, - 'active' => 1, - 'title' => 'A', - 'trigger' => 'store-journal', - 'description' => 'D', - 'rule-trigger' => [ - 1 => 'from_account_starts', - ], - 'rule-trigger-value' => [ - 1 => 'B', - ], - 'rule-action' => [ - 1 => 'set_category', - ], - 'rule-action-value' => [ - 1 => 'C', - ], - ]; - - $repository = $this->mock(RuleRepositoryInterface::class); - $repository->shouldReceive('store')->andReturn(new Rule); - - $this->be($this->user()); - $this->call('post', route('rules.store', [1]), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } - - /** - * This actually hits an error and not the actually code but OK. - * - * @covers \FireflyIII\Http\Controllers\RuleController::testTriggers - */ - public function testTestTriggers() - { - $this->be($this->user()); - $this->call('get', route('rules.test-triggers', [1])); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\RuleController::up - */ - public function testUp() - { - $this->be($this->user()); - $this->call('get', route('rules.up', [1])); - $this->assertResponseStatus(302); - $this->assertRedirectedToRoute('rules.index'); - } - - /** - * @covers \FireflyIII\Http\Controllers\RuleController::update - */ - public function testUpdate() - { - $data = [ - 'rule_group_id' => 1, - 'title' => 'Your first default rule', - 'trigger' => 'store-journal', - 'active' => 1, - 'description' => 'This rule is an example. You can safely delete it.', - 'rule-trigger' => [ - 1 => 'description_is', - ], - 'rule-trigger-value' => [ - 1 => 'something', - ], - 'rule-action' => [ - 1 => 'prepend_description', - ], - 'rule-action-value' => [ - 1 => 'Bla bla', - ], - ]; - $this->session(['rules.edit.url' => 'http://localhost']); - - $repository = $this->mock(RuleRepositoryInterface::class); - $repository->shouldReceive('update'); - - $this->be($this->user()); - $this->call('post', route('rules.update', [1]), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } -} diff --git a/tests/acceptance/Controllers/RuleGroupControllerTest.php b/tests/acceptance/Controllers/RuleGroupControllerTest.php deleted file mode 100644 index b61a95409e..0000000000 --- a/tests/acceptance/Controllers/RuleGroupControllerTest.php +++ /dev/null @@ -1,173 +0,0 @@ -<?php -/** - * RuleGroupControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ -use Carbon\Carbon; -use FireflyIII\Models\RuleGroup; -use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; - - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:43. - */ -class RuleGroupControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\RuleGroupController::create - */ - public function testCreate() - { - $this->be($this->user()); - $this->call('get', route('rule-groups.create')); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\RuleGroupController::delete - */ - public function testDelete() - { - $this->be($this->user()); - $this->call('get', route('rule-groups.delete', [1])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\RuleGroupController::destroy - */ - public function testDestroy() - { - $repository = $this->mock(RuleGroupRepositoryInterface::class); - $repository->shouldReceive('destroy'); - - $this->session(['rule-groups.delete.url' => 'http://localhost']); - $this->be($this->user()); - $this->call('post', route('rule-groups.destroy', [1])); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - $this->assertRedirectedToRoute('index'); - } - - /** - * @covers \FireflyIII\Http\Controllers\RuleGroupController::down - */ - public function testDown() - { - $this->be($this->user()); - $this->call('get', route('rule-groups.down', [1])); - $this->assertResponseStatus(302); - $this->assertRedirectedToRoute('rules.index'); - } - - /** - * @covers \FireflyIII\Http\Controllers\RuleGroupController::edit - */ - public function testEdit() - { - $this->be($this->user()); - $this->call('get', route('rule-groups.edit', [1])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\RuleGroupController::execute - */ - public function testExecute() - { - $this->session(['first' => new Carbon('2010-01-01')]); - $data = [ - 'accounts' => [1], - 'start_date' => '2010-01-02', - 'end_date' => '2010-01-02', - ]; - $this->be($this->user()); - $this->call('post', route('rule-groups.execute', [1]), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - $this->assertRedirectedToRoute('rules.index'); - } - - /** - * @covers \FireflyIII\Http\Controllers\RuleGroupController::selectTransactions - */ - public function testSelectTransactions() - { - $this->be($this->user()); - $this->call('get', route('rule-groups.select-transactions', [1])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\RuleGroupController::store - */ - public function testStore() - { - $this->session(['rule-groups.create.url' => 'http://localhost']); - $data = [ - 'title' => 'A', - 'description' => '', - ]; - - $repository = $this->mock(RuleGroupRepositoryInterface::class); - $repository->shouldReceive('store')->andReturn(new RuleGroup); - $repository->shouldReceive('find')->andReturn(new RuleGroup); - - $this->be($this->user()); - $this->call('post', route('rule-groups.store', [1]), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } - - /** - * @covers \FireflyIII\Http\Controllers\RuleGroupController::up - */ - public function testUp() - { - $this->be($this->user()); - $this->call('get', route('rule-groups.up', [1])); - $this->assertResponseStatus(302); - $this->assertRedirectedToRoute('rules.index'); - } - - /** - * @covers \FireflyIII\Http\Controllers\RuleGroupController::update - */ - public function testUpdate() - { - $data = [ - 'title' => 'C', - 'description' => 'XX', - ]; - $this->session(['rule-groups.edit.url' => 'http://localhost']); - - $repository = $this->mock(RuleGroupRepositoryInterface::class); - $repository->shouldReceive('update'); - $repository->shouldReceive('find')->andReturn(new RuleGroup); - - $this->be($this->user()); - $this->call('post', route('rule-groups.update', [1]), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } -} diff --git a/tests/acceptance/Controllers/SearchControllerTest.php b/tests/acceptance/Controllers/SearchControllerTest.php deleted file mode 100644 index 68b1b97636..0000000000 --- a/tests/acceptance/Controllers/SearchControllerTest.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php -/** - * SearchControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ -use FireflyIII\Support\Search\SearchInterface; -use Illuminate\Support\Collection; - - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:43. - */ -class SearchControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\SearchController::index - * Implement testIndex(). - */ - public function testIndex() - { - $search = $this->mock(SearchInterface::class); - $search->shouldReceive('setLimit')->once(); - $search->shouldReceive('searchTransactions')->andReturn(new Collection)->withArgs([['test']])->once(); - $search->shouldReceive('searchBudgets')->andReturn(new Collection)->withArgs([['test']])->once(); - $search->shouldReceive('searchTags')->andReturn(new Collection)->withArgs([['test']])->once(); - $search->shouldReceive('searchCategories')->andReturn(new Collection)->withArgs([['test']])->once(); - $search->shouldReceive('searchAccounts')->andReturn(new Collection)->withArgs([['test']])->once(); - $this->be($this->user()); - $this->call('get', route('search.index') . '?q=test'); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } -} diff --git a/tests/acceptance/Controllers/TagControllerTest.php b/tests/acceptance/Controllers/TagControllerTest.php deleted file mode 100644 index 4a7717d528..0000000000 --- a/tests/acceptance/Controllers/TagControllerTest.php +++ /dev/null @@ -1,135 +0,0 @@ -<?php -/** - * TagControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ -use FireflyIII\Models\Tag; -use FireflyIII\Repositories\Tag\TagRepositoryInterface; - - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:43. - */ -class TagControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\TagController::create - */ - public function testCreate() - { - $this->be($this->user()); - $this->call('GET', route('tags.create')); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\TagController::delete - */ - public function testDelete() - { - $this->be($this->user()); - $this->call('GET', route('tags.delete', [1])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\TagController::destroy - */ - public function testDestroy() - { - $repository = $this->mock(TagRepositoryInterface::class); - $repository->shouldReceive('destroy'); - - $this->be($this->user()); - $this->call('post', route('tags.destroy', [1])); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } - - /** - * @covers \FireflyIII\Http\Controllers\TagController::edit - */ - public function testEdit() - { - $this->be($this->user()); - $this->call('GET', route('tags.edit', [1])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\TagController::index - */ - public function testIndex() - { - $this->be($this->user()); - $this->call('GET', route('tags.index')); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\TagController::show - */ - public function testShow() - { - $this->be($this->user()); - $this->call('GET', route('tags.show', [1])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\TagController::store - */ - public function testStore() - { - $this->session(['tags.create.url' => 'http://localhost']); - $data = [ - 'tag' => 'Hello new tag' . rand(999, 10000), - 'tagMode' => 'nothing', - ]; - $this->be($this->user()); - $this->call('post', route('tags.store'), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } - - /** - * @covers \FireflyIII\Http\Controllers\TagController::update - */ - public function testUpdate() - { - $this->session(['tags.edit.url' => 'http://localhost']); - $data = [ - 'tag' => 'Hello updated tag' . rand(999, 10000), - 'tagMode' => 'nothing', - ]; - $repository = $this->mock(TagRepositoryInterface::class); - $repository->shouldReceive('update'); - $repository->shouldReceive('find')->andReturn(new Tag); - - $this->be($this->user()); - $this->call('post', route('tags.update', [1]), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } -} diff --git a/tests/acceptance/Controllers/Transaction/ConvertControllerTest.php b/tests/acceptance/Controllers/Transaction/ConvertControllerTest.php deleted file mode 100644 index deab21f8d7..0000000000 --- a/tests/acceptance/Controllers/Transaction/ConvertControllerTest.php +++ /dev/null @@ -1,121 +0,0 @@ -<?php -/** - * ConvertControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Transaction; - -use FireflyIII\Models\TransactionJournal; -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:43. - */ -class ConvertControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\ConvertController::index - */ - public function testIndexDepositTransfer() - { - $deposit = TransactionJournal::where('transaction_type_id', 2)->where('user_id', $this->user()->id)->first(); - - $this->be($this->user()); - $this->call('get', route('transactions.convert.index', ['transfer', $deposit->id])); - $this->assertResponseStatus(200); - $this->see('Convert a deposit into a transfer'); - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\ConvertController::index - */ - public function testIndexDepositWithdrawal() - { - $deposit = TransactionJournal::where('transaction_type_id', 2)->where('user_id', $this->user()->id)->first(); - $this->be($this->user()); - $this->call('get', route('transactions.convert.index', ['withdrawal', $deposit->id])); - $this->assertResponseStatus(200); - $this->see('Convert a deposit into a withdrawal'); - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\ConvertController::index - */ - public function testIndexTransferDeposit() - { - $transfer = TransactionJournal::where('transaction_type_id', 3)->where('user_id', $this->user()->id)->first(); - $this->be($this->user()); - $this->call('get', route('transactions.convert.index', ['deposit', $transfer->id])); - $this->assertResponseStatus(200); - $this->see('Convert a transfer into a deposit'); - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\ConvertController::index - */ - public function testIndexTransferWithdrawal() - { - $transfer = TransactionJournal::where('transaction_type_id', 3)->where('user_id', $this->user()->id)->first(); - $this->be($this->user()); - $this->call('get', route('transactions.convert.index', ['withdrawal', $transfer->id])); - $this->assertResponseStatus(200); - $this->see('Convert a transfer into a withdrawal'); - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\ConvertController::index - */ - public function testIndexWithdrawalDeposit() - { - $withdrawal= TransactionJournal::where('transaction_type_id', 1)->where('user_id', $this->user()->id)->first(); - $this->be($this->user()); - $this->call('get', route('transactions.convert.index', ['deposit', $withdrawal->id])); - $this->assertResponseStatus(200); - $this->see('Convert a withdrawal into a deposit'); - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\ConvertController::index - */ - public function testIndexWithdrawalTransfer() - { - $withdrawal= TransactionJournal::where('transaction_type_id', 1)->where('user_id', $this->user()->id)->first(); - $this->be($this->user()); - $this->call('get', route('transactions.convert.index', ['transfer', $withdrawal->id])); - $this->assertResponseStatus(200); - $this->see('Convert a withdrawal into a transfer'); - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\ConvertController::postIndex - */ - public function testPostIndex() - { - $withdrawal= TransactionJournal::where('transaction_type_id', 1)->where('user_id', $this->user()->id)->first(); - // convert a withdrawal to a transfer. Requires the ID of another asset account. - $data = [ - 'destination_account_asset' => 2, - ]; - $this->be($this->user()); - $this->call('post', route('transactions.convert.index', ['transfer', $withdrawal->id]), $data); - $this->assertResponseStatus(302); - $this->assertRedirectedToRoute('transactions.show', [$withdrawal->id]); - } -} diff --git a/tests/acceptance/Controllers/Transaction/MassControllerTest.php b/tests/acceptance/Controllers/Transaction/MassControllerTest.php deleted file mode 100644 index e6bf99179e..0000000000 --- a/tests/acceptance/Controllers/Transaction/MassControllerTest.php +++ /dev/null @@ -1,115 +0,0 @@ -<?php -/** - * MassControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Transaction; - -use FireflyIII\Models\TransactionJournal; -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:44. - */ -class MassControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\MassController::delete - */ - public function testDelete() - { - $withdrawals = TransactionJournal::where('transaction_type_id', 1)->where('user_id', $this->user()->id)->take(2)->get()->pluck('id')->toArray(); - $this->be($this->user()); - $this->call('get', route('transactions.mass.delete', $withdrawals)); - $this->assertResponseStatus(200); - $this->see('Delete a number of transactions'); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\MassController::destroy - */ - public function testDestroy() - { - $this->session(['transactions.mass-delete.url' => 'http://localhost']); - $deposits = TransactionJournal::where('transaction_type_id', 2)->where('user_id', $this->user()->id)->take(2)->get()->pluck('id')->toArray(); - $data = [ - 'confirm_mass_delete' => $deposits, - ]; - $this->be($this->user()); - $this->call('post', route('transactions.mass.destroy'), $data); - $this->assertSessionHas('success'); - $this->assertResponseStatus(302); - - // visit them should give 404. - $this->call('get', route('transactions.show', [$deposits[0]])); - $this->assertResponseStatus(404); - - - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\MassController::edit - */ - public function testEdit() - { - $transfers = TransactionJournal::where('transaction_type_id', 3)->where('user_id', $this->user()->id)->take(2)->get()->pluck('id')->toArray(); - $this->be($this->user()); - $this->call('get', route('transactions.mass.delete', $transfers)); - $this->assertResponseStatus(200); - $this->see('Edit a number of transactions'); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\MassController::update - */ - public function testUpdate() - { - $deposit = TransactionJournal::where('transaction_type_id', 2)->where('user_id', $this->user()->id) - ->whereNull('deleted_at') - ->first(); - $this->session(['transactions.mass-edit.url' => 'http://localhost']); - - $data = [ - 'journals' => [$deposit->id], - 'description' => [$deposit->id => 'Updated salary thing'], - 'amount' => [$deposit->id => 1600], - 'amount_currency_id_amount_' . $deposit->id => 1, - 'date' => [$deposit->id => '2014-07-24'], - 'source_account_name' => [$deposit->id => 'Job'], - 'destination_account_id' => [$deposit->id => 1], - 'category' => [$deposit->id => 'Salary'], - ]; - - $this->be($this->user()); - $this->call('post', route('transactions.mass.update', [$deposit->id]), $data); - $this->assertSessionHas('success'); - $this->assertResponseStatus(302); - - // visit them should show updated content - $this->call('get', route('transactions.show', [$deposit->id])); - $this->assertResponseStatus(200); - $this->see('Updated salary thing'); - } - -} diff --git a/tests/acceptance/Controllers/Transaction/SingleControllerTest.php b/tests/acceptance/Controllers/Transaction/SingleControllerTest.php deleted file mode 100644 index 81e67a4e21..0000000000 --- a/tests/acceptance/Controllers/Transaction/SingleControllerTest.php +++ /dev/null @@ -1,141 +0,0 @@ -<?php -/** - * SingleControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Transaction; - -use FireflyIII\Models\TransactionJournal; -use FireflyIII\Repositories\Journal\JournalRepositoryInterface; -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:44. - */ -class SingleControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\SingleController::create - */ - public function testCreate() - { - $this->be($this->user()); - $this->call('get', route('transactions.create', ['withdrawal'])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\SingleController::delete - */ - public function testDelete() - { - $this->be($this->user()); - $this->call('get', route('transactions.delete', [12])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\SingleController::destroy - */ - public function testDestroy() - { - $this->session(['transactions.delete.url' => 'http://localhost']); - $this->be($this->user()); - - $repository = $this->mock(JournalRepositoryInterface::class); - $repository->shouldReceive('first')->once()->andReturn(new TransactionJournal); - $repository->shouldReceive('delete')->once(); - - $this->call('post', route('transactions.destroy', [13])); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\SingleController::edit - */ - public function testEdit() - { - $this->be($this->user()); - $this->call('get', route('transactions.edit', [13])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\SingleController::store - */ - public function testStore() - { - $this->session(['transactions.create.url' => 'http://localhost']); - $this->be($this->user()); - - $data = [ - 'what' => 'withdrawal', - 'amount' => '10', - 'amount_currency_id_amount' => 1, - 'source_account_id' => 1, - 'destination_account_name' => 'Some destination', - 'date' => '2016-01-01', - 'description' => 'Test descr', - ]; - $this->call('post', route('transactions.store', ['withdrawal']), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\SingleController::update - */ - public function testUpdate() - { - $this->session(['transactions.edit.url' => 'http://localhost']); - $this->be($this->user()); - $data = [ - 'id' => 123, - 'what' => 'withdrawal', - 'description' => 'Updated groceries', - 'source_account_id' => 1, - 'destination_account_name' => 'PLUS', - 'amount' => '123', - 'amount_currency_id_amount' => 1, - 'budget_id' => 1, - 'category' => 'Daily groceries', - 'tags' => '', - 'date' => '2016-01-01', - ]; - - $this->call('post', route('transactions.update', [123]), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - - $this->call('get', route('transactions.show', [123])); - $this->assertResponseStatus(200); - $this->see('Updated groceries'); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - - } -} diff --git a/tests/acceptance/Controllers/Transaction/SplitControllerTest.php b/tests/acceptance/Controllers/Transaction/SplitControllerTest.php deleted file mode 100644 index df52e7af6f..0000000000 --- a/tests/acceptance/Controllers/Transaction/SplitControllerTest.php +++ /dev/null @@ -1,86 +0,0 @@ -<?php -/** - * SplitControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -namespace Transaction; - -use FireflyIII\Models\TransactionJournal; -use TestCase; - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:44. - */ -class SplitControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\SplitController::edit - * Implement testEdit(). - */ - public function testEdit() - { - $deposit = TransactionJournal::where('transaction_type_id', 2)->where('user_id', $this->user()->id)->first(); - $this->be($this->user()); - $this->call('get', route('transactions.split.edit', [$deposit->id])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\Transaction\SplitController::update - * Implement testUpdate(). - */ - public function testUpdate() - { - $this->session(['transactions.edit-split.url' => 'http://localhost']); - $deposit = TransactionJournal::where('transaction_type_id', 2)->where('user_id', $this->user()->id)->first(); - $data = [ - 'id' => $deposit->id, - 'what' => 'deposit', - 'journal_description' => 'Updated salary', - 'currency_id' => 1, - 'journal_destination_account_id' => 1, - 'journal_amount' => 1591, - 'date' => '2014-01-24', - 'tags' => '', - 'transactions' => [ - [ - 'description' => 'Split #1', - 'source_account_name' => 'Job', - 'amount' => 1591, - 'category' => '', - ], - ], - ]; - $this->be($this->user()); - $this->call('post', route('transactions.split.update', [$deposit->id]), $data); - $this->assertResponseStatus(302); - $this->assertSessionHas('success'); - - // journal is updated? - $this->call('get', route('transactions.show', [$deposit->id])); - $this->assertResponseStatus(200); - $this->see('Updated salary'); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - -} diff --git a/tests/acceptance/Controllers/TransactionControllerTest.php b/tests/acceptance/Controllers/TransactionControllerTest.php deleted file mode 100644 index d5e596fd60..0000000000 --- a/tests/acceptance/Controllers/TransactionControllerTest.php +++ /dev/null @@ -1,89 +0,0 @@ -<?php -/** - * TransactionControllerTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - - -/** - * Generated by PHPUnit_SkeletonGenerator on 2016-12-10 at 05:51:44. - */ -class TransactionControllerTest extends TestCase -{ - - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - } - - /** - * @covers \FireflyIII\Http\Controllers\TransactionController::index - */ - public function testIndex() - { - - $this->be($this->user()); - $this->call('get', route('transactions.index', ['transfer'])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\TransactionController::indexAll - */ - public function testIndexAll() - { - $this->be($this->user()); - $this->call('get', route('transactions.index.all', ['transfer'])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\TransactionController::indexByDate - */ - public function testIndexByDate() - { - $this->be($this->user()); - $this->call('get', route('transactions.index.date', ['transfer', '2016-01-01'])); - $this->assertResponseStatus(200); - // has bread crumb - $this->see('<ol class="breadcrumb">'); - } - - /** - * @covers \FireflyIII\Http\Controllers\TransactionController::reorder - */ - public function testReorder() - { - $data = [ - 'items' => [], - ]; - $this->be($this->user()); - $this->call('post', route('transactions.reorder'), $data); - $this->assertResponseStatus(200); - } - - /** - * @covers \FireflyIII\Http\Controllers\TransactionController::show - */ - public function testShow() - { - $this->be($this->user()); - $this->call('get', route('transactions.show', [1])); - $this->assertResponseStatus(200); - $this->see('<ol class="breadcrumb">'); - } -} diff --git a/tests/unit/Models/AccountTest.php b/tests/unit/Models/AccountTest.php deleted file mode 100644 index 6be272929b..0000000000 --- a/tests/unit/Models/AccountTest.php +++ /dev/null @@ -1,83 +0,0 @@ -<?php -/** - * AccountTest.php - * Copyright (c) 2016 thegrumpydictator@gmail.com - * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -declare(strict_types = 1); - -use FireflyIII\Models\Account; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; - -class AccountTest extends TestCase -{ - - /** - * @covers \FireflyIII\Models\Account::firstOrCreateEncrypted - */ - public function testEncrypted() - { - $data = [ - 'user_id' => 1, - 'name' => 'Test account #' . rand(1000, 9999), - ]; - $account = Account::firstOrCreateEncrypted($data); - - $this->assertEquals($account->user_id, $data['user_id']); - $this->assertEquals($account->name, $data['name']); - } - - /** - * @covers \FireflyIII\Models\Account::firstOrCreateEncrypted - */ - public function testEncryptedIban() - { - $data = [ - 'user_id' => 1, - 'iban' => 'NL64RABO0133183395', - ]; - $account = Account::firstOrCreateEncrypted($data); - - $this->assertEquals($account->user_id, $data['user_id']); - $this->assertEquals($account->name, $data['iban']); - } - - /** - * @covers \FireflyIII\Models\Account::firstOrCreateEncrypted - * @expectedException \FireflyIII\Exceptions\FireflyException - */ - public function testEncryptedNoId() - { - $data = [ - 'name' => 'Test account', - ]; - $account = Account::firstOrCreateEncrypted($data); - } - - /** - * @covers \FireflyIII\Models\Account::routeBinder - */ - public function testRouteBinder() - { - // not logged in? - $this->be($this->user()); - $this->call('get', route('accounts.show', [1])); - - } - - /** - * One that belongs to another user. - * - * @covers \FireflyIII\Models\Account::routeBinder - */ - public function testRouteBinderError() - { - $account = Account::whereUserId(3)->first(); - $this->be($this->user()); - $this->call('get', route('accounts.show', [$account->id])); - $this->assertResponseStatus(404); - } -} \ No newline at end of file diff --git a/tests/unit/Models/TransactionTypeTest.php b/tests/unit/Models/TransactionTypeTest.php deleted file mode 100644 index 51d8a1ea76..0000000000 --- a/tests/unit/Models/TransactionTypeTest.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -/** - * TransactionTypeTest.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -use FireflyIII\Models\TransactionType; - - -/** - * Class TransactionTypeTest - */ -class TransactionTypeTest extends TestCase -{ - /** - * @covers \FireflyIII\Models\TransactionType::isDeposit - */ - public function testIsDeposit() - { - - $transactionType = TransactionType::whereType(TransactionType::DEPOSIT)->first(); - $this->assertTrue($transactionType->isDeposit()); - } - - /** - * @covers \FireflyIII\Models\TransactionType::isOpeningBalance - */ - public function testIsOpeningBalance() - { - $transactionType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first(); - $this->assertTrue($transactionType->isOpeningBalance()); - } - - /** - * @covers \FireflyIII\Models\TransactionType::isTransfer - */ - public function testIsTransfer() - { - $transactionType = TransactionType::whereType(TransactionType::TRANSFER)->first(); - $this->assertTrue($transactionType->isTransfer()); - } - - /** - * @covers \FireflyIII\Models\TransactionType::isWithdrawal - */ - public function testIsWithdrawal() - { - $transactionType = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); - $this->assertTrue($transactionType->isWithdrawal()); - } -} From 229f71875446e43eaed418fe3e629feed79ec6be Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 5 Feb 2017 15:41:41 +0100 Subject: [PATCH 643/709] Update Composer files. --- composer.json | 9 +++-- composer.lock | 96 ++++++++++++--------------------------------------- 2 files changed, 27 insertions(+), 78 deletions(-) diff --git a/composer.json b/composer.json index 4c1764d3c0..8e493a3031 100755 --- a/composer.json +++ b/composer.json @@ -68,8 +68,7 @@ "symfony/css-selector": "3.1.*", "symfony/dom-crawler": "3.1.*", "barryvdh/laravel-debugbar": "2.*", - "barryvdh/laravel-ide-helper": "2.*", - "johnkary/phpunit-speedtrap": "^1.0" + "barryvdh/laravel-ide-helper": "2.*" }, "autoload": { "classmap": [ @@ -80,9 +79,9 @@ } }, "autoload-dev": { - "classmap": [ - "tests/TestCase.php" - ] + "psr-4": { + "Tests\\": "tests/" + } }, "scripts": { "post-root-package-install": [ diff --git a/composer.lock b/composer.lock index 46a59ecc21..1c8211b043 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "485d633a8ac40b245a597a33fae7abf4", + "content-hash": "bccf2c2fdac584664dd6c952f1d91ba8", "packages": [ { "name": "bacon/bacon-qr-code", @@ -433,16 +433,16 @@ }, { "name": "doctrine/dbal", - "version": "v2.5.10", + "version": "v2.5.11", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "fc376f7a61498e18520cd6fa083752a4ca08072b" + "reference": "1b1effbddbdc0f40d1c8f849f44bcddac4f52a48" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/fc376f7a61498e18520cd6fa083752a4ca08072b", - "reference": "fc376f7a61498e18520cd6fa083752a4ca08072b", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/1b1effbddbdc0f40d1c8f849f44bcddac4f52a48", + "reference": "1b1effbddbdc0f40d1c8f849f44bcddac4f52a48", "shasum": "" }, "require": { @@ -500,7 +500,7 @@ "persistence", "queryobject" ], - "time": "2017-01-23T23:17:10+00:00" + "time": "2017-02-04T21:20:13+00:00" }, { "name": "doctrine/inflector", @@ -667,16 +667,16 @@ }, { "name": "laravel/framework", - "version": "v5.4.6", + "version": "v5.4.9", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "5913b960656d7f978605ff9c1513ffbfd190c173" + "reference": "600330ae1d218919b3b307e0578461a2df248663" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/5913b960656d7f978605ff9c1513ffbfd190c173", - "reference": "5913b960656d7f978605ff9c1513ffbfd190c173", + "url": "https://api.github.com/repos/laravel/framework/zipball/600330ae1d218919b3b307e0578461a2df248663", + "reference": "600330ae1d218919b3b307e0578461a2df248663", "shasum": "" }, "require": { @@ -792,7 +792,7 @@ "framework", "laravel" ], - "time": "2017-01-27T19:27:15+00:00" + "time": "2017-02-03T19:47:35+00:00" }, { "name": "laravelcollective/html", @@ -976,16 +976,16 @@ }, { "name": "league/flysystem", - "version": "1.0.33", + "version": "1.0.34", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "5c7f98498b12d47f9de90ec9186a90000125777c" + "reference": "469ad53c13ea19a0e54e3e5d70f61227ddcc0299" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/5c7f98498b12d47f9de90ec9186a90000125777c", - "reference": "5c7f98498b12d47f9de90ec9186a90000125777c", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/469ad53c13ea19a0e54e3e5d70f61227ddcc0299", + "reference": "469ad53c13ea19a0e54e3e5d70f61227ddcc0299", "shasum": "" }, "require": { @@ -1055,7 +1055,7 @@ "sftp", "storage" ], - "time": "2017-01-23T10:32:09+00:00" + "time": "2017-01-30T17:41:17+00:00" }, { "name": "monolog/monolog", @@ -2998,56 +2998,6 @@ ], "time": "2015-05-11T14:41:42+00:00" }, - { - "name": "johnkary/phpunit-speedtrap", - "version": "v1.0.1", - "source": { - "type": "git", - "url": "https://github.com/johnkary/phpunit-speedtrap.git", - "reference": "76a26f8a903a9434608cdad2b41c40cd134ea326" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/johnkary/phpunit-speedtrap/zipball/76a26f8a903a9434608cdad2b41c40cd134ea326", - "reference": "76a26f8a903a9434608cdad2b41c40cd134ea326", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "3.7.*|~4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "JohnKary": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "John Kary", - "email": "john@johnkary.net" - } - ], - "description": "Find slow tests in your PHPUnit test suite", - "homepage": "https://github.com/johnkary/phpunit-speedtrap", - "keywords": [ - "phpunit", - "profile", - "slow" - ], - "time": "2015-09-13T19:01:00+00:00" - }, { "name": "maximebf/debugbar", "version": "1.13.1", @@ -3671,16 +3621,16 @@ }, { "name": "phpunit/phpunit", - "version": "5.7.9", + "version": "5.7.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "69f832b87c731d5cacad7f91948778fe98335fdd" + "reference": "bf0804199f516fe80ffcc48ac6d4741c49baeb6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/69f832b87c731d5cacad7f91948778fe98335fdd", - "reference": "69f832b87c731d5cacad7f91948778fe98335fdd", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bf0804199f516fe80ffcc48ac6d4741c49baeb6e", + "reference": "bf0804199f516fe80ffcc48ac6d4741c49baeb6e", "shasum": "" }, "require": { @@ -3697,11 +3647,11 @@ "phpunit/php-text-template": "~1.2", "phpunit/php-timer": "^1.0.6", "phpunit/phpunit-mock-objects": "^3.2", - "sebastian/comparator": "~1.2.2", + "sebastian/comparator": "^1.2.4", "sebastian/diff": "~1.2", "sebastian/environment": "^1.3.4 || ^2.0", "sebastian/exporter": "~2.0", - "sebastian/global-state": "^1.0 || ^2.0", + "sebastian/global-state": "^1.1", "sebastian/object-enumerator": "~2.0", "sebastian/resource-operations": "~1.0", "sebastian/version": "~1.0|~2.0", @@ -3749,7 +3699,7 @@ "testing", "xunit" ], - "time": "2017-01-28T06:14:33+00:00" + "time": "2017-02-04T09:03:53+00:00" }, { "name": "phpunit/phpunit-mock-objects", From a9d5b6ef928895a0fa1b510595499323e7ac604c Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 5 Feb 2017 15:42:00 +0100 Subject: [PATCH 644/709] Update to laravel 5.4 style tests --- tests/CreatesApplication.php | 22 +++++ tests/Feature/ExampleTest.php | 23 +++++ tests/TestCase.php | 154 ++-------------------------------- tests/Unit/ExampleTest.php | 20 +++++ 4 files changed, 73 insertions(+), 146 deletions(-) create mode 100644 tests/CreatesApplication.php create mode 100644 tests/Feature/ExampleTest.php create mode 100644 tests/Unit/ExampleTest.php diff --git a/tests/CreatesApplication.php b/tests/CreatesApplication.php new file mode 100644 index 0000000000..8d8a77e99d --- /dev/null +++ b/tests/CreatesApplication.php @@ -0,0 +1,22 @@ +<?php + +namespace Tests; + +use Illuminate\Contracts\Console\Kernel; + +trait CreatesApplication +{ + /** + * Creates the application. + * + * @return \Illuminate\Foundation\Application + */ + public function createApplication() + { + $app = require __DIR__.'/../bootstrap/app.php'; + + $app->make(Kernel::class)->bootstrap(); + + return $app; + } +} \ No newline at end of file diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php new file mode 100644 index 0000000000..c23bfc63d5 --- /dev/null +++ b/tests/Feature/ExampleTest.php @@ -0,0 +1,23 @@ +<?php + +namespace Tests\Feature; + +use Tests\TestCase; +use Illuminate\Foundation\Testing\WithoutMiddleware; +use Illuminate\Foundation\Testing\DatabaseMigrations; +use Illuminate\Foundation\Testing\DatabaseTransactions; + +class ExampleTest extends TestCase +{ + /** + * A basic test example. + * + * @return void + */ + public function testBasicTest() + { + $response = $this->get('/'); + + $response->assertStatus(200); + } +} \ No newline at end of file diff --git a/tests/TestCase.php b/tests/TestCase.php index c2a57c8e43..03cde96b56 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1,153 +1,15 @@ <?php -/** - * TestCase.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ -use Carbon\Carbon; -use FireflyIII\Models\Preference; -use FireflyIII\User; +namespace Tests; + +use Illuminate\Foundation\Testing\TestCase as BaseTestCase; /** * Class TestCase + * + * @package Tests */ -abstract class TestCase extends Illuminate\Foundation\Testing\TestCase +abstract class TestCase extends BaseTestCase { - /** - * The base URL to use while testing the application. - * - * @var string - */ - protected $baseUrl = 'http://localhost'; - - /** - * @param User $user - * @param string $range - */ - public function changeDateRange(User $user, $range) - { - $valid = ['1D', '1W', '1M', '3M', '6M', '1Y', 'custom']; - if (in_array($range, $valid)) { - Preference::where('user_id', $user->id)->where('name', 'viewRange')->delete(); - Preference::create( - [ - 'user_id' => $user->id, - 'name' => 'viewRange', - 'data' => $range, - ] - ); - // set period to match? - - } - if ($range === 'custom') { - $this->session( - [ - 'start' => Carbon::now()->subDays(20), - 'end' => Carbon::now(), - ] - ); - } - } - - /** - * Creates the application. - * - * @return \Illuminate\Foundation\Application - */ - public function createApplication() - { - $app = require __DIR__ . '/../bootstrap/app.php'; - - $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap(); - - return $app; - } - - /** - * @return array - */ - public function dateRangeProvider() - { - return [ - 'one day' => ['1D'], - 'one week' => ['1W'], - 'one month' => ['1M'], - 'three months' => ['3M'], - 'six months' => ['6M'], - 'one year' => ['1Y'], - 'custom range' => ['custom'], - ]; - } - - /** - * @return User - */ - public function emptyUser() - { - $user = User::find(2); - - return $user; - } - - /** - * @return array - */ - public function naughtyStringProvider() - { - /* - * If on Travis, return very small set. - */ - if (getenv('TRAVIS') == 'true') { - return [['Default value']]; - - } - $path = realpath(__DIR__ . '/../resources/tests/blns.base64.json'); - $content = file_get_contents($path); - $array = json_decode($content); - $return = []; - foreach ($array as $entry) { - $return[] = [base64_decode($entry)]; - } - - return $return; - } - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - public function setUp() - { - parent::setUp(); - - } - - /** - * @return User - */ - public function user() - { - $user = User::find(1); - - return $user; - } - - /** - * @param string $class - * - * @return \Mockery\MockInterface - */ - protected function mock($class) - { - Log::debug(sprintf('Will now mock %s', $class)); - $object = Mockery::mock($class); - $this->app->instance($class, $object); - - return $object; - } -} + use CreatesApplication; +} \ No newline at end of file diff --git a/tests/Unit/ExampleTest.php b/tests/Unit/ExampleTest.php new file mode 100644 index 0000000000..239346394a --- /dev/null +++ b/tests/Unit/ExampleTest.php @@ -0,0 +1,20 @@ +<?php + +namespace Tests\Unit; + +use Tests\TestCase; +use Illuminate\Foundation\Testing\DatabaseMigrations; +use Illuminate\Foundation\Testing\DatabaseTransactions; + +class ExampleTest extends TestCase +{ + /** + * A basic test example. + * + * @return void + */ + public function testBasicTest() + { + $this->assertTrue(true); + } +} \ No newline at end of file From 3ff83cd4317de3ea9621b0f1eb920ecc3ace1b31 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 5 Feb 2017 15:43:56 +0100 Subject: [PATCH 645/709] Remove speed trap. --- phpunit.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index d439428075..c096c7a945 100755 --- a/phpunit.xml +++ b/phpunit.xml @@ -17,11 +17,6 @@ <directory suffix="Test.php">./tests/unit</directory> </testsuite> </testsuites> - - <listeners> - <listener class="JohnKary\PHPUnit\Listener\SpeedTrapListener"/> - </listeners> - <filter> <whitelist addUncoveredFilesFromWhitelist="true"> <directory suffix=".php">./app</directory> From 152fb3f885c8d207fe86583e66b073a8a24d2707 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 5 Feb 2017 15:58:27 +0100 Subject: [PATCH 646/709] Update the last providers. --- app/Providers/ExportJobServiceProvider.php | 18 +++++++++--------- app/Providers/RuleGroupServiceProvider.php | 2 +- app/Providers/RuleServiceProvider.php | 19 +++++++++---------- app/Providers/SearchServiceProvider.php | 18 +++++++++--------- app/Providers/TagServiceProvider.php | 19 ++++++++++--------- 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/app/Providers/ExportJobServiceProvider.php b/app/Providers/ExportJobServiceProvider.php index 7c12925df1..6850f417b5 100644 --- a/app/Providers/ExportJobServiceProvider.php +++ b/app/Providers/ExportJobServiceProvider.php @@ -14,9 +14,10 @@ declare(strict_types = 1); namespace FireflyIII\Providers; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Repositories\ExportJob\ExportJobRepository; use FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface; +use FireflyIII\Repositories\ImportJob\ImportJobRepository; +use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -72,16 +73,15 @@ class ExportJobServiceProvider extends ServiceProvider private function importJob() { $this->app->bind( - 'FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface', - function (Application $app, array $arguments) { - if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\ImportJob\ImportJobRepository', [auth()->user()]); - } - if (!isset($arguments[0]) && !$app->auth->check()) { - throw new FireflyException('There is no user present.'); + ImportJobRepositoryInterface::class, + function (Application $app) { + /** @var ImportJobRepository $repository */ + $repository = app(ImportJobRepository::class); + if ($app->auth->check()) { + $repository->setUser(auth()->user()); } - return app('FireflyIII\Repositories\ImportJob\ImportJobRepository', $arguments); + return $repository; } ); } diff --git a/app/Providers/RuleGroupServiceProvider.php b/app/Providers/RuleGroupServiceProvider.php index c6b86c1604..0a3c8db599 100644 --- a/app/Providers/RuleGroupServiceProvider.php +++ b/app/Providers/RuleGroupServiceProvider.php @@ -46,7 +46,7 @@ class RuleGroupServiceProvider extends ServiceProvider { $this->app->bind( RuleGroupRepositoryInterface::class, - function (Application $app, array $arguments) { + function (Application $app) { /** @var RuleGroupRepository $repository */ $repository = app(RuleGroupRepository::class); if ($app->auth->check()) { diff --git a/app/Providers/RuleServiceProvider.php b/app/Providers/RuleServiceProvider.php index a395e8cda3..5d694411b3 100644 --- a/app/Providers/RuleServiceProvider.php +++ b/app/Providers/RuleServiceProvider.php @@ -14,7 +14,8 @@ declare(strict_types = 1); namespace FireflyIII\Providers; -use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Repositories\Rule\RuleRepository; +use FireflyIII\Repositories\Rule\RuleRepositoryInterface; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -43,16 +44,14 @@ class RuleServiceProvider extends ServiceProvider public function register() { $this->app->bind( - 'FireflyIII\Repositories\Rule\RuleRepositoryInterface', - function (Application $app, array $arguments) { - if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\Rule\RuleRepository', [auth()->user()]); + RuleRepositoryInterface::class, + function (Application $app) { + /** @var RuleRepository $repository */ + $repository = app(RuleRepository::class); + if ($app->auth->check()) { + $repository->setUser(auth()->user()); } - if (!isset($arguments[0]) && !$app->auth->check()) { - throw new FireflyException('There is no user present.'); - } - - return app('FireflyIII\Repositories\Rule\RuleRepository', $arguments); + return $repository; } ); } diff --git a/app/Providers/SearchServiceProvider.php b/app/Providers/SearchServiceProvider.php index a7be767e5e..18d5b48c6e 100644 --- a/app/Providers/SearchServiceProvider.php +++ b/app/Providers/SearchServiceProvider.php @@ -14,7 +14,8 @@ declare(strict_types = 1); namespace FireflyIII\Providers; -use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Support\Search\Search; +use FireflyIII\Support\Search\SearchInterface; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -43,16 +44,15 @@ class SearchServiceProvider extends ServiceProvider public function register() { $this->app->bind( - 'FireflyIII\Support\Search\SearchInterface', - function (Application $app, array $arguments) { - if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Support\Search\Search', [auth()->user()]); - } - if (!isset($arguments[0]) && !$app->auth->check()) { - throw new FireflyException('There is no user present.'); + SearchInterface::class, + function (Application $app) { + /** @var Search $search */ + $search = app(Search::class); + if ($app->auth->check()) { + $search->setUser(auth()->user()); } - return app('FireflyIII\Support\Search\Search', $arguments); + return $search; } ); } diff --git a/app/Providers/TagServiceProvider.php b/app/Providers/TagServiceProvider.php index 8889c9fbeb..b5daa36c6c 100644 --- a/app/Providers/TagServiceProvider.php +++ b/app/Providers/TagServiceProvider.php @@ -14,7 +14,8 @@ declare(strict_types = 1); namespace FireflyIII\Providers; -use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Repositories\Tag\TagRepository; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -43,16 +44,16 @@ class TagServiceProvider extends ServiceProvider public function register() { $this->app->bind( - 'FireflyIII\Repositories\Tag\TagRepositoryInterface', - function (Application $app, array $arguments) { - if (!isset($arguments[0]) && $app->auth->check()) { - return app('FireflyIII\Repositories\Tag\TagRepository', [auth()->user()]); - } - if (!isset($arguments[0]) && !$app->auth->check()) { - throw new FireflyException('There is no user present.'); + TagRepositoryInterface::class, + function (Application $app) { + /** @var TagRepository $repository */ + $repository = app(TagRepository::class); + + if ($app->auth->check()) { + $repository->setUser(auth()->user()); } - return app('FireflyIII\Repositories\Tag\TagRepository', $arguments); + return $repository; } ); } From 371ce37be4df0e941f03c009d52cb9bb41631241 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 5 Feb 2017 15:58:42 +0100 Subject: [PATCH 647/709] Update export classes --- app/Export/Exporter/BasicExporter.php | 16 +++++++++++----- app/Export/Exporter/CsvExporter.php | 8 ++------ app/Export/Exporter/ExporterInterface.php | 6 ++++++ app/Export/Processor.php | 13 ++++++++----- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/app/Export/Exporter/BasicExporter.php b/app/Export/Exporter/BasicExporter.php index b16b19776a..256fa3b544 100644 --- a/app/Export/Exporter/BasicExporter.php +++ b/app/Export/Exporter/BasicExporter.php @@ -26,17 +26,15 @@ class BasicExporter { /** @var ExportJob */ protected $job; - private $entries; + /** @var Collection */ + private $entries; /** * BasicExporter constructor. - * - * @param ExportJob $job */ - public function __construct(ExportJob $job) + public function __construct() { $this->entries = new Collection; - $this->job = $job; } /** @@ -55,5 +53,13 @@ class BasicExporter $this->entries = $entries; } + /** + * @param ExportJob $job + */ + public function setJob(ExportJob $job) + { + $this->job = $job; + } + } diff --git a/app/Export/Exporter/CsvExporter.php b/app/Export/Exporter/CsvExporter.php index b1f12f4e30..200bf8116d 100644 --- a/app/Export/Exporter/CsvExporter.php +++ b/app/Export/Exporter/CsvExporter.php @@ -14,7 +14,6 @@ declare(strict_types = 1); namespace FireflyIII\Export\Exporter; use FireflyIII\Export\Entry\Entry; -use FireflyIII\Models\ExportJob; use League\Csv\Writer; use SplFileObject; @@ -30,13 +29,10 @@ class CsvExporter extends BasicExporter implements ExporterInterface /** * CsvExporter constructor. - * - * @param ExportJob $job */ - public function __construct(ExportJob $job) + public function __construct() { - parent::__construct($job); - + parent::__construct(); } /** diff --git a/app/Export/Exporter/ExporterInterface.php b/app/Export/Exporter/ExporterInterface.php index 3559d89cec..9e267b4812 100644 --- a/app/Export/Exporter/ExporterInterface.php +++ b/app/Export/Exporter/ExporterInterface.php @@ -13,6 +13,7 @@ declare(strict_types = 1); namespace FireflyIII\Export\Exporter; +use FireflyIII\Models\ExportJob; use Illuminate\Support\Collection; /** @@ -45,4 +46,9 @@ interface ExporterInterface */ public function setEntries(Collection $entries); + /** + * @param ExportJob $job + */ + public function setJob(ExportJob $job); + } diff --git a/app/Export/Processor.php b/app/Export/Processor.php index 0d0421a460..90ff2d8692 100644 --- a/app/Export/Processor.php +++ b/app/Export/Processor.php @@ -19,7 +19,6 @@ use FireflyIII\Export\Collector\JournalExportCollector; use FireflyIII\Export\Collector\UploadCollector; use FireflyIII\Export\Entry\Entry; use FireflyIII\Models\ExportJob; -use Illuminate\Filesystem\FilesystemAdapter; use Illuminate\Support\Collection; use Log; use Storage; @@ -78,7 +77,8 @@ class Processor implements ProcessorInterface public function collectAttachments(): bool { /** @var AttachmentCollector $attachmentCollector */ - $attachmentCollector = app(AttachmentCollector::class, [$this->job]); + $attachmentCollector = app(AttachmentCollector::class); + $attachmentCollector->setJob($this->job); $attachmentCollector->setDates($this->settings['startDate'], $this->settings['endDate']); $attachmentCollector->run(); $this->files = $this->files->merge($attachmentCollector->getEntries()); @@ -92,7 +92,8 @@ class Processor implements ProcessorInterface public function collectJournals(): bool { /** @var JournalExportCollector $collector */ - $collector = app(JournalExportCollector::class, [$this->job]); + $collector = app(JournalExportCollector::class); + $collector->setJob($this->job); $collector->setDates($this->settings['startDate'], $this->settings['endDate']); $collector->setAccounts($this->settings['accounts']); $collector->run(); @@ -108,7 +109,8 @@ class Processor implements ProcessorInterface public function collectOldUploads(): bool { /** @var UploadCollector $uploadCollector */ - $uploadCollector = app(UploadCollector::class, [$this->job]); + $uploadCollector = app(UploadCollector::class); + $uploadCollector->setJob($this->job); $uploadCollector->run(); $this->files = $this->files->merge($uploadCollector->getEntries()); @@ -166,7 +168,8 @@ class Processor implements ProcessorInterface public function exportJournals(): bool { $exporterClass = config('firefly.export_formats.' . $this->exportFormat); - $exporter = app($exporterClass, [$this->job]); + $exporter = app($exporterClass); + $exporter->setJob($this->job); $exporter->setEntries($this->exportEntries); $exporter->run(); $this->files->push($exporter->getFileName()); From 704c0922e863a9da5c8299cf5de53cd319888547 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 5 Feb 2017 15:58:55 +0100 Subject: [PATCH 648/709] Update collector classes --- app/Export/Collector/AttachmentCollector.php | 6 ++---- app/Export/Collector/BasicCollector.php | 13 +++++++++---- app/Export/Collector/CollectorInterface.php | 8 ++++++++ app/Export/Collector/UploadCollector.php | 20 +++++++------------- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/app/Export/Collector/AttachmentCollector.php b/app/Export/Collector/AttachmentCollector.php index e068f32585..810dc1db85 100644 --- a/app/Export/Collector/AttachmentCollector.php +++ b/app/Export/Collector/AttachmentCollector.php @@ -43,10 +43,8 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface /** * AttachmentCollector constructor. - * - * @param ExportJob $job */ - public function __construct(ExportJob $job) + public function __construct() { /** @var AttachmentRepositoryInterface repository */ $this->repository = app(AttachmentRepositoryInterface::class); @@ -54,7 +52,7 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface $this->uploadDisk = Storage::disk('upload'); $this->exportDisk = Storage::disk('export'); - parent::__construct($job); + parent::__construct(); } /** diff --git a/app/Export/Collector/BasicCollector.php b/app/Export/Collector/BasicCollector.php index 4fd571c84d..c80e8ec280 100644 --- a/app/Export/Collector/BasicCollector.php +++ b/app/Export/Collector/BasicCollector.php @@ -31,13 +31,10 @@ class BasicCollector /** * BasicCollector constructor. - * - * @param ExportJob $job */ - public function __construct(ExportJob $job) + public function __construct() { $this->entries = new Collection; - $this->job = $job; } /** @@ -56,5 +53,13 @@ class BasicCollector $this->entries = $entries; } + /** + * @param ExportJob $job + */ + public function setJob(ExportJob $job) + { + $this->job = $job; + } + } diff --git a/app/Export/Collector/CollectorInterface.php b/app/Export/Collector/CollectorInterface.php index f1778e9adc..54a3fa88a1 100644 --- a/app/Export/Collector/CollectorInterface.php +++ b/app/Export/Collector/CollectorInterface.php @@ -13,6 +13,7 @@ declare(strict_types = 1); namespace FireflyIII\Export\Collector; +use FireflyIII\Models\ExportJob; use Illuminate\Support\Collection; /** @@ -32,6 +33,13 @@ interface CollectorInterface */ public function run(): bool; + /** + * @param ExportJob $job + * + * @return mixed + */ + public function setJob(ExportJob $job); + /** * @param Collection $entries * diff --git a/app/Export/Collector/UploadCollector.php b/app/Export/Collector/UploadCollector.php index 1895cb8a5e..35c2e75188 100644 --- a/app/Export/Collector/UploadCollector.php +++ b/app/Export/Collector/UploadCollector.php @@ -14,7 +14,6 @@ declare(strict_types = 1); namespace FireflyIII\Export\Collector; use Crypt; -use FireflyIII\Models\ExportJob; use Illuminate\Contracts\Encryption\DecryptException; use Log; use Storage; @@ -35,22 +34,12 @@ class UploadCollector extends BasicCollector implements CollectorInterface /** * AttachmentCollector constructor. - * - * @param ExportJob $job */ - public function __construct(ExportJob $job) + public function __construct() { - parent::__construct($job); - - Log::debug('Going to collect attachments', ['key' => $job->key]); - - // make storage: + parent::__construct(); $this->uploadDisk = Storage::disk('upload'); $this->exportDisk = Storage::disk('export'); - - // file names associated with the old import routine. - $this->vintageFormat = sprintf('csv-upload-%d-', auth()->user()->id); - } /** @@ -60,6 +49,11 @@ class UploadCollector extends BasicCollector implements CollectorInterface */ public function run(): bool { + Log::debug('Going to collect attachments', ['key' => $this->job->key]); + + // file names associated with the old import routine. + $this->vintageFormat = sprintf('csv-upload-%d-', $this->job->user->id); + // collect old upload files (names beginning with "csv-upload". $this->collectVintageUploads(); From 77e52f42a6dd1d21b38979babe9a94bdd43437bd Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 5 Feb 2017 16:14:23 +0100 Subject: [PATCH 649/709] Update export routine. --- app/Export/Processor.php | 31 ++++++++++++++++++------------- app/Export/ProcessorInterface.php | 10 ++++++---- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/app/Export/Processor.php b/app/Export/Processor.php index 90ff2d8692..cbbdd2736b 100644 --- a/app/Export/Processor.php +++ b/app/Export/Processor.php @@ -53,21 +53,12 @@ class Processor implements ProcessorInterface /** * Processor constructor. - * - * @param array $settings */ - public function __construct(array $settings) + public function __construct() { - // save settings - $this->settings = $settings; - $this->accounts = $settings['accounts']; - $this->exportFormat = $settings['exportFormat']; - $this->includeAttachments = $settings['includeAttachments']; - $this->includeOldUploads = $settings['includeOldUploads']; - $this->job = $settings['job']; - $this->journals = new Collection; - $this->exportEntries = new Collection; - $this->files = new Collection; + $this->journals = new Collection; + $this->exportEntries = new Collection; + $this->files = new Collection; } @@ -185,6 +176,20 @@ class Processor implements ProcessorInterface return $this->files; } + /** + * @param array $settings + */ + public function setSettings(array $settings) + { + // save settings + $this->settings = $settings; + $this->accounts = $settings['accounts']; + $this->exportFormat = $settings['exportFormat']; + $this->includeAttachments = $settings['includeAttachments']; + $this->includeOldUploads = $settings['includeOldUploads']; + $this->job = $settings['job']; + } + /** * */ diff --git a/app/Export/ProcessorInterface.php b/app/Export/ProcessorInterface.php index 614d748304..540dbcaf37 100644 --- a/app/Export/ProcessorInterface.php +++ b/app/Export/ProcessorInterface.php @@ -25,11 +25,8 @@ interface ProcessorInterface /** * Processor constructor. - * - * @param array $settings - * */ - public function __construct(array $settings); + public function __construct(); /** * @return bool @@ -65,4 +62,9 @@ interface ProcessorInterface * @return Collection */ public function getFiles(): Collection; + + /** + * @param array $settings + */ + public function setSettings(array $settings); } From 8a98204a69acd1eb8acfc78178fed9435e5ba272 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 5 Feb 2017 16:16:15 +0100 Subject: [PATCH 650/709] Update app() calls --- app/Console/Commands/CreateImport.php | 3 +- .../Report/Audit/MonthReportGenerator.php | 3 +- .../Report/Budget/MonthReportGenerator.php | 3 +- .../Report/Category/MonthReportGenerator.php | 6 ++-- app/Helpers/Chart/MetaPieChart.php | 12 ++++--- app/Helpers/Collector/JournalCollector.php | 3 +- app/Helpers/Report/ReportHelper.php | 3 +- app/Http/Controllers/AccountController.php | 35 ++++++++++--------- app/Http/Controllers/BillController.php | 3 +- app/Http/Controllers/BudgetController.php | 9 +++-- app/Http/Controllers/CategoryController.php | 3 +- .../Controllers/Chart/BudgetController.php | 3 +- .../Chart/BudgetReportController.php | 3 +- .../Chart/CategoryReportController.php | 6 ++-- app/Http/Controllers/ExportController.php | 3 +- .../Controllers/Popup/ReportController.php | 21 +++++++---- app/Http/Controllers/RuleController.php | 4 +-- .../Controllers/TransactionController.php | 9 +++-- app/Http/Requests/RuleGroupFormRequest.php | 3 +- app/Import/Converter/AccountId.php | 3 +- app/Import/Converter/AssetAccountIban.php | 3 +- app/Import/Converter/AssetAccountName.php | 3 +- app/Import/Converter/AssetAccountNumber.php | 3 +- app/Import/Converter/BillId.php | 3 +- app/Import/Converter/BillName.php | 3 +- app/Import/Converter/BudgetId.php | 3 +- app/Import/Converter/BudgetName.php | 3 +- app/Import/Converter/CategoryId.php | 3 +- app/Import/Converter/CategoryName.php | 3 +- app/Import/Converter/CurrencyCode.php | 1 + app/Import/Converter/CurrencyId.php | 3 +- app/Import/Converter/CurrencyName.php | 3 +- app/Import/Converter/CurrencySymbol.php | 3 +- app/Import/Converter/OpposingAccountIban.php | 3 +- app/Import/Converter/OpposingAccountName.php | 3 +- .../Converter/OpposingAccountNumber.php | 3 +- app/Import/Converter/TagSplit.php | 3 +- app/Import/ImportProcedure.php | 3 +- app/Import/ImportStorage.php | 3 +- app/Import/ImportValidator.php | 12 ++++--- app/Import/Setup/CsvSetup.php | 3 +- ...ExecuteRuleGroupOnExistingTransactions.php | 3 +- app/Repositories/Budget/BudgetRepository.php | 6 ++-- .../Category/CategoryRepository.php | 9 +++-- app/Repositories/Tag/TagRepository.php | 6 ++-- app/Rules/Actions/SetBudget.php | 3 +- app/Rules/Actions/SetDestinationAccount.php | 3 +- app/Rules/Actions/SetSourceAccount.php | 3 +- app/Rules/TransactionMatcher.php | 3 +- app/Support/Search/Search.php | 23 ++++++------ app/Support/Search/SearchInterface.php | 6 ++++ 51 files changed, 174 insertions(+), 99 deletions(-) diff --git a/app/Console/Commands/CreateImport.php b/app/Console/Commands/CreateImport.php index 41fd83d762..00ff5094c6 100644 --- a/app/Console/Commands/CreateImport.php +++ b/app/Console/Commands/CreateImport.php @@ -79,7 +79,8 @@ class CreateImport extends Command $this->info(sprintf('Type of import: %s', $type)); /** @var ImportJobRepositoryInterface $jobRepository */ - $jobRepository = app(ImportJobRepositoryInterface::class, [$user]); + $jobRepository = app(ImportJobRepositoryInterface::class); + $jobRepository->setUser($user); $job = $jobRepository->create($type); $this->line(sprintf('Created job "%s"...', $job->key)); diff --git a/app/Generator/Report/Audit/MonthReportGenerator.php b/app/Generator/Report/Audit/MonthReportGenerator.php index 9c21b1a0ba..02cc9d08c2 100644 --- a/app/Generator/Report/Audit/MonthReportGenerator.php +++ b/app/Generator/Report/Audit/MonthReportGenerator.php @@ -139,7 +139,8 @@ class MonthReportGenerator implements ReportGeneratorInterface { /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAccounts(new Collection([$account]))->setRange($this->start, $this->end); $journals = $collector->getJournals(); $journals = $journals->reverse(); diff --git a/app/Generator/Report/Budget/MonthReportGenerator.php b/app/Generator/Report/Budget/MonthReportGenerator.php index 38a53aad79..3ab5227ecc 100644 --- a/app/Generator/Report/Budget/MonthReportGenerator.php +++ b/app/Generator/Report/Budget/MonthReportGenerator.php @@ -185,7 +185,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface } /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) ->setTypes([TransactionType::WITHDRAWAL]) ->setBudgets($this->budgets)->withOpposingAccount()->disableFilter(); diff --git a/app/Generator/Report/Category/MonthReportGenerator.php b/app/Generator/Report/Category/MonthReportGenerator.php index 5517d7be13..865121006e 100644 --- a/app/Generator/Report/Category/MonthReportGenerator.php +++ b/app/Generator/Report/Category/MonthReportGenerator.php @@ -195,7 +195,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface } /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) ->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) ->setCategories($this->categories)->withOpposingAccount()->disableFilter(); @@ -218,7 +219,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface } /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) ->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) ->setCategories($this->categories)->withOpposingAccount(); diff --git a/app/Helpers/Chart/MetaPieChart.php b/app/Helpers/Chart/MetaPieChart.php index fbeb70cc25..e83b103ba0 100644 --- a/app/Helpers/Chart/MetaPieChart.php +++ b/app/Helpers/Chart/MetaPieChart.php @@ -85,7 +85,8 @@ class MetaPieChart implements MetaPieChartInterface // also collect all other transactions if ($this->collectOtherObjects && $direction === 'expense') { /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [$this->user]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser($this->user); $collector->setAccounts($this->accounts)->setRange($this->start, $this->end)->setTypes([TransactionType::WITHDRAWAL]); $journals = $collector->getJournals(); $sum = strval($journals->sum('transaction_amount')); @@ -96,7 +97,8 @@ class MetaPieChart implements MetaPieChartInterface if ($this->collectOtherObjects && $direction === 'income') { /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAccounts($this->accounts)->setRange($this->start, $this->end)->setTypes([TransactionType::DEPOSIT]); $journals = $collector->getJournals(); $sum = strval($journals->sum('transaction_amount')); @@ -201,7 +203,8 @@ class MetaPieChart implements MetaPieChartInterface $modifier = 1; } /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAccounts($this->accounts); $collector->setRange($this->start, $this->end); $collector->setTypes($types); @@ -258,7 +261,8 @@ class MetaPieChart implements MetaPieChartInterface { $chartData = []; $names = []; - $repository = app($this->repositories[$type], [$this->user]); + $repository = app($this->repositories[$type]); + $repository->setUser($this->user); foreach ($array as $objectId => $amount) { if (!isset($names[$objectId])) { $object = $repository->find(intval($objectId)); diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index 7952ed2e2f..6e2d7a0d8e 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -233,7 +233,8 @@ class JournalCollector implements JournalCollectorInterface public function setAllAssetAccounts(): JournalCollectorInterface { /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class, [$this->user]); + $repository = app(AccountRepositoryInterface::class); + $repository->setUser($this->user); $accounts = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]); if ($accounts->count() > 0) { $accountIds = $accounts->pluck('id')->toArray(); diff --git a/app/Helpers/Report/ReportHelper.php b/app/Helpers/Report/ReportHelper.php index 40b7b68eaf..f49805cd1b 100644 --- a/app/Helpers/Report/ReportHelper.php +++ b/app/Helpers/Report/ReportHelper.php @@ -68,7 +68,8 @@ class ReportHelper implements ReportHelperInterface /** @var BillRepositoryInterface $repository */ $repository = app(BillRepositoryInterface::class); $bills = $repository->getBillsForAccounts($accounts); - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAccounts($accounts)->setRange($start, $end)->setBills($bills); $journals = $collector->getJournals(); $collection = new BillCollection; diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index b3b007cb9e..92d06ece6f 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -23,7 +23,6 @@ use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use FireflyIII\Models\Transaction; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\CacheProperties; @@ -97,12 +96,12 @@ class AccountController extends Controller } /** - * @param ARI $repository - * @param Account $account + * @param AccountRepositoryInterface $repository + * @param Account $account * * @return View */ - public function delete(ARI $repository, Account $account) + public function delete(AccountRepositoryInterface $repository, Account $account) { $typeName = config('firefly.shortNamesByFullName.' . $account->accountType->type); $subTitle = trans('firefly.delete_' . $typeName . '_account', ['name' => $account->name]); @@ -119,12 +118,12 @@ class AccountController extends Controller /** * @param Request $request - * @param ARI $repository + * @param AccountRepositoryInterface $repository * @param Account $account * * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ - public function destroy(Request $request, ARI $repository, Account $account) + public function destroy(Request $request, AccountRepositoryInterface $repository, Account $account) { $type = $account->accountType->type; $typeName = config('firefly.shortNamesByFullName.' . $type); @@ -199,12 +198,12 @@ class AccountController extends Controller } /** - * @param ARI $repository + * @param AccountRepositoryInterface $repository * @param string $what * * @return View */ - public function index(ARI $repository, string $what) + public function index(AccountRepositoryInterface $repository, string $what) { $what = $what ?? 'asset'; $subTitle = trans('firefly.' . $what . '_accounts'); @@ -270,7 +269,7 @@ class AccountController extends Controller /** * @param Request $request - * @param ARI $repository + * @param AccountRepositoryInterface $repository * @param Account $account * * @return View @@ -284,7 +283,8 @@ class AccountController extends Controller // replace with journal collector: /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page); $journals = $collector->getPaginatedJournals(); $journals->setPath('accounts/show/' . $account->id . '/all'); @@ -318,7 +318,8 @@ class AccountController extends Controller // replace with journal collector: /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page); $journals = $collector->getPaginatedJournals(); $journals->setPath('accounts/show/' . $account->id . '/' . $date); @@ -332,12 +333,12 @@ class AccountController extends Controller /** * @param AccountFormRequest $request - * @param ARI $repository + * @param AccountRepositoryInterface $repository * * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector * */ - public function store(AccountFormRequest $request, ARI $repository) + public function store(AccountFormRequest $request, AccountRepositoryInterface $repository) { $data = $request->getAccountData(); $account = $repository->store($data); @@ -365,12 +366,12 @@ class AccountController extends Controller /** * @param AccountFormRequest $request - * @param ARI $repository + * @param AccountRepositoryInterface $repository * @param Account $account * * @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ - public function update(AccountFormRequest $request, ARI $repository, Account $account) + public function update(AccountFormRequest $request, AccountRepositoryInterface $repository, Account $account) { $data = $request->getAccountData(); $repository->update($account, $data); @@ -417,8 +418,8 @@ class AccountController extends Controller */ private function periodEntries(Account $account): Collection { - /** @var ARI $repository */ - $repository = app(ARI::class); + /** @var AccountRepositoryInterface $repository */ + $repository = app(AccountRepositoryInterface::class); /** @var AccountTaskerInterface $tasker */ $tasker = app(AccountTaskerInterface::class); diff --git a/app/Http/Controllers/BillController.php b/app/Http/Controllers/BillController.php index 0642dd260b..913ac09208 100644 --- a/app/Http/Controllers/BillController.php +++ b/app/Http/Controllers/BillController.php @@ -215,7 +215,8 @@ class BillController extends Controller // use collector: /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->setLimit($pageSize)->setPage($page)->withBudgetInformation() ->withCategoryInformation(); $journals = $collector->getPaginatedJournals(); diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index 0d8ce65937..aaf56a50ec 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -212,7 +212,8 @@ class BudgetController extends Controller // collector /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withoutBudget(); $journals = $collector->getPaginatedJournals(); $journals->setPath('/budgets/list/noBudget'); @@ -253,7 +254,8 @@ class BudgetController extends Controller $repetition = null; // collector: /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page)->withCategoryInformation(); $journals = $collector->getPaginatedJournals(); $journals->setPath('/budgets/show/' . $budget->id); @@ -290,7 +292,8 @@ class BudgetController extends Controller // collector: /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAllAssetAccounts()->setRange($budgetLimit->start_date, $budgetLimit->end_date) ->setBudget($budget)->setLimit($pageSize)->setPage($page)->withCategoryInformation(); $journals = $collector->getPaginatedJournals(); diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index 2a52505818..81f1e230d9 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -165,7 +165,8 @@ class CategoryController extends Controller // new collector: /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAllAssetAccounts()->setRange($start, $end)->withoutCategory();//->groupJournals(); $journals = $collector->getJournals(); $subTitle = trans( diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index 0a8fe543cf..2c792fa908 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -416,7 +416,8 @@ class BudgetController extends Controller { // collector /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $types = [TransactionType::WITHDRAWAL]; $collector->setAllAssetAccounts()->setTypes($types)->setRange($start, $end)->withoutBudget(); $journals = $collector->getJournals(); diff --git a/app/Http/Controllers/Chart/BudgetReportController.php b/app/Http/Controllers/Chart/BudgetReportController.php index d131f8a661..b5ab26355c 100644 --- a/app/Http/Controllers/Chart/BudgetReportController.php +++ b/app/Http/Controllers/Chart/BudgetReportController.php @@ -239,7 +239,8 @@ class BudgetReportController extends Controller private function getExpenses(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end): Collection { /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) ->setBudgets($budgets)->withOpposingAccount()->disableFilter(); $accountIds = $accounts->pluck('id')->toArray(); diff --git a/app/Http/Controllers/Chart/CategoryReportController.php b/app/Http/Controllers/Chart/CategoryReportController.php index e156becd7e..0cd50c308c 100644 --- a/app/Http/Controllers/Chart/CategoryReportController.php +++ b/app/Http/Controllers/Chart/CategoryReportController.php @@ -284,7 +284,8 @@ class CategoryReportController extends Controller private function getExpenses(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): Collection { /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) ->setCategories($categories)->withOpposingAccount()->disableFilter(); $accountIds = $accounts->pluck('id')->toArray(); @@ -305,7 +306,8 @@ class CategoryReportController extends Controller private function getIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): Collection { /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) ->setCategories($categories)->withOpposingAccount(); $accountIds = $accounts->pluck('id')->toArray(); diff --git a/app/Http/Controllers/ExportController.php b/app/Http/Controllers/ExportController.php index 1ace8118c1..823c82e928 100644 --- a/app/Http/Controllers/ExportController.php +++ b/app/Http/Controllers/ExportController.php @@ -150,7 +150,8 @@ class ExportController extends Controller $jobs->changeStatus($job, 'export_status_make_exporter'); /** @var ProcessorInterface $processor */ - $processor = app(ProcessorInterface::class, [$settings]); + $processor = app(ProcessorInterface::class); + $processor->setSettings($settings); /* * Collect journals: diff --git a/app/Http/Controllers/Popup/ReportController.php b/app/Http/Controllers/Popup/ReportController.php index 0f436dc962..7bd8803fa5 100644 --- a/app/Http/Controllers/Popup/ReportController.php +++ b/app/Http/Controllers/Popup/ReportController.php @@ -103,7 +103,8 @@ class ReportController extends Controller switch (true) { case ($role === BalanceLine::ROLE_DEFAULTROLE && !is_null($budget->id)): /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector ->setAccounts(new Collection([$account])) ->setRange($attributes['startDate'], $attributes['endDate']) @@ -114,7 +115,8 @@ class ReportController extends Controller case ($role === BalanceLine::ROLE_DEFAULTROLE && is_null($budget->id)): $budget->name = strval(trans('firefly.no_budget')); /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector ->setAccounts(new Collection([$account])) ->setTypes($types) @@ -124,7 +126,8 @@ class ReportController extends Controller break; case ($role === BalanceLine::ROLE_DIFFROLE): /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector ->setAccounts(new Collection([$account])) ->setTypes($types) @@ -169,7 +172,8 @@ class ReportController extends Controller $repository = app(BudgetRepositoryInterface::class); $budget = $repository->find(intval($attributes['budgetId'])); /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector ->setAccounts($attributes['accounts']) @@ -203,7 +207,8 @@ class ReportController extends Controller $category = $repository->find(intval($attributes['categoryId'])); $types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]; /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAccounts($attributes['accounts'])->setTypes($types) ->setRange($attributes['startDate'], $attributes['endDate']) ->setCategory($category); @@ -230,7 +235,8 @@ class ReportController extends Controller $account = $repository->find(intval($attributes['accountId'])); $types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]; /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setTypes($types); $journals = $collector->getJournals(); $report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report @@ -266,7 +272,8 @@ class ReportController extends Controller $account = $repository->find(intval($attributes['accountId'])); $types = [TransactionType::DEPOSIT, TransactionType::TRANSFER]; /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setTypes($types); $journals = $collector->getJournals(); $report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 8e0e466933..1d6663835a 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -293,7 +293,7 @@ class RuleController extends Controller $range = config('firefly.test-triggers.range'); /** @var TransactionMatcher $matcher */ - $matcher = app('FireflyIII\Rules\TransactionMatcher'); + $matcher = app(TransactionMatcher::class); $matcher->setLimit($limit); $matcher->setRange($range); $matcher->setTriggers($triggers); @@ -357,7 +357,7 @@ class RuleController extends Controller private function createDefaultRule() { /** @var RuleRepositoryInterface $repository */ - $repository = app('FireflyIII\Repositories\Rule\RuleRepositoryInterface'); + $repository = app(RuleRepositoryInterface::class); if ($repository->count() === 0) { $data = [ diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 2ec3e8ce5c..ba38605b8f 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -72,7 +72,8 @@ class TransactionController extends Controller $end = session('end', Navigation::endOfPeriod(new Carbon, $range)); /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts(); $collector->setRange($start, $end)->withBudgetInformation()->withCategoryInformation(); @@ -122,7 +123,8 @@ class TransactionController extends Controller $subTitle = sprintf('%s (%s)', trans('firefly.title_' . $what), strtolower(trans('firefly.everything'))); $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->withBudgetInformation()->withCategoryInformation(); // do not filter transfers if $what = transfer. @@ -161,7 +163,8 @@ class TransactionController extends Controller Log::debug(sprintf('Transaction index by date will show between %s and %s', $start->format('Y-m-d'), $end->format('Y-m-d'))); /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts(); $collector->setRange($start, $end)->withBudgetInformation()->withCategoryInformation(); diff --git a/app/Http/Requests/RuleGroupFormRequest.php b/app/Http/Requests/RuleGroupFormRequest.php index fcf92d0105..b5917242d7 100644 --- a/app/Http/Requests/RuleGroupFormRequest.php +++ b/app/Http/Requests/RuleGroupFormRequest.php @@ -49,7 +49,8 @@ class RuleGroupFormRequest extends Request public function rules() { /** @var RuleGroupRepositoryInterface $repository */ - $repository = app(RuleGroupRepositoryInterface::class, [auth()->user()]); + $repository = app(RuleGroupRepositoryInterface::class); + $repository->setUser(auth()->user()); $titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title'; if (!is_null($repository->find(intval($this->get('id')))->id)) { $titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title,' . intval($this->get('id')); diff --git a/app/Import/Converter/AccountId.php b/app/Import/Converter/AccountId.php index 5b39999f2e..4c07790fa5 100644 --- a/app/Import/Converter/AccountId.php +++ b/app/Import/Converter/AccountId.php @@ -40,7 +40,8 @@ class AccountId extends BasicConverter implements ConverterInterface return new Account; } /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class, [$this->user]); + $repository = app(AccountRepositoryInterface::class); + $repository->setUser($this->user); if (isset($this->mapping[$value])) { Log::debug('Found account in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); diff --git a/app/Import/Converter/AssetAccountIban.php b/app/Import/Converter/AssetAccountIban.php index 9ed749a781..d2336c282d 100644 --- a/app/Import/Converter/AssetAccountIban.php +++ b/app/Import/Converter/AssetAccountIban.php @@ -43,7 +43,8 @@ class AssetAccountIban extends BasicConverter implements ConverterInterface } /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class, [$this->user]); + $repository = app(AccountRepositoryInterface::class); + $repository->setUser($this->user); if (isset($this->mapping[$value])) { diff --git a/app/Import/Converter/AssetAccountName.php b/app/Import/Converter/AssetAccountName.php index 4785ae16d2..8618d52f2d 100644 --- a/app/Import/Converter/AssetAccountName.php +++ b/app/Import/Converter/AssetAccountName.php @@ -43,7 +43,8 @@ class AssetAccountName extends BasicConverter implements ConverterInterface } /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class, [$this->user]); + $repository = app(AccountRepositoryInterface::class); + $repository->setUser($this->user); if (isset($this->mapping[$value])) { diff --git a/app/Import/Converter/AssetAccountNumber.php b/app/Import/Converter/AssetAccountNumber.php index 5c71bf57f1..d389492c91 100644 --- a/app/Import/Converter/AssetAccountNumber.php +++ b/app/Import/Converter/AssetAccountNumber.php @@ -41,7 +41,8 @@ class AssetAccountNumber extends BasicConverter implements ConverterInterface } /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class, [$this->user]); + $repository = app(AccountRepositoryInterface::class); + $repository->setUser($this->user); if (isset($this->mapping[$value])) { diff --git a/app/Import/Converter/BillId.php b/app/Import/Converter/BillId.php index 06713afaf4..cb20e4c7c9 100644 --- a/app/Import/Converter/BillId.php +++ b/app/Import/Converter/BillId.php @@ -42,7 +42,8 @@ class BillId extends BasicConverter implements ConverterInterface } /** @var BillRepositoryInterface $repository */ - $repository = app(BillRepositoryInterface::class, [$this->user]); + $repository = app(BillRepositoryInterface::class); + $repository->setUser($this->user); if (isset($this->mapping[$value])) { Log::debug('Found bill in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); diff --git a/app/Import/Converter/BillName.php b/app/Import/Converter/BillName.php index 6c4aa2fc17..3d2dbe9a71 100644 --- a/app/Import/Converter/BillName.php +++ b/app/Import/Converter/BillName.php @@ -44,7 +44,8 @@ class BillName extends BasicConverter implements ConverterInterface } /** @var BillRepositoryInterface $repository */ - $repository = app(BillRepositoryInterface::class, [$this->user]); + $repository = app(BillRepositoryInterface::class); + $repository->setUser($this->user); if (isset($this->mapping[$value])) { Log::debug('Found bill in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); diff --git a/app/Import/Converter/BudgetId.php b/app/Import/Converter/BudgetId.php index 0fe769f70e..cf709c0beb 100644 --- a/app/Import/Converter/BudgetId.php +++ b/app/Import/Converter/BudgetId.php @@ -42,7 +42,8 @@ class BudgetId extends BasicConverter implements ConverterInterface } /** @var BudgetRepositoryInterface $repository */ - $repository = app(BudgetRepositoryInterface::class, [$this->user]); + $repository = app(BudgetRepositoryInterface::class); + $repository->setUser($this->user); if (isset($this->mapping[$value])) { Log::debug('Found budget in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); diff --git a/app/Import/Converter/BudgetName.php b/app/Import/Converter/BudgetName.php index fc5d5416fc..7ecd85530c 100644 --- a/app/Import/Converter/BudgetName.php +++ b/app/Import/Converter/BudgetName.php @@ -42,7 +42,8 @@ class BudgetName extends BasicConverter implements ConverterInterface } /** @var BudgetRepositoryInterface $repository */ - $repository = app(BudgetRepositoryInterface::class, [$this->user]); + $repository = app(BudgetRepositoryInterface::class); + $repository->setUser($this->user); if (isset($this->mapping[$value])) { Log::debug('Found budget in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); diff --git a/app/Import/Converter/CategoryId.php b/app/Import/Converter/CategoryId.php index 5d538f4b90..2544a61597 100644 --- a/app/Import/Converter/CategoryId.php +++ b/app/Import/Converter/CategoryId.php @@ -42,7 +42,8 @@ class CategoryId extends BasicConverter implements ConverterInterface } /** @var CategoryRepositoryInterface $repository */ - $repository = app(CategoryRepositoryInterface::class, [$this->user]); + $repository = app(CategoryRepositoryInterface::class); + $repository->setUser($this->user); if (isset($this->mapping[$value])) { Log::debug('Found category in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); diff --git a/app/Import/Converter/CategoryName.php b/app/Import/Converter/CategoryName.php index 28e7650c1f..f8af2414b1 100644 --- a/app/Import/Converter/CategoryName.php +++ b/app/Import/Converter/CategoryName.php @@ -42,7 +42,8 @@ class CategoryName extends BasicConverter implements ConverterInterface } /** @var CategoryRepositoryInterface $repository */ - $repository = app(CategoryRepositoryInterface::class, [$this->user]); + $repository = app(CategoryRepositoryInterface::class); + $repository->setUser($this->user); if (isset($this->mapping[$value])) { Log::debug('Found category in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); diff --git a/app/Import/Converter/CurrencyCode.php b/app/Import/Converter/CurrencyCode.php index 48901cceaa..f78433ddf3 100644 --- a/app/Import/Converter/CurrencyCode.php +++ b/app/Import/Converter/CurrencyCode.php @@ -36,6 +36,7 @@ class CurrencyCode extends BasicConverter implements ConverterInterface /** @var CurrencyRepositoryInterface $repository */ $repository = app(CurrencyRepositoryInterface::class); + $repository->setUser($this->user); if (isset($this->mapping[$value])) { Log::debug('Found currency in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); diff --git a/app/Import/Converter/CurrencyId.php b/app/Import/Converter/CurrencyId.php index 6622899264..299cde1bf3 100644 --- a/app/Import/Converter/CurrencyId.php +++ b/app/Import/Converter/CurrencyId.php @@ -42,7 +42,8 @@ class CurrencyId extends BasicConverter implements ConverterInterface } /** @var CurrencyRepositoryInterface $repository */ - $repository = app(CurrencyRepositoryInterface::class, [$this->user]); + $repository = app(CurrencyRepositoryInterface::class); + $repository->setUser($this->user); if (isset($this->mapping[$value])) { Log::debug('Found currency in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); diff --git a/app/Import/Converter/CurrencyName.php b/app/Import/Converter/CurrencyName.php index ad28107f4b..71af377582 100644 --- a/app/Import/Converter/CurrencyName.php +++ b/app/Import/Converter/CurrencyName.php @@ -42,7 +42,8 @@ class CurrencyName extends BasicConverter implements ConverterInterface } /** @var CurrencyRepositoryInterface $repository */ - $repository = app(CurrencyRepositoryInterface::class, [$this->user]); + $repository = app(CurrencyRepositoryInterface::class); + $repository->setUser($this->user); if (isset($this->mapping[$value])) { Log::debug('Found currency in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); diff --git a/app/Import/Converter/CurrencySymbol.php b/app/Import/Converter/CurrencySymbol.php index 307d409cd0..27ed50dd48 100644 --- a/app/Import/Converter/CurrencySymbol.php +++ b/app/Import/Converter/CurrencySymbol.php @@ -42,7 +42,8 @@ class CurrencySymbol extends BasicConverter implements ConverterInterface } /** @var CurrencyRepositoryInterface $repository */ - $repository = app(CurrencyRepositoryInterface::class, [$this->user]); + $repository = app(CurrencyRepositoryInterface::class); + $repository->setUser($this->user); if (isset($this->mapping[$value])) { Log::debug('Found currency in mapping. Should exist.', ['value' => $value, 'map' => $this->mapping[$value]]); diff --git a/app/Import/Converter/OpposingAccountIban.php b/app/Import/Converter/OpposingAccountIban.php index c3dacbd712..ee8b40747c 100644 --- a/app/Import/Converter/OpposingAccountIban.php +++ b/app/Import/Converter/OpposingAccountIban.php @@ -43,7 +43,8 @@ class OpposingAccountIban extends BasicConverter implements ConverterInterface } /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class, [$this->user]); + $repository = app(AccountRepositoryInterface::class); + $repository->setUser($this->user); if (isset($this->mapping[$value])) { diff --git a/app/Import/Converter/OpposingAccountName.php b/app/Import/Converter/OpposingAccountName.php index 4516873a25..fa51245fd6 100644 --- a/app/Import/Converter/OpposingAccountName.php +++ b/app/Import/Converter/OpposingAccountName.php @@ -42,7 +42,8 @@ class OpposingAccountName extends BasicConverter implements ConverterInterface } /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class, [$this->user]); + $repository = app(AccountRepositoryInterface::class); + $repository->setUser($this->user); if (isset($this->mapping[$value])) { diff --git a/app/Import/Converter/OpposingAccountNumber.php b/app/Import/Converter/OpposingAccountNumber.php index 86df680223..d513a88ae0 100644 --- a/app/Import/Converter/OpposingAccountNumber.php +++ b/app/Import/Converter/OpposingAccountNumber.php @@ -43,7 +43,8 @@ class OpposingAccountNumber extends BasicConverter implements ConverterInterface } /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class, [$this->user]); + $repository = app(AccountRepositoryInterface::class); + $repository->setUser($this->user); if (isset($this->mapping[$value])) { diff --git a/app/Import/Converter/TagSplit.php b/app/Import/Converter/TagSplit.php index cc99561663..d93074b7d0 100644 --- a/app/Import/Converter/TagSplit.php +++ b/app/Import/Converter/TagSplit.php @@ -39,7 +39,8 @@ class TagSplit Log::debug('Exploded parts.', $parts); /** @var TagRepositoryInterface $repository */ - $repository = app(TagRepositoryInterface::class, [$user]); + $repository = app(TagRepositoryInterface::class); + $repository->setUser($user); /** @var string $part */ diff --git a/app/Import/ImportProcedure.php b/app/Import/ImportProcedure.php index c535d47fae..991c5addcd 100644 --- a/app/Import/ImportProcedure.php +++ b/app/Import/ImportProcedure.php @@ -58,7 +58,8 @@ class ImportProcedure implements ImportProcedureInterface if ($job->configuration['import-account'] != 0) { /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class, [$job->user]); + $repository = app(AccountRepositoryInterface::class); + $repository->setUser($job->user); $validator->setDefaultImportAccount($repository->find($job->configuration['import-account'])); } diff --git a/app/Import/ImportStorage.php b/app/Import/ImportStorage.php index 4edf17d5f8..12b5e33a6c 100644 --- a/app/Import/ImportStorage.php +++ b/app/Import/ImportStorage.php @@ -159,7 +159,8 @@ class ImportStorage private function createImportTag(): Tag { /** @var TagRepositoryInterface $repository */ - $repository = app(TagRepositoryInterface::class, [$this->user]); + $repository = app(TagRepositoryInterface::class); + $repository->setUser($this->user); $data = [ 'tag' => trans('firefly.import_with_key', ['key' => $this->job->key]), 'date' => new Carbon, diff --git a/app/Import/ImportValidator.php b/app/Import/ImportValidator.php index d6d15d3ed2..9a5a254151 100644 --- a/app/Import/ImportValidator.php +++ b/app/Import/ImportValidator.php @@ -177,7 +177,8 @@ class ImportValidator // find it first by new type: /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class, [$this->user]); + $repository = app(AccountRepositoryInterface::class); + $repository->setUser($this->user); $result = $repository->findByName($account->name, [$type]); if (is_null($result->id)) { @@ -214,7 +215,8 @@ class ImportValidator { /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class, [$this->user]); + $repository = app(AccountRepositoryInterface::class); + $repository->setUser($this->user); $name = 'Unknown expense account'; $result = $repository->findByName($name, [AccountType::EXPENSE]); @@ -235,7 +237,8 @@ class ImportValidator { /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class, [$this->user]); + $repository = app(AccountRepositoryInterface::class); + $repository->setUser($this->user); $name = 'Unknown revenue account'; $result = $repository->findByName($name, [AccountType::REVENUE]); @@ -382,7 +385,8 @@ class ImportValidator { if (is_null($entry->fields['currency'])) { /** @var CurrencyRepositoryInterface $repository */ - $repository = app(CurrencyRepositoryInterface::class, [$this->user]); + $repository = app(CurrencyRepositoryInterface::class); + $repository->setUser($this->user); // is the default currency for the user or the system $defaultCode = Preferences::getForUser($this->user, 'currencyPreference', config('firefly.default_currency', 'EUR'))->data; diff --git a/app/Import/Setup/CsvSetup.php b/app/Import/Setup/CsvSetup.php index 20c938bda3..ff20d45958 100644 --- a/app/Import/Setup/CsvSetup.php +++ b/app/Import/Setup/CsvSetup.php @@ -181,7 +181,8 @@ class CsvSetup implements SetupInterface public function saveImportConfiguration(array $data, FileBag $files): bool { /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class, [auth()->user()]); + $repository = app(AccountRepositoryInterface::class); + $repository->setUser(auth()->user()); $importId = $data['csv_import_account'] ?? 0; $account = $repository->find(intval($importId)); diff --git a/app/Jobs/ExecuteRuleGroupOnExistingTransactions.php b/app/Jobs/ExecuteRuleGroupOnExistingTransactions.php index 1678cb2bcc..15a852414e 100644 --- a/app/Jobs/ExecuteRuleGroupOnExistingTransactions.php +++ b/app/Jobs/ExecuteRuleGroupOnExistingTransactions.php @@ -156,7 +156,8 @@ class ExecuteRuleGroupOnExistingTransactions extends Job implements ShouldQueue protected function collectJournals() { /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAccounts($this->accounts)->setRange($this->startDate, $this->endDate); return $collector->getJournals(); diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 8f0588dc1a..9b4f985390 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -442,7 +442,8 @@ class BudgetRepository implements BudgetRepositoryInterface public function spentInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): string { /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [$this->user]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser($this->user); $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setBudgets($budgets); if ($accounts->count() > 0) { @@ -468,7 +469,8 @@ class BudgetRepository implements BudgetRepositoryInterface public function spentInPeriodWoBudget(Collection $accounts, Carbon $start, Carbon $end): string { /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [$this->user]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser($this->user); $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->withoutBudget(); if ($accounts->count() > 0) { diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index fb366c0812..bed2d5847f 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -57,7 +57,8 @@ class CategoryRepository implements CategoryRepositoryInterface public function earnedInPeriod(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string { /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [$this->user]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser($this->user); $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($accounts)->setCategories($categories); $set = $collector->getJournals(); $sum = strval($set->sum('transaction_amount')); @@ -393,7 +394,8 @@ class CategoryRepository implements CategoryRepositoryInterface public function spentInPeriod(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string { /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [$this->user]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser($this->user); $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setCategories($categories); @@ -421,7 +423,8 @@ class CategoryRepository implements CategoryRepositoryInterface public function spentInPeriodWithoutCategory(Collection $accounts, Carbon $start, Carbon $end): string { /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [$this->user]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser($this->user); $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->withoutCategory(); if ($accounts->count() > 0) { diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index 1b0950467f..9f91816252 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -394,7 +394,8 @@ class TagRepository implements TagRepositoryInterface public function earnedInPeriod(Tag $tag, Carbon $start, Carbon $end): string { /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [$this->user]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser($this->user); $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAllAssetAccounts()->setTag($tag); $set = $collector->getJournals(); $sum = strval($set->sum('transaction_amount')); @@ -412,7 +413,8 @@ class TagRepository implements TagRepositoryInterface public function spentInPeriod(Tag $tag, Carbon $start, Carbon $end): string { /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [$this->user]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser($this->user); $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAllAssetAccounts()->setTag($tag); $set = $collector->getJournals(); $sum = strval($set->sum('transaction_amount')); diff --git a/app/Rules/Actions/SetBudget.php b/app/Rules/Actions/SetBudget.php index 90d7aa6d18..e9f8c1fae9 100644 --- a/app/Rules/Actions/SetBudget.php +++ b/app/Rules/Actions/SetBudget.php @@ -50,7 +50,8 @@ class SetBudget implements ActionInterface public function act(TransactionJournal $journal): bool { /** @var BudgetRepositoryInterface $repository */ - $repository = app(BudgetRepositoryInterface::class, [$journal->user]); + $repository = app(BudgetRepositoryInterface::class); + $repository->setUser($journal->user); $search = $this->action->action_value; $budgets = $repository->getActiveBudgets(); $budget = $budgets->filter( diff --git a/app/Rules/Actions/SetDestinationAccount.php b/app/Rules/Actions/SetDestinationAccount.php index 829c4928c4..25677e546b 100644 --- a/app/Rules/Actions/SetDestinationAccount.php +++ b/app/Rules/Actions/SetDestinationAccount.php @@ -60,7 +60,8 @@ class SetDestinationAccount implements ActionInterface public function act(TransactionJournal $journal): bool { $this->journal = $journal; - $this->repository = app(AccountRepositoryInterface::class, [$journal->user]); + $this->repository = app(AccountRepositoryInterface::class); + $this->repository->setUser($journal->user); $count = $journal->transactions()->count(); if ($count > 2) { Log::error(sprintf('Cannot change destination account of journal #%d because it is a split journal.', $journal->id)); diff --git a/app/Rules/Actions/SetSourceAccount.php b/app/Rules/Actions/SetSourceAccount.php index 5e625fe886..fc8067856c 100644 --- a/app/Rules/Actions/SetSourceAccount.php +++ b/app/Rules/Actions/SetSourceAccount.php @@ -60,7 +60,8 @@ class SetSourceAccount implements ActionInterface public function act(TransactionJournal $journal): bool { $this->journal = $journal; - $this->repository = app(AccountRepositoryInterface::class, [$journal->user]); + $this->repository = app(AccountRepositoryInterface::class); + $this->repository->setUser($journal->user); $count = $journal->transactions()->count(); if ($count > 2) { Log::error(sprintf('Cannot change source account of journal #%d because it is a split journal.', $journal->id)); diff --git a/app/Rules/TransactionMatcher.php b/app/Rules/TransactionMatcher.php index acac7c8521..5241e4f84c 100644 --- a/app/Rules/TransactionMatcher.php +++ b/app/Rules/TransactionMatcher.php @@ -78,7 +78,8 @@ class TransactionMatcher do { // Fetch a batch of transactions from the database /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [auth()->user()]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser(auth()->user()); $collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page)->setTypes($this->transactionTypes); $set = $collector->getPaginatedJournals(); Log::debug(sprintf('Found %d journals to check. ', $set->count())); diff --git a/app/Support/Search/Search.php b/app/Support/Search/Search.php index c71e1e2985..b37af85daa 100644 --- a/app/Support/Search/Search.php +++ b/app/Support/Search/Search.php @@ -37,16 +37,6 @@ class Search implements SearchInterface /** @var User */ private $user; - /** - * AttachmentRepository constructor. - * - * @param User $user - */ - public function __construct(User $user) - { - $this->user = $user; - } - /** * The search will assume that the user does not have so many accounts * that this search should be paginated. @@ -164,7 +154,8 @@ class Search implements SearchInterface $result = new Collection(); do { /** @var JournalCollectorInterface $collector */ - $collector = app(JournalCollectorInterface::class, [$this->user]); + $collector = app(JournalCollectorInterface::class); + $collector->setUser($this->user); $collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page); $set = $collector->getPaginatedJournals(); Log::debug(sprintf('Found %d journals to check. ', $set->count())); @@ -222,6 +213,14 @@ class Search implements SearchInterface $this->limit = $limit; } + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } + /** * @param string $haystack * @param array $needle @@ -241,4 +240,4 @@ class Search implements SearchInterface return false; } -} +} diff --git a/app/Support/Search/SearchInterface.php b/app/Support/Search/SearchInterface.php index bd16c78853..ed9c3c6c88 100644 --- a/app/Support/Search/SearchInterface.php +++ b/app/Support/Search/SearchInterface.php @@ -13,6 +13,7 @@ declare(strict_types = 1); namespace FireflyIII\Support\Search; +use FireflyIII\User; use Illuminate\Support\Collection; /** @@ -29,6 +30,11 @@ interface SearchInterface */ public function searchAccounts(array $words): Collection; + /** + * @param User $user + */ + public function setUser(User $user); + /** * @param array $words * From 3a7faa736836aa626ab4aa21cfaa57fe3db8fc85 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 5 Feb 2017 18:44:37 +0100 Subject: [PATCH 651/709] Small fixes for laravel 5.4 --- app/Http/Controllers/Controller.php | 3 --- app/Http/Controllers/Transaction/SingleController.php | 1 - config/app.php | 2 ++ 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index d2233d75fb..bbf6cb51d5 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -83,12 +83,9 @@ class Controller extends BaseController protected function getPreviousUri(string $identifier): string { $uri = strval(session($identifier)); - // 1 (see above): if (!(strpos($identifier, 'delete') === false) && !(strpos($uri, '/show/') === false)) { $uri = route('index'); } - - // 2 (see above) if (!(strpos($uri, 'javascript') === false)) { $uri = route('index'); } diff --git a/app/Http/Controllers/Transaction/SingleController.php b/app/Http/Controllers/Transaction/SingleController.php index c031443e1c..4a70121724 100644 --- a/app/Http/Controllers/Transaction/SingleController.php +++ b/app/Http/Controllers/Transaction/SingleController.php @@ -190,7 +190,6 @@ class SingleController extends Controller if ($this->isOpeningBalance($transactionJournal)) { return $this->redirectToAccount($transactionJournal); } - $journalId = $transactionJournal->id; $type = TransactionJournal::transactionTypeStr($transactionJournal); Session::flash('success', strval(trans('firefly.deleted_' . strtolower($type), ['description' => e($transactionJournal->description)]))); diff --git a/config/app.php b/config/app.php index 1bbf9d2452..a990e181f9 100755 --- a/config/app.php +++ b/config/app.php @@ -50,6 +50,8 @@ return [ Illuminate\Translation\TranslationServiceProvider::class, Illuminate\Validation\ValidationServiceProvider::class, Illuminate\View\ViewServiceProvider::class, + Collective\Html\HtmlServiceProvider::class, + /* * Application Service Providers... From c6f69f63fc5c278b38e51d4b67f4ca34779cf46a Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 5 Feb 2017 18:49:13 +0100 Subject: [PATCH 652/709] Friendly error message. [skip ci] --- resources/views/errors/FireflyException.twig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/resources/views/errors/FireflyException.twig b/resources/views/errors/FireflyException.twig index 9d8343e04c..0e3499c8f3 100644 --- a/resources/views/errors/FireflyException.twig +++ b/resources/views/errors/FireflyException.twig @@ -38,6 +38,20 @@ </p> </div> </div> + {% if not debug %} + <div class="row"> + <div class="col-lg-12 col-md-12 col-sm-12"> + <p> + More information about this error may be available in the log files. Search for the error above and a stack trace + will appear. This stack trace may help you track down what's going wrong. + </p> + <p> + If you cannot find the cause of this error or when you need some help, please open a ticket on + <strong><a href="https://github.com/firefly-iii/firefly-iii/issues/new">Github</a></strong>. + </p> + </div> + </div> + {% endif %} </div> {% if debug %} @@ -51,5 +65,6 @@ </div> </div> {% endif %} + </body> </html> From b09a250a031422689e82b3ca7ace46c7ab816c80 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 5 Feb 2017 18:50:19 +0100 Subject: [PATCH 653/709] Better link for issues. [skip ci] --- resources/views/emails/error-html.twig | 2 +- resources/views/emails/error-text.twig | 2 +- resources/views/errors/FireflyException.twig | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/views/emails/error-html.twig b/resources/views/emails/error-html.twig index afc890506a..5594a2a7eb 100644 --- a/resources/views/emails/error-html.twig +++ b/resources/views/emails/error-html.twig @@ -34,7 +34,7 @@ This can help fix the bug you just encountered. </p> <p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;"> - If you prefer, you can also open a new issue on <a href="https://github.com/firefly-iii/firefly-iii/issues/new">Github</a>. + If you prefer, you can also open a new issue on <a href="https://github.com/firefly-iii/firefly-iii/issues">Github</a>. </p> <p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;"> diff --git a/resources/views/emails/error-text.twig b/resources/views/emails/error-text.twig index 913d54d741..e195cdb631 100644 --- a/resources/views/emails/error-text.twig +++ b/resources/views/emails/error-text.twig @@ -21,7 +21,7 @@ the bug you just encountered. If you prefer, you can also open a new issue here: -https://github.com/firefly-iii/firefly-iii/issues/new +https://github.com/firefly-iii/firefly-iii/issues The full stacktrace is below: diff --git a/resources/views/errors/FireflyException.twig b/resources/views/errors/FireflyException.twig index 0e3499c8f3..01028a7e9d 100644 --- a/resources/views/errors/FireflyException.twig +++ b/resources/views/errors/FireflyException.twig @@ -47,7 +47,7 @@ </p> <p> If you cannot find the cause of this error or when you need some help, please open a ticket on - <strong><a href="https://github.com/firefly-iii/firefly-iii/issues/new">Github</a></strong>. + <strong><a href="https://github.com/firefly-iii/firefly-iii/issues">Github</a></strong>. </p> </div> </div> From dba92d73c43ec070d3e896751347d44a50f8dbb4 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sun, 5 Feb 2017 19:51:58 +0100 Subject: [PATCH 654/709] Include new line at the end of the file. --- app/Helpers/Chart/MetaPieChart.php | 2 +- app/Helpers/Chart/MetaPieChartInterface.php | 2 +- app/Http/Controllers/JavascriptController.php | 2 +- app/Import/ImportProcedureInterface.php | 2 +- app/Providers/LogServiceProvider.php | 2 +- config/app.php | 0 resources/lang/de_DE/auth.php | 2 +- resources/lang/de_DE/breadcrumbs.php | 2 +- resources/lang/de_DE/config.php | 2 +- resources/lang/de_DE/csv.php | 2 +- resources/lang/de_DE/demo.php | 2 +- resources/lang/de_DE/firefly.php | 2 +- resources/lang/de_DE/form.php | 2 +- resources/lang/de_DE/help.php | 2 +- resources/lang/de_DE/list.php | 2 +- resources/lang/de_DE/pagination.php | 2 +- resources/lang/de_DE/passwords.php | 2 +- resources/lang/de_DE/validation.php | 2 +- resources/lang/en_US/auth.php | 0 resources/lang/en_US/demo.php | 2 +- resources/lang/en_US/pagination.php | 0 resources/lang/en_US/passwords.php | 0 resources/lang/es_ES/auth.php | 2 +- resources/lang/es_ES/breadcrumbs.php | 2 +- resources/lang/es_ES/config.php | 2 +- resources/lang/es_ES/csv.php | 2 +- resources/lang/es_ES/demo.php | 2 +- resources/lang/es_ES/firefly.php | 2 +- resources/lang/es_ES/form.php | 2 +- resources/lang/es_ES/help.php | 2 +- resources/lang/es_ES/list.php | 2 +- resources/lang/es_ES/pagination.php | 2 +- resources/lang/es_ES/passwords.php | 2 +- resources/lang/es_ES/validation.php | 2 +- resources/lang/fr_FR/auth.php | 2 +- resources/lang/fr_FR/breadcrumbs.php | 2 +- resources/lang/fr_FR/config.php | 2 +- resources/lang/fr_FR/csv.php | 2 +- resources/lang/fr_FR/demo.php | 2 +- resources/lang/fr_FR/firefly.php | 2 +- resources/lang/fr_FR/form.php | 2 +- resources/lang/fr_FR/help.php | 2 +- resources/lang/fr_FR/list.php | 2 +- resources/lang/fr_FR/pagination.php | 2 +- resources/lang/fr_FR/passwords.php | 2 +- resources/lang/fr_FR/validation.php | 2 +- resources/lang/hr_HR/auth.php | 2 +- resources/lang/hr_HR/breadcrumbs.php | 2 +- resources/lang/hr_HR/config.php | 2 +- resources/lang/hr_HR/csv.php | 2 +- resources/lang/hr_HR/demo.php | 2 +- resources/lang/hr_HR/firefly.php | 2 +- resources/lang/hr_HR/form.php | 2 +- resources/lang/hr_HR/help.php | 2 +- resources/lang/hr_HR/list.php | 2 +- resources/lang/hr_HR/pagination.php | 2 +- resources/lang/hr_HR/passwords.php | 2 +- resources/lang/hr_HR/validation.php | 2 +- resources/lang/nl_NL/auth.php | 2 +- resources/lang/nl_NL/breadcrumbs.php | 2 +- resources/lang/nl_NL/config.php | 2 +- resources/lang/nl_NL/csv.php | 2 +- resources/lang/nl_NL/demo.php | 2 +- resources/lang/nl_NL/firefly.php | 2 +- resources/lang/nl_NL/form.php | 2 +- resources/lang/nl_NL/help.php | 2 +- resources/lang/nl_NL/list.php | 2 +- resources/lang/nl_NL/pagination.php | 2 +- resources/lang/nl_NL/passwords.php | 2 +- resources/lang/nl_NL/validation.php | 2 +- resources/lang/pl_PL/auth.php | 2 +- resources/lang/pl_PL/breadcrumbs.php | 2 +- resources/lang/pl_PL/config.php | 2 +- resources/lang/pl_PL/csv.php | 2 +- resources/lang/pl_PL/demo.php | 2 +- resources/lang/pl_PL/firefly.php | 2 +- resources/lang/pl_PL/form.php | 2 +- resources/lang/pl_PL/help.php | 2 +- resources/lang/pl_PL/list.php | 2 +- resources/lang/pl_PL/pagination.php | 2 +- resources/lang/pl_PL/passwords.php | 2 +- resources/lang/pl_PL/validation.php | 2 +- resources/lang/pt_BR/auth.php | 2 +- resources/lang/pt_BR/breadcrumbs.php | 2 +- resources/lang/pt_BR/config.php | 2 +- resources/lang/pt_BR/csv.php | 2 +- resources/lang/pt_BR/demo.php | 2 +- resources/lang/pt_BR/firefly.php | 2 +- resources/lang/pt_BR/form.php | 2 +- resources/lang/pt_BR/help.php | 2 +- resources/lang/pt_BR/list.php | 2 +- resources/lang/pt_BR/pagination.php | 2 +- resources/lang/pt_BR/passwords.php | 2 +- resources/lang/pt_BR/validation.php | 2 +- resources/lang/ru_RU/auth.php | 2 +- resources/lang/ru_RU/breadcrumbs.php | 2 +- resources/lang/ru_RU/config.php | 2 +- resources/lang/ru_RU/csv.php | 2 +- resources/lang/ru_RU/demo.php | 2 +- resources/lang/ru_RU/firefly.php | 2 +- resources/lang/ru_RU/form.php | 2 +- resources/lang/ru_RU/help.php | 2 +- resources/lang/ru_RU/list.php | 2 +- resources/lang/ru_RU/pagination.php | 2 +- resources/lang/ru_RU/passwords.php | 2 +- resources/lang/ru_RU/validation.php | 2 +- resources/lang/zh_HK/auth.php | 2 +- resources/lang/zh_HK/breadcrumbs.php | 2 +- resources/lang/zh_HK/config.php | 2 +- resources/lang/zh_HK/csv.php | 2 +- resources/lang/zh_HK/demo.php | 2 +- resources/lang/zh_HK/firefly.php | 2 +- resources/lang/zh_HK/form.php | 2 +- resources/lang/zh_HK/help.php | 2 +- resources/lang/zh_HK/list.php | 2 +- resources/lang/zh_HK/pagination.php | 2 +- resources/lang/zh_HK/passwords.php | 2 +- resources/lang/zh_HK/validation.php | 2 +- resources/lang/zh_TW/auth.php | 2 +- resources/lang/zh_TW/breadcrumbs.php | 2 +- resources/lang/zh_TW/config.php | 2 +- resources/lang/zh_TW/csv.php | 2 +- resources/lang/zh_TW/demo.php | 2 +- resources/lang/zh_TW/firefly.php | 2 +- resources/lang/zh_TW/form.php | 2 +- resources/lang/zh_TW/help.php | 2 +- resources/lang/zh_TW/list.php | 2 +- resources/lang/zh_TW/pagination.php | 2 +- resources/lang/zh_TW/passwords.php | 2 +- resources/lang/zh_TW/validation.php | 2 +- resources/views/accounts/create.twig | 2 +- resources/views/accounts/edit.twig | 2 +- resources/views/demo/accounts/index.twig | 2 +- resources/views/demo/budgets/index.twig | 2 +- resources/views/demo/currencies/index.twig | 2 +- resources/views/demo/home.twig | 2 +- resources/views/demo/import/configure.twig | 2 +- resources/views/demo/import/index.twig | 2 +- resources/views/demo/index.twig | 2 +- resources/views/demo/piggy-banks/index.twig | 2 +- resources/views/demo/reports/index.twig | 2 +- resources/views/demo/transactions/index.twig | 2 +- resources/views/export/index.twig | 2 +- resources/views/javascript/variables.twig | 2 +- resources/views/piggy-banks/create.twig | 2 +- resources/views/piggy-banks/edit.twig | 2 +- resources/views/preferences/index.twig | 2 +- resources/views/reports/options/category.twig | 2 +- resources/views/rules/rule-group/select-transactions.twig | 2 +- resources/views/tags/create.twig | 2 +- resources/views/tags/edit.twig | 2 +- tests/CreatesApplication.php | 2 +- tests/Feature/ExampleTest.php | 2 +- tests/TestCase.php | 2 +- tests/Unit/ExampleTest.php | 2 +- 155 files changed, 151 insertions(+), 151 deletions(-) mode change 100755 => 100644 config/app.php mode change 100755 => 100644 resources/lang/en_US/auth.php mode change 100755 => 100644 resources/lang/en_US/pagination.php mode change 100755 => 100644 resources/lang/en_US/passwords.php diff --git a/app/Helpers/Chart/MetaPieChart.php b/app/Helpers/Chart/MetaPieChart.php index e83b103ba0..4290720405 100644 --- a/app/Helpers/Chart/MetaPieChart.php +++ b/app/Helpers/Chart/MetaPieChart.php @@ -279,4 +279,4 @@ class MetaPieChart implements MetaPieChartInterface return $chartData; } -} \ No newline at end of file +} diff --git a/app/Helpers/Chart/MetaPieChartInterface.php b/app/Helpers/Chart/MetaPieChartInterface.php index a466db8a38..006300d308 100644 --- a/app/Helpers/Chart/MetaPieChartInterface.php +++ b/app/Helpers/Chart/MetaPieChartInterface.php @@ -79,4 +79,4 @@ interface MetaPieChartInterface */ public function setUser(User $user): MetaPieChartInterface; -} \ No newline at end of file +} diff --git a/app/Http/Controllers/JavascriptController.php b/app/Http/Controllers/JavascriptController.php index 67c3d268d7..719371598e 100644 --- a/app/Http/Controllers/JavascriptController.php +++ b/app/Http/Controllers/JavascriptController.php @@ -129,4 +129,4 @@ class JavascriptController extends Controller ]; } -} \ No newline at end of file +} diff --git a/app/Import/ImportProcedureInterface.php b/app/Import/ImportProcedureInterface.php index 72765aa4ce..22a519aa95 100644 --- a/app/Import/ImportProcedureInterface.php +++ b/app/Import/ImportProcedureInterface.php @@ -30,4 +30,4 @@ interface ImportProcedureInterface */ public function runImport(ImportJob $job): Collection; -} \ No newline at end of file +} diff --git a/app/Providers/LogServiceProvider.php b/app/Providers/LogServiceProvider.php index 86bf30481f..dc742d6b80 100644 --- a/app/Providers/LogServiceProvider.php +++ b/app/Providers/LogServiceProvider.php @@ -50,4 +50,4 @@ class LogServiceProvider extends LaravelLogServiceProvider $this->logLevel() ); } -} \ No newline at end of file +} diff --git a/config/app.php b/config/app.php old mode 100755 new mode 100644 diff --git a/resources/lang/de_DE/auth.php b/resources/lang/de_DE/auth.php index 5eff4c4e6b..a20bd688a1 100644 --- a/resources/lang/de_DE/auth.php +++ b/resources/lang/de_DE/auth.php @@ -25,4 +25,4 @@ return [ 'failed' => 'Falscher Benutzername und/oder falsches Passwort.', 'throttle' => 'Zu viele Login-Versuche. Bitte versuchen Sie es in :seconds Sekunde(n) erneut.', -]; \ No newline at end of file +]; diff --git a/resources/lang/de_DE/breadcrumbs.php b/resources/lang/de_DE/breadcrumbs.php index 0ce3f40bef..6566bd879c 100644 --- a/resources/lang/de_DE/breadcrumbs.php +++ b/resources/lang/de_DE/breadcrumbs.php @@ -38,4 +38,4 @@ return [ 'createTag' => 'Neuen Tag erstellen', 'edit_tag' => 'Bearbeite Tag ":tag"', 'delete_tag' => 'Lösche Tag ":tag"', -]; \ No newline at end of file +]; diff --git a/resources/lang/de_DE/config.php b/resources/lang/de_DE/config.php index 783fe0a459..563a4b39ee 100644 --- a/resources/lang/de_DE/config.php +++ b/resources/lang/de_DE/config.php @@ -20,4 +20,4 @@ return [ 'year' => '%Y', 'half_year' => '%B %Y', -]; \ No newline at end of file +]; diff --git a/resources/lang/de_DE/csv.php b/resources/lang/de_DE/csv.php index fca9ee7d0f..451551ca93 100644 --- a/resources/lang/de_DE/csv.php +++ b/resources/lang/de_DE/csv.php @@ -77,4 +77,4 @@ return [ 'column_tags-space' => 'Tags (durch Leerzeichen getrennt)', 'column_account-number' => 'Bestandskonto (Kontonr.)', 'column_opposing-number' => 'Zielkonto (Kontonr.)', -]; \ No newline at end of file +]; diff --git a/resources/lang/de_DE/demo.php b/resources/lang/de_DE/demo.php index 24e22bfa97..3a54a5a14e 100644 --- a/resources/lang/de_DE/demo.php +++ b/resources/lang/de_DE/demo.php @@ -21,4 +21,4 @@ return [ 'import-index' => 'Natürlich kann eine CSV-Datei in Firefly III importiert werden ', 'import-configure-security' => 'Aufgrund von Sicherheitsbedenken wurde der Upload mit einer lokalen Datei ersetzt.', 'import-configure-configuration' => 'Die unten stehende Konfiguration für die lokale Datei ist korrekt.', -]; \ No newline at end of file +]; diff --git a/resources/lang/de_DE/firefly.php b/resources/lang/de_DE/firefly.php index e0198d089f..2f72327fed 100644 --- a/resources/lang/de_DE/firefly.php +++ b/resources/lang/de_DE/firefly.php @@ -960,4 +960,4 @@ Sollen zusätzlich Ihre Girokonten angezeigt werden?', 'import_finished_intro' => 'The import has finished! You can now see the new transactions in Firefly.', 'import_finished_text_without_link' => 'It seems there is no tag that points to all your imported transactions. Please look for your imported data in the menu on the left, under "Transactions".', 'import_finished_text_with_link' => 'You can find a list of your imported transactions on the page of the <a href="tags/show/:tag">tag that was created for this import</a>.', -]; \ No newline at end of file +]; diff --git a/resources/lang/de_DE/form.php b/resources/lang/de_DE/form.php index a5e6307e0d..ed179e8bcb 100644 --- a/resources/lang/de_DE/form.php +++ b/resources/lang/de_DE/form.php @@ -181,4 +181,4 @@ return [ 'payment_date' => 'Zahlungsdatum', 'invoice_date' => 'Rechnungsdatum', 'internal_reference' => 'Interner Verweis', -]; \ No newline at end of file +]; diff --git a/resources/lang/de_DE/help.php b/resources/lang/de_DE/help.php index 54f198a3cd..6fe068f80e 100644 --- a/resources/lang/de_DE/help.php +++ b/resources/lang/de_DE/help.php @@ -30,4 +30,4 @@ return [ 'main-content-end-text' => 'Denken Sie daran, dass jede Seite ein kleines Fragezeichen in der oberen rechten Ecke hat. Klicken Sie darauf um Hilfe zur aktuellen Seite zu erhalten.', 'index' => 'index', 'home' => 'home', -]; \ No newline at end of file +]; diff --git a/resources/lang/de_DE/list.php b/resources/lang/de_DE/list.php index 801ae0be8b..0b6e04f918 100644 --- a/resources/lang/de_DE/list.php +++ b/resources/lang/de_DE/list.php @@ -86,4 +86,4 @@ return [ 'budget_count' => 'Anzahl Budgets', 'rule_and_groups_count' => 'Anzahl Regeln und Regelgruppen', 'tags_count' => 'Anzahl Tags', -]; \ No newline at end of file +]; diff --git a/resources/lang/de_DE/pagination.php b/resources/lang/de_DE/pagination.php index 37de9e090a..2d2efd2bd0 100644 --- a/resources/lang/de_DE/pagination.php +++ b/resources/lang/de_DE/pagination.php @@ -14,4 +14,4 @@ return [ 'previous' => '« Vorherige', 'next' => 'Nächste »', -]; \ No newline at end of file +]; diff --git a/resources/lang/de_DE/passwords.php b/resources/lang/de_DE/passwords.php index dcc36d2e91..b9deea7fd9 100644 --- a/resources/lang/de_DE/passwords.php +++ b/resources/lang/de_DE/passwords.php @@ -16,4 +16,4 @@ return [ 'sent' => 'Wir haben Ihnen einen Link zum Zurücksetzen des Passworts zugesendet!', 'reset' => 'Ihr Passwort wurde zurückgesetzt!', 'blocked' => 'Netter Versuch.', -]; \ No newline at end of file +]; diff --git a/resources/lang/de_DE/validation.php b/resources/lang/de_DE/validation.php index 56ca0aa328..472dc30487 100644 --- a/resources/lang/de_DE/validation.php +++ b/resources/lang/de_DE/validation.php @@ -88,4 +88,4 @@ return [ 'in_array' => ':attribute existiert nicht in :other.', 'present' => 'Das :attribute Feld muss vorhanden sein.', 'amount_zero' => 'Der Gesamtbetrag darf nicht Null sein', -]; \ No newline at end of file +]; diff --git a/resources/lang/en_US/auth.php b/resources/lang/en_US/auth.php old mode 100755 new mode 100644 diff --git a/resources/lang/en_US/demo.php b/resources/lang/en_US/demo.php index e7f8ea934d..3efa9c60a3 100644 --- a/resources/lang/en_US/demo.php +++ b/resources/lang/en_US/demo.php @@ -21,4 +21,4 @@ return [ 'import-index' => 'Of course, any CSV file can be imported into Firefly III ', 'import-configure-security' => 'Because of security concerns, your upload has been replaced with a local file.', 'import-configure-configuration' => 'The configuration you see below is correct for the local file.', -]; \ No newline at end of file +]; diff --git a/resources/lang/en_US/pagination.php b/resources/lang/en_US/pagination.php old mode 100755 new mode 100644 diff --git a/resources/lang/en_US/passwords.php b/resources/lang/en_US/passwords.php old mode 100755 new mode 100644 diff --git a/resources/lang/es_ES/auth.php b/resources/lang/es_ES/auth.php index 5d833b3d68..7319a9d7b8 100644 --- a/resources/lang/es_ES/auth.php +++ b/resources/lang/es_ES/auth.php @@ -25,4 +25,4 @@ return [ 'failed' => 'These credentials do not match our records.', 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', -]; \ No newline at end of file +]; diff --git a/resources/lang/es_ES/breadcrumbs.php b/resources/lang/es_ES/breadcrumbs.php index 6bcf9b862d..54cb260179 100644 --- a/resources/lang/es_ES/breadcrumbs.php +++ b/resources/lang/es_ES/breadcrumbs.php @@ -38,4 +38,4 @@ return [ 'createTag' => 'Create new tag', 'edit_tag' => 'Edit tag ":tag"', 'delete_tag' => 'Delete tag ":tag"', -]; \ No newline at end of file +]; diff --git a/resources/lang/es_ES/config.php b/resources/lang/es_ES/config.php index 58220429bf..0c23d3f6cd 100644 --- a/resources/lang/es_ES/config.php +++ b/resources/lang/es_ES/config.php @@ -20,4 +20,4 @@ return [ 'year' => '%Y', 'half_year' => '%B %Y', -]; \ No newline at end of file +]; diff --git a/resources/lang/es_ES/csv.php b/resources/lang/es_ES/csv.php index 4acb52efdc..6b5abd3431 100644 --- a/resources/lang/es_ES/csv.php +++ b/resources/lang/es_ES/csv.php @@ -77,4 +77,4 @@ return [ 'column_tags-space' => 'Tags (space separated)', 'column_account-number' => 'Asset account (account number)', 'column_opposing-number' => 'Opposing account (account number)', -]; \ No newline at end of file +]; diff --git a/resources/lang/es_ES/demo.php b/resources/lang/es_ES/demo.php index e7f8ea934d..3efa9c60a3 100644 --- a/resources/lang/es_ES/demo.php +++ b/resources/lang/es_ES/demo.php @@ -21,4 +21,4 @@ return [ 'import-index' => 'Of course, any CSV file can be imported into Firefly III ', 'import-configure-security' => 'Because of security concerns, your upload has been replaced with a local file.', 'import-configure-configuration' => 'The configuration you see below is correct for the local file.', -]; \ No newline at end of file +]; diff --git a/resources/lang/es_ES/firefly.php b/resources/lang/es_ES/firefly.php index a91170cd53..d54d34d4a6 100644 --- a/resources/lang/es_ES/firefly.php +++ b/resources/lang/es_ES/firefly.php @@ -959,4 +959,4 @@ return [ 'import_finished_intro' => 'The import has finished! You can now see the new transactions in Firefly.', 'import_finished_text_without_link' => 'It seems there is no tag that points to all your imported transactions. Please look for your imported data in the menu on the left, under "Transactions".', 'import_finished_text_with_link' => 'You can find a list of your imported transactions on the page of the <a href="tags/show/:tag">tag that was created for this import</a>.', -]; \ No newline at end of file +]; diff --git a/resources/lang/es_ES/form.php b/resources/lang/es_ES/form.php index ff44cbf9ef..271005bc4f 100644 --- a/resources/lang/es_ES/form.php +++ b/resources/lang/es_ES/form.php @@ -181,4 +181,4 @@ return [ 'payment_date' => 'Payment date', 'invoice_date' => 'Invoice date', 'internal_reference' => 'Internal reference', -]; \ No newline at end of file +]; diff --git a/resources/lang/es_ES/help.php b/resources/lang/es_ES/help.php index 61210ffe41..5198e5510f 100644 --- a/resources/lang/es_ES/help.php +++ b/resources/lang/es_ES/help.php @@ -30,4 +30,4 @@ return [ 'main-content-end-text' => 'Remember that every page has a small question mark at the right top. Click it to get help about the page you\'re on.', 'index' => 'index', 'home' => 'home', -]; \ No newline at end of file +]; diff --git a/resources/lang/es_ES/list.php b/resources/lang/es_ES/list.php index 90625d54e6..91254e401d 100644 --- a/resources/lang/es_ES/list.php +++ b/resources/lang/es_ES/list.php @@ -86,4 +86,4 @@ return [ 'budget_count' => 'Number of budgets', 'rule_and_groups_count' => 'Number of rules and rule groups', 'tags_count' => 'Number of tags', -]; \ No newline at end of file +]; diff --git a/resources/lang/es_ES/pagination.php b/resources/lang/es_ES/pagination.php index 4eeab21dee..9e61a6cfcc 100644 --- a/resources/lang/es_ES/pagination.php +++ b/resources/lang/es_ES/pagination.php @@ -14,4 +14,4 @@ return [ 'previous' => '« Previous', 'next' => 'Next »', -]; \ No newline at end of file +]; diff --git a/resources/lang/es_ES/passwords.php b/resources/lang/es_ES/passwords.php index 2e11aa92dc..8e1b72506b 100644 --- a/resources/lang/es_ES/passwords.php +++ b/resources/lang/es_ES/passwords.php @@ -16,4 +16,4 @@ return [ 'sent' => 'We have e-mailed your password reset link!', 'reset' => 'Your password has been reset!', 'blocked' => 'Nice try though.', -]; \ No newline at end of file +]; diff --git a/resources/lang/es_ES/validation.php b/resources/lang/es_ES/validation.php index addd0e6f39..ea21edefe4 100644 --- a/resources/lang/es_ES/validation.php +++ b/resources/lang/es_ES/validation.php @@ -88,4 +88,4 @@ return [ 'in_array' => 'The :attribute field does not exist in :other.', 'present' => 'The :attribute field must be present.', 'amount_zero' => 'The total amount cannot be zero', -]; \ No newline at end of file +]; diff --git a/resources/lang/fr_FR/auth.php b/resources/lang/fr_FR/auth.php index 7fc7beebaa..4b87a3d12c 100644 --- a/resources/lang/fr_FR/auth.php +++ b/resources/lang/fr_FR/auth.php @@ -25,4 +25,4 @@ return [ 'failed' => 'Ces identifiants n\'ont aucune correspondance.', 'throttle' => 'Trop de tentatives de connexion. Veuillez essayer à nouveau dans :seconds secondes.', -]; \ No newline at end of file +]; diff --git a/resources/lang/fr_FR/breadcrumbs.php b/resources/lang/fr_FR/breadcrumbs.php index 5211db8cfe..2f4b250674 100644 --- a/resources/lang/fr_FR/breadcrumbs.php +++ b/resources/lang/fr_FR/breadcrumbs.php @@ -38,4 +38,4 @@ return [ 'createTag' => 'Créer un nouveau tag', 'edit_tag' => 'Éditer le tag ":tag"', 'delete_tag' => 'Supprimer le tag ":tag"', -]; \ No newline at end of file +]; diff --git a/resources/lang/fr_FR/config.php b/resources/lang/fr_FR/config.php index 9ddc4761b8..a04e54e24b 100644 --- a/resources/lang/fr_FR/config.php +++ b/resources/lang/fr_FR/config.php @@ -20,4 +20,4 @@ return [ 'year' => '%Y', 'half_year' => '%B %Y', -]; \ No newline at end of file +]; diff --git a/resources/lang/fr_FR/csv.php b/resources/lang/fr_FR/csv.php index 2da66f7da6..1bb3e6666d 100644 --- a/resources/lang/fr_FR/csv.php +++ b/resources/lang/fr_FR/csv.php @@ -77,4 +77,4 @@ return [ 'column_tags-space' => 'Tags(séparé par des espaces)', 'column_account-number' => 'Compte d’actif (numéro de compte)', 'column_opposing-number' => 'Compte destination (numéro de compte)', -]; \ No newline at end of file +]; diff --git a/resources/lang/fr_FR/demo.php b/resources/lang/fr_FR/demo.php index e7f8ea934d..3efa9c60a3 100644 --- a/resources/lang/fr_FR/demo.php +++ b/resources/lang/fr_FR/demo.php @@ -21,4 +21,4 @@ return [ 'import-index' => 'Of course, any CSV file can be imported into Firefly III ', 'import-configure-security' => 'Because of security concerns, your upload has been replaced with a local file.', 'import-configure-configuration' => 'The configuration you see below is correct for the local file.', -]; \ No newline at end of file +]; diff --git a/resources/lang/fr_FR/firefly.php b/resources/lang/fr_FR/firefly.php index cfd9123263..e7c6a9689f 100644 --- a/resources/lang/fr_FR/firefly.php +++ b/resources/lang/fr_FR/firefly.php @@ -959,4 +959,4 @@ return [ 'import_finished_intro' => 'The import has finished! You can now see the new transactions in Firefly.', 'import_finished_text_without_link' => 'It seems there is no tag that points to all your imported transactions. Please look for your imported data in the menu on the left, under "Transactions".', 'import_finished_text_with_link' => 'You can find a list of your imported transactions on the page of the <a href="tags/show/:tag">tag that was created for this import</a>.', -]; \ No newline at end of file +]; diff --git a/resources/lang/fr_FR/form.php b/resources/lang/fr_FR/form.php index e833b5995a..9e14815256 100644 --- a/resources/lang/fr_FR/form.php +++ b/resources/lang/fr_FR/form.php @@ -181,4 +181,4 @@ return [ 'payment_date' => 'Date de paiement', 'invoice_date' => 'Date de facturation', 'internal_reference' => 'Référence interne', -]; \ No newline at end of file +]; diff --git a/resources/lang/fr_FR/help.php b/resources/lang/fr_FR/help.php index 42bdf91724..466fe59547 100644 --- a/resources/lang/fr_FR/help.php +++ b/resources/lang/fr_FR/help.php @@ -30,4 +30,4 @@ return [ 'main-content-end-text' => 'N\'oubliez pas que chaque page a un petit point d\'interrogation en haut à droite. Cliquez dessus pour obtenir de l\'aide concernant la page actuelle.', 'index' => 'index', 'home' => 'accueil', -]; \ No newline at end of file +]; diff --git a/resources/lang/fr_FR/list.php b/resources/lang/fr_FR/list.php index 55aba8cc1d..663fecee44 100644 --- a/resources/lang/fr_FR/list.php +++ b/resources/lang/fr_FR/list.php @@ -86,4 +86,4 @@ return [ 'budget_count' => 'Number of budgets', 'rule_and_groups_count' => 'Number of rules and rule groups', 'tags_count' => 'Number of tags', -]; \ No newline at end of file +]; diff --git a/resources/lang/fr_FR/pagination.php b/resources/lang/fr_FR/pagination.php index f073608f48..8adc2767a1 100644 --- a/resources/lang/fr_FR/pagination.php +++ b/resources/lang/fr_FR/pagination.php @@ -14,4 +14,4 @@ return [ 'previous' => '« Précédent', 'next' => 'Suivant »', -]; \ No newline at end of file +]; diff --git a/resources/lang/fr_FR/passwords.php b/resources/lang/fr_FR/passwords.php index 56d5c341d7..b9b998ec31 100644 --- a/resources/lang/fr_FR/passwords.php +++ b/resources/lang/fr_FR/passwords.php @@ -16,4 +16,4 @@ return [ 'sent' => 'Nous vous avons envoyé par e-mail un lien de réinitialisation de votre mot de passe !', 'reset' => 'Votre mot de passe a été réinitialisé !', 'blocked' => 'Bien essayé cependant.', -]; \ No newline at end of file +]; diff --git a/resources/lang/fr_FR/validation.php b/resources/lang/fr_FR/validation.php index aab122ceaa..f34faf28ea 100644 --- a/resources/lang/fr_FR/validation.php +++ b/resources/lang/fr_FR/validation.php @@ -88,4 +88,4 @@ return [ 'in_array' => 'The :attribute field does not exist in :other.', 'present' => 'The :attribute field must be present.', 'amount_zero' => 'The total amount cannot be zero', -]; \ No newline at end of file +]; diff --git a/resources/lang/hr_HR/auth.php b/resources/lang/hr_HR/auth.php index 5d833b3d68..7319a9d7b8 100644 --- a/resources/lang/hr_HR/auth.php +++ b/resources/lang/hr_HR/auth.php @@ -25,4 +25,4 @@ return [ 'failed' => 'These credentials do not match our records.', 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', -]; \ No newline at end of file +]; diff --git a/resources/lang/hr_HR/breadcrumbs.php b/resources/lang/hr_HR/breadcrumbs.php index 4b23411578..b6c21030e4 100644 --- a/resources/lang/hr_HR/breadcrumbs.php +++ b/resources/lang/hr_HR/breadcrumbs.php @@ -38,4 +38,4 @@ return [ 'createTag' => 'Kreiraj novu oznaku', 'edit_tag' => 'Uredi oznaku ":tag"', 'delete_tag' => 'Obriši oznaku ":tag"', -]; \ No newline at end of file +]; diff --git a/resources/lang/hr_HR/config.php b/resources/lang/hr_HR/config.php index aed39872de..1c612ab51c 100644 --- a/resources/lang/hr_HR/config.php +++ b/resources/lang/hr_HR/config.php @@ -20,4 +20,4 @@ return [ 'year' => '%Y', 'half_year' => '%B %Y', -]; \ No newline at end of file +]; diff --git a/resources/lang/hr_HR/csv.php b/resources/lang/hr_HR/csv.php index 4acb52efdc..6b5abd3431 100644 --- a/resources/lang/hr_HR/csv.php +++ b/resources/lang/hr_HR/csv.php @@ -77,4 +77,4 @@ return [ 'column_tags-space' => 'Tags (space separated)', 'column_account-number' => 'Asset account (account number)', 'column_opposing-number' => 'Opposing account (account number)', -]; \ No newline at end of file +]; diff --git a/resources/lang/hr_HR/demo.php b/resources/lang/hr_HR/demo.php index e7f8ea934d..3efa9c60a3 100644 --- a/resources/lang/hr_HR/demo.php +++ b/resources/lang/hr_HR/demo.php @@ -21,4 +21,4 @@ return [ 'import-index' => 'Of course, any CSV file can be imported into Firefly III ', 'import-configure-security' => 'Because of security concerns, your upload has been replaced with a local file.', 'import-configure-configuration' => 'The configuration you see below is correct for the local file.', -]; \ No newline at end of file +]; diff --git a/resources/lang/hr_HR/firefly.php b/resources/lang/hr_HR/firefly.php index a91170cd53..d54d34d4a6 100644 --- a/resources/lang/hr_HR/firefly.php +++ b/resources/lang/hr_HR/firefly.php @@ -959,4 +959,4 @@ return [ 'import_finished_intro' => 'The import has finished! You can now see the new transactions in Firefly.', 'import_finished_text_without_link' => 'It seems there is no tag that points to all your imported transactions. Please look for your imported data in the menu on the left, under "Transactions".', 'import_finished_text_with_link' => 'You can find a list of your imported transactions on the page of the <a href="tags/show/:tag">tag that was created for this import</a>.', -]; \ No newline at end of file +]; diff --git a/resources/lang/hr_HR/form.php b/resources/lang/hr_HR/form.php index ff44cbf9ef..271005bc4f 100644 --- a/resources/lang/hr_HR/form.php +++ b/resources/lang/hr_HR/form.php @@ -181,4 +181,4 @@ return [ 'payment_date' => 'Payment date', 'invoice_date' => 'Invoice date', 'internal_reference' => 'Internal reference', -]; \ No newline at end of file +]; diff --git a/resources/lang/hr_HR/help.php b/resources/lang/hr_HR/help.php index 61210ffe41..5198e5510f 100644 --- a/resources/lang/hr_HR/help.php +++ b/resources/lang/hr_HR/help.php @@ -30,4 +30,4 @@ return [ 'main-content-end-text' => 'Remember that every page has a small question mark at the right top. Click it to get help about the page you\'re on.', 'index' => 'index', 'home' => 'home', -]; \ No newline at end of file +]; diff --git a/resources/lang/hr_HR/list.php b/resources/lang/hr_HR/list.php index 90625d54e6..91254e401d 100644 --- a/resources/lang/hr_HR/list.php +++ b/resources/lang/hr_HR/list.php @@ -86,4 +86,4 @@ return [ 'budget_count' => 'Number of budgets', 'rule_and_groups_count' => 'Number of rules and rule groups', 'tags_count' => 'Number of tags', -]; \ No newline at end of file +]; diff --git a/resources/lang/hr_HR/pagination.php b/resources/lang/hr_HR/pagination.php index 0efae8cb62..0d6df7e178 100644 --- a/resources/lang/hr_HR/pagination.php +++ b/resources/lang/hr_HR/pagination.php @@ -14,4 +14,4 @@ return [ 'previous' => '« Prethodna', 'next' => 'Sljedeća »', -]; \ No newline at end of file +]; diff --git a/resources/lang/hr_HR/passwords.php b/resources/lang/hr_HR/passwords.php index 80362c2eca..93f1918c4e 100644 --- a/resources/lang/hr_HR/passwords.php +++ b/resources/lang/hr_HR/passwords.php @@ -16,4 +16,4 @@ return [ 'sent' => 'Na e-mail smo ti poslali poveznicu za promjenu lozinke!', 'reset' => 'Tvoja lozinka je promijenjena!', 'blocked' => 'Lijep pokušaj.', -]; \ No newline at end of file +]; diff --git a/resources/lang/hr_HR/validation.php b/resources/lang/hr_HR/validation.php index addd0e6f39..ea21edefe4 100644 --- a/resources/lang/hr_HR/validation.php +++ b/resources/lang/hr_HR/validation.php @@ -88,4 +88,4 @@ return [ 'in_array' => 'The :attribute field does not exist in :other.', 'present' => 'The :attribute field must be present.', 'amount_zero' => 'The total amount cannot be zero', -]; \ No newline at end of file +]; diff --git a/resources/lang/nl_NL/auth.php b/resources/lang/nl_NL/auth.php index f046afaa6e..ea6e4eba7f 100644 --- a/resources/lang/nl_NL/auth.php +++ b/resources/lang/nl_NL/auth.php @@ -25,4 +25,4 @@ return [ 'failed' => 'Deze gegevens zijn niet correct.', 'throttle' => 'Te veel inlogpogingen. Probeer opnieuw in: seconden seconden.', -]; \ No newline at end of file +]; diff --git a/resources/lang/nl_NL/breadcrumbs.php b/resources/lang/nl_NL/breadcrumbs.php index 915aae7995..b163e9ed68 100644 --- a/resources/lang/nl_NL/breadcrumbs.php +++ b/resources/lang/nl_NL/breadcrumbs.php @@ -38,4 +38,4 @@ return [ 'createTag' => 'Maak nieuwe tag', 'edit_tag' => 'Wijzig tag ":tag"', 'delete_tag' => 'Verwijder tag ":tag"', -]; \ No newline at end of file +]; diff --git a/resources/lang/nl_NL/config.php b/resources/lang/nl_NL/config.php index d27314468a..1680ae558a 100644 --- a/resources/lang/nl_NL/config.php +++ b/resources/lang/nl_NL/config.php @@ -20,4 +20,4 @@ return [ 'year' => '%Y', 'half_year' => '%B %Y', -]; \ No newline at end of file +]; diff --git a/resources/lang/nl_NL/csv.php b/resources/lang/nl_NL/csv.php index 876ca796a4..a50e4dd193 100644 --- a/resources/lang/nl_NL/csv.php +++ b/resources/lang/nl_NL/csv.php @@ -77,4 +77,4 @@ return [ 'column_tags-space' => 'Tags (spatiegescheiden)', 'column_account-number' => 'Betaalrekening (rekeningnummer)', 'column_opposing-number' => 'Tegenrekening (rekeningnummer)', -]; \ No newline at end of file +]; diff --git a/resources/lang/nl_NL/demo.php b/resources/lang/nl_NL/demo.php index ff2c7b4b54..f50aa30c9a 100644 --- a/resources/lang/nl_NL/demo.php +++ b/resources/lang/nl_NL/demo.php @@ -21,4 +21,4 @@ return [ 'import-index' => 'Of course, any CSV file can be imported into Firefly III ', 'import-configure-security' => 'Because of security concerns, your upload has been replaced with a local file.', 'import-configure-configuration' => 'The configuration you see below is correct for the local file.', -]; \ No newline at end of file +]; diff --git a/resources/lang/nl_NL/firefly.php b/resources/lang/nl_NL/firefly.php index 65b5810fb7..f5c9ccebf7 100644 --- a/resources/lang/nl_NL/firefly.php +++ b/resources/lang/nl_NL/firefly.php @@ -959,4 +959,4 @@ return [ 'import_finished_intro' => 'De import is klaar! Je kan de transacties nu terugvinden in Firefly.', 'import_finished_text_without_link' => 'Er is geen tag die al je transacties bevat. Kijk links in het menu onder "Transacties" en zoek daar je nieuwe transacties op.', 'import_finished_text_with_link' => 'Je kan je geïmporteerde transacties <a href="tags/show/:tag">op deze pagina</a> terug vinden.', -]; \ No newline at end of file +]; diff --git a/resources/lang/nl_NL/form.php b/resources/lang/nl_NL/form.php index c3a5997cea..2e075be59a 100644 --- a/resources/lang/nl_NL/form.php +++ b/resources/lang/nl_NL/form.php @@ -181,4 +181,4 @@ return [ 'payment_date' => 'Betalingsdatum', 'invoice_date' => 'Factuurdatum', 'internal_reference' => 'Interne verwijzing', -]; \ No newline at end of file +]; diff --git a/resources/lang/nl_NL/help.php b/resources/lang/nl_NL/help.php index 9ca29221ad..2b4b79b2f9 100644 --- a/resources/lang/nl_NL/help.php +++ b/resources/lang/nl_NL/help.php @@ -30,4 +30,4 @@ return [ 'main-content-end-text' => 'Elke pagina heeft een vraagtekentje rechtsboven. Gebruik deze voor meer hulp. Veel plezier!', 'index' => 'index', 'home' => 'home', -]; \ No newline at end of file +]; diff --git a/resources/lang/nl_NL/list.php b/resources/lang/nl_NL/list.php index 95efafc5d6..2eeeb69f34 100644 --- a/resources/lang/nl_NL/list.php +++ b/resources/lang/nl_NL/list.php @@ -86,4 +86,4 @@ return [ 'budget_count' => 'Aantal budgetten', 'rule_and_groups_count' => 'Aantal regels en regelgroepen', 'tags_count' => 'Aantal tags', -]; \ No newline at end of file +]; diff --git a/resources/lang/nl_NL/pagination.php b/resources/lang/nl_NL/pagination.php index 02de2ab65b..df407a1708 100644 --- a/resources/lang/nl_NL/pagination.php +++ b/resources/lang/nl_NL/pagination.php @@ -14,4 +14,4 @@ return [ 'previous' => '« Vorige', 'next' => 'Volgende »', -]; \ No newline at end of file +]; diff --git a/resources/lang/nl_NL/passwords.php b/resources/lang/nl_NL/passwords.php index 3839a12e68..0e6bcd3176 100644 --- a/resources/lang/nl_NL/passwords.php +++ b/resources/lang/nl_NL/passwords.php @@ -16,4 +16,4 @@ return [ 'sent' => 'Je krijgt een mailtje met een linkje om je wachtwoord te herstellen!', 'reset' => 'Je wachtwoord is hersteld!', 'blocked' => 'Leuk geprobeerd wel.', -]; \ No newline at end of file +]; diff --git a/resources/lang/nl_NL/validation.php b/resources/lang/nl_NL/validation.php index 7533fe6848..4cc89b2fb5 100644 --- a/resources/lang/nl_NL/validation.php +++ b/resources/lang/nl_NL/validation.php @@ -88,4 +88,4 @@ return [ 'in_array' => 'Het :attribute veld bestaat niet in :other.', 'present' => 'Het :attribute veld moet aanwezig zijn.', 'amount_zero' => 'Het totaalbedrag kan niet nul zijn', -]; \ No newline at end of file +]; diff --git a/resources/lang/pl_PL/auth.php b/resources/lang/pl_PL/auth.php index a7b55af932..dabd854a8e 100644 --- a/resources/lang/pl_PL/auth.php +++ b/resources/lang/pl_PL/auth.php @@ -25,4 +25,4 @@ return [ 'failed' => 'Te poświadczenia nie zgadzają się z naszymi danymi.', 'throttle' => 'Zbyt wiele prób logowania. Spróbuj ponownie za :seconds sekund.', -]; \ No newline at end of file +]; diff --git a/resources/lang/pl_PL/breadcrumbs.php b/resources/lang/pl_PL/breadcrumbs.php index 210aab7ca0..fbd869f96d 100644 --- a/resources/lang/pl_PL/breadcrumbs.php +++ b/resources/lang/pl_PL/breadcrumbs.php @@ -38,4 +38,4 @@ return [ 'createTag' => 'Utwórz nowy tag', 'edit_tag' => 'Modyfikuj tag ":tag"', 'delete_tag' => 'Usuń tag ":tag"', -]; \ No newline at end of file +]; diff --git a/resources/lang/pl_PL/config.php b/resources/lang/pl_PL/config.php index 29dfe740f4..6acc711d2b 100644 --- a/resources/lang/pl_PL/config.php +++ b/resources/lang/pl_PL/config.php @@ -20,4 +20,4 @@ return [ 'year' => '%Y', 'half_year' => '%B %Y', -]; \ No newline at end of file +]; diff --git a/resources/lang/pl_PL/csv.php b/resources/lang/pl_PL/csv.php index d9b1b88c79..96e5ce2148 100644 --- a/resources/lang/pl_PL/csv.php +++ b/resources/lang/pl_PL/csv.php @@ -77,4 +77,4 @@ return [ 'column_tags-space' => 'Tagi (oddzielone spacjami)', 'column_account-number' => 'Konto aktywów (numer konta)', 'column_opposing-number' => 'Opposing account (account number)', -]; \ No newline at end of file +]; diff --git a/resources/lang/pl_PL/demo.php b/resources/lang/pl_PL/demo.php index 5e19d0ed45..623854e8e0 100644 --- a/resources/lang/pl_PL/demo.php +++ b/resources/lang/pl_PL/demo.php @@ -21,4 +21,4 @@ return [ 'import-index' => 'Of course, any CSV file can be imported into Firefly III ', 'import-configure-security' => 'Because of security concerns, your upload has been replaced with a local file.', 'import-configure-configuration' => 'Konfiguracja, która widzisz poniżej jest prawidłowa dla lokalnego pliku.', -]; \ No newline at end of file +]; diff --git a/resources/lang/pl_PL/firefly.php b/resources/lang/pl_PL/firefly.php index d101e6129f..347351bd19 100644 --- a/resources/lang/pl_PL/firefly.php +++ b/resources/lang/pl_PL/firefly.php @@ -959,4 +959,4 @@ return [ 'import_finished_intro' => 'The import has finished! You can now see the new transactions in Firefly.', 'import_finished_text_without_link' => 'It seems there is no tag that points to all your imported transactions. Please look for your imported data in the menu on the left, under "Transactions".', 'import_finished_text_with_link' => 'You can find a list of your imported transactions on the page of the <a href="tags/show/:tag">tag that was created for this import</a>.', -]; \ No newline at end of file +]; diff --git a/resources/lang/pl_PL/form.php b/resources/lang/pl_PL/form.php index e5466680b7..792e53c89d 100644 --- a/resources/lang/pl_PL/form.php +++ b/resources/lang/pl_PL/form.php @@ -181,4 +181,4 @@ return [ 'payment_date' => 'Data płatności', 'invoice_date' => 'Data faktury', 'internal_reference' => 'Internal reference', -]; \ No newline at end of file +]; diff --git a/resources/lang/pl_PL/help.php b/resources/lang/pl_PL/help.php index 8b73dbc51d..0639bfeea4 100644 --- a/resources/lang/pl_PL/help.php +++ b/resources/lang/pl_PL/help.php @@ -30,4 +30,4 @@ return [ 'main-content-end-text' => 'Pamiętaj, że każda strona ma małą ikonkę ze znakiem zapytania w prawym górnym rogu. Kliknij ją aby otrzymać dodatkowe informacje o stronie, na której się znajdujesz.', 'index' => 'indeks', 'home' => 'początek', -]; \ No newline at end of file +]; diff --git a/resources/lang/pl_PL/list.php b/resources/lang/pl_PL/list.php index 0ce37d9346..1de98adf48 100644 --- a/resources/lang/pl_PL/list.php +++ b/resources/lang/pl_PL/list.php @@ -86,4 +86,4 @@ return [ 'budget_count' => 'Liczba budżetów', 'rule_and_groups_count' => 'Liczba reguł i grup reguł', 'tags_count' => 'Liczba tagów', -]; \ No newline at end of file +]; diff --git a/resources/lang/pl_PL/pagination.php b/resources/lang/pl_PL/pagination.php index f666586cef..0cdbd60741 100644 --- a/resources/lang/pl_PL/pagination.php +++ b/resources/lang/pl_PL/pagination.php @@ -14,4 +14,4 @@ return [ 'previous' => '« Poprzednia', 'next' => 'Następna »', -]; \ No newline at end of file +]; diff --git a/resources/lang/pl_PL/passwords.php b/resources/lang/pl_PL/passwords.php index f3d2d44d09..64141c8686 100644 --- a/resources/lang/pl_PL/passwords.php +++ b/resources/lang/pl_PL/passwords.php @@ -16,4 +16,4 @@ return [ 'sent' => 'Wysłaliśmy link do resetowania hasła na twój adres email!', 'reset' => 'Twoje hasło zostało zresetowane!', 'blocked' => 'Przynajmniej próbowałeś.', -]; \ No newline at end of file +]; diff --git a/resources/lang/pl_PL/validation.php b/resources/lang/pl_PL/validation.php index 107f70fe31..f1ad408cb7 100644 --- a/resources/lang/pl_PL/validation.php +++ b/resources/lang/pl_PL/validation.php @@ -88,4 +88,4 @@ return [ 'in_array' => 'Pole :attribute nie istnieje w :other.', 'present' => 'Pole :attribute musi być obecne.', 'amount_zero' => 'Całkowita kwota nie może być zerem', -]; \ No newline at end of file +]; diff --git a/resources/lang/pt_BR/auth.php b/resources/lang/pt_BR/auth.php index 5a17d21cad..15a40c990b 100644 --- a/resources/lang/pt_BR/auth.php +++ b/resources/lang/pt_BR/auth.php @@ -25,4 +25,4 @@ return [ 'failed' => 'Essas credenciais não correspondem aos nossos registros.', 'throttle' => 'Muitas tentativas de login. Por favor, tente novamente em :seconds segundos.', -]; \ No newline at end of file +]; diff --git a/resources/lang/pt_BR/breadcrumbs.php b/resources/lang/pt_BR/breadcrumbs.php index cd6c42b790..6f8a0fbefe 100644 --- a/resources/lang/pt_BR/breadcrumbs.php +++ b/resources/lang/pt_BR/breadcrumbs.php @@ -38,4 +38,4 @@ return [ 'createTag' => 'Criar nova etiqueta', 'edit_tag' => 'Editar etiqueta ":tag"', 'delete_tag' => 'Apagar etiqueta ":tag"', -]; \ No newline at end of file +]; diff --git a/resources/lang/pt_BR/config.php b/resources/lang/pt_BR/config.php index 1800829bde..3a6582b8d2 100644 --- a/resources/lang/pt_BR/config.php +++ b/resources/lang/pt_BR/config.php @@ -20,4 +20,4 @@ return [ 'year' => '%Y', 'half_year' => '%B %Y', -]; \ No newline at end of file +]; diff --git a/resources/lang/pt_BR/csv.php b/resources/lang/pt_BR/csv.php index a1b92485f2..aca1e7ebc2 100644 --- a/resources/lang/pt_BR/csv.php +++ b/resources/lang/pt_BR/csv.php @@ -77,4 +77,4 @@ return [ 'column_tags-space' => 'Tags (separadas por espaço)', 'column_account-number' => 'Conta de ativo (número da conta)', 'column_opposing-number' => 'Conta Contrária (número da conta)', -]; \ No newline at end of file +]; diff --git a/resources/lang/pt_BR/demo.php b/resources/lang/pt_BR/demo.php index 559620fedf..4842ffa8fe 100644 --- a/resources/lang/pt_BR/demo.php +++ b/resources/lang/pt_BR/demo.php @@ -21,4 +21,4 @@ return [ 'import-index' => 'Claro, qualquer arquivo .CSV pode ser importado em Firefly III ', 'import-configure-security' => 'Por questões de segurança, seu upload foi substituído por um arquivo local.', 'import-configure-configuration' => 'A configuração que você vê abaixo é a correta para o arquivo local.', -]; \ No newline at end of file +]; diff --git a/resources/lang/pt_BR/firefly.php b/resources/lang/pt_BR/firefly.php index 6d270fdea5..9cfa513247 100644 --- a/resources/lang/pt_BR/firefly.php +++ b/resources/lang/pt_BR/firefly.php @@ -959,4 +959,4 @@ return [ 'import_finished_intro' => 'The import has finished! You can now see the new transactions in Firefly.', 'import_finished_text_without_link' => 'It seems there is no tag that points to all your imported transactions. Please look for your imported data in the menu on the left, under "Transactions".', 'import_finished_text_with_link' => 'You can find a list of your imported transactions on the page of the <a href="tags/show/:tag">tag that was created for this import</a>.', -]; \ No newline at end of file +]; diff --git a/resources/lang/pt_BR/form.php b/resources/lang/pt_BR/form.php index 4d7a1cae62..62f0fbdca9 100644 --- a/resources/lang/pt_BR/form.php +++ b/resources/lang/pt_BR/form.php @@ -181,4 +181,4 @@ return [ 'payment_date' => 'Data de pagamento', 'invoice_date' => 'Data da Fatura', 'internal_reference' => 'Referência interna', -]; \ No newline at end of file +]; diff --git a/resources/lang/pt_BR/help.php b/resources/lang/pt_BR/help.php index 83268848f9..877259b46b 100644 --- a/resources/lang/pt_BR/help.php +++ b/resources/lang/pt_BR/help.php @@ -30,4 +30,4 @@ return [ 'main-content-end-text' => 'Lembre-se que cada página tem um pequeno ponto de interrogação na parte superior direita. Clique nele para obter ajuda sobre a página que você está.', 'index' => 'índice', 'home' => 'casa', -]; \ No newline at end of file +]; diff --git a/resources/lang/pt_BR/list.php b/resources/lang/pt_BR/list.php index 4cc4bbbd49..548fc9cabe 100644 --- a/resources/lang/pt_BR/list.php +++ b/resources/lang/pt_BR/list.php @@ -86,4 +86,4 @@ return [ 'budget_count' => 'Número de orçamentos', 'rule_and_groups_count' => 'Número de regras e grupos de regras', 'tags_count' => 'Número de tags', -]; \ No newline at end of file +]; diff --git a/resources/lang/pt_BR/pagination.php b/resources/lang/pt_BR/pagination.php index f16e5f9bb1..7849521349 100644 --- a/resources/lang/pt_BR/pagination.php +++ b/resources/lang/pt_BR/pagination.php @@ -14,4 +14,4 @@ return [ 'previous' => '« Anterior', 'next' => 'Próximo »', -]; \ No newline at end of file +]; diff --git a/resources/lang/pt_BR/passwords.php b/resources/lang/pt_BR/passwords.php index d167089a72..24863232a1 100644 --- a/resources/lang/pt_BR/passwords.php +++ b/resources/lang/pt_BR/passwords.php @@ -16,4 +16,4 @@ return [ 'sent' => 'Nós te enviamos um email com um link para trocar a senha!', 'reset' => 'Sua senha foi redefinida!', 'blocked' => 'Boa tentativa.', -]; \ No newline at end of file +]; diff --git a/resources/lang/pt_BR/validation.php b/resources/lang/pt_BR/validation.php index 3e4891f349..527734719f 100644 --- a/resources/lang/pt_BR/validation.php +++ b/resources/lang/pt_BR/validation.php @@ -88,4 +88,4 @@ return [ 'in_array' => 'O campo :attribute não existe em :other.', 'present' => 'O campo :attribute deve estar presente.', 'amount_zero' => 'A quantidade total não pode ser zero', -]; \ No newline at end of file +]; diff --git a/resources/lang/ru_RU/auth.php b/resources/lang/ru_RU/auth.php index bc725bca9b..715498e97b 100644 --- a/resources/lang/ru_RU/auth.php +++ b/resources/lang/ru_RU/auth.php @@ -25,4 +25,4 @@ return [ 'failed' => 'Неправильный адрес электронной почты или пароль.', 'throttle' => 'Слишком много попыток логина. Пожалуйста, попробуйте снова через :seconds секунд.', -]; \ No newline at end of file +]; diff --git a/resources/lang/ru_RU/breadcrumbs.php b/resources/lang/ru_RU/breadcrumbs.php index 646511cec4..317c52461b 100644 --- a/resources/lang/ru_RU/breadcrumbs.php +++ b/resources/lang/ru_RU/breadcrumbs.php @@ -38,4 +38,4 @@ return [ 'createTag' => 'Создать новый тег', 'edit_tag' => 'Редактировать тег ":tag"', 'delete_tag' => 'Удалить тег ":tag"', -]; \ No newline at end of file +]; diff --git a/resources/lang/ru_RU/config.php b/resources/lang/ru_RU/config.php index 5544349117..668f45deb4 100644 --- a/resources/lang/ru_RU/config.php +++ b/resources/lang/ru_RU/config.php @@ -20,4 +20,4 @@ return [ 'year' => '%Y', 'half_year' => '%B %Y', -]; \ No newline at end of file +]; diff --git a/resources/lang/ru_RU/csv.php b/resources/lang/ru_RU/csv.php index 37e4e88998..2340f76ea4 100644 --- a/resources/lang/ru_RU/csv.php +++ b/resources/lang/ru_RU/csv.php @@ -77,4 +77,4 @@ return [ 'column_tags-space' => 'Tags (space separated)', 'column_account-number' => 'Asset account (account number)', 'column_opposing-number' => 'Opposing account (account number)', -]; \ No newline at end of file +]; diff --git a/resources/lang/ru_RU/demo.php b/resources/lang/ru_RU/demo.php index e7f8ea934d..3efa9c60a3 100644 --- a/resources/lang/ru_RU/demo.php +++ b/resources/lang/ru_RU/demo.php @@ -21,4 +21,4 @@ return [ 'import-index' => 'Of course, any CSV file can be imported into Firefly III ', 'import-configure-security' => 'Because of security concerns, your upload has been replaced with a local file.', 'import-configure-configuration' => 'The configuration you see below is correct for the local file.', -]; \ No newline at end of file +]; diff --git a/resources/lang/ru_RU/firefly.php b/resources/lang/ru_RU/firefly.php index a91170cd53..d54d34d4a6 100644 --- a/resources/lang/ru_RU/firefly.php +++ b/resources/lang/ru_RU/firefly.php @@ -959,4 +959,4 @@ return [ 'import_finished_intro' => 'The import has finished! You can now see the new transactions in Firefly.', 'import_finished_text_without_link' => 'It seems there is no tag that points to all your imported transactions. Please look for your imported data in the menu on the left, under "Transactions".', 'import_finished_text_with_link' => 'You can find a list of your imported transactions on the page of the <a href="tags/show/:tag">tag that was created for this import</a>.', -]; \ No newline at end of file +]; diff --git a/resources/lang/ru_RU/form.php b/resources/lang/ru_RU/form.php index ff44cbf9ef..271005bc4f 100644 --- a/resources/lang/ru_RU/form.php +++ b/resources/lang/ru_RU/form.php @@ -181,4 +181,4 @@ return [ 'payment_date' => 'Payment date', 'invoice_date' => 'Invoice date', 'internal_reference' => 'Internal reference', -]; \ No newline at end of file +]; diff --git a/resources/lang/ru_RU/help.php b/resources/lang/ru_RU/help.php index 61210ffe41..5198e5510f 100644 --- a/resources/lang/ru_RU/help.php +++ b/resources/lang/ru_RU/help.php @@ -30,4 +30,4 @@ return [ 'main-content-end-text' => 'Remember that every page has a small question mark at the right top. Click it to get help about the page you\'re on.', 'index' => 'index', 'home' => 'home', -]; \ No newline at end of file +]; diff --git a/resources/lang/ru_RU/list.php b/resources/lang/ru_RU/list.php index 90625d54e6..91254e401d 100644 --- a/resources/lang/ru_RU/list.php +++ b/resources/lang/ru_RU/list.php @@ -86,4 +86,4 @@ return [ 'budget_count' => 'Number of budgets', 'rule_and_groups_count' => 'Number of rules and rule groups', 'tags_count' => 'Number of tags', -]; \ No newline at end of file +]; diff --git a/resources/lang/ru_RU/pagination.php b/resources/lang/ru_RU/pagination.php index 4eeab21dee..9e61a6cfcc 100644 --- a/resources/lang/ru_RU/pagination.php +++ b/resources/lang/ru_RU/pagination.php @@ -14,4 +14,4 @@ return [ 'previous' => '« Previous', 'next' => 'Next »', -]; \ No newline at end of file +]; diff --git a/resources/lang/ru_RU/passwords.php b/resources/lang/ru_RU/passwords.php index 2e11aa92dc..8e1b72506b 100644 --- a/resources/lang/ru_RU/passwords.php +++ b/resources/lang/ru_RU/passwords.php @@ -16,4 +16,4 @@ return [ 'sent' => 'We have e-mailed your password reset link!', 'reset' => 'Your password has been reset!', 'blocked' => 'Nice try though.', -]; \ No newline at end of file +]; diff --git a/resources/lang/ru_RU/validation.php b/resources/lang/ru_RU/validation.php index addd0e6f39..ea21edefe4 100644 --- a/resources/lang/ru_RU/validation.php +++ b/resources/lang/ru_RU/validation.php @@ -88,4 +88,4 @@ return [ 'in_array' => 'The :attribute field does not exist in :other.', 'present' => 'The :attribute field must be present.', 'amount_zero' => 'The total amount cannot be zero', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_HK/auth.php b/resources/lang/zh_HK/auth.php index 5d833b3d68..7319a9d7b8 100644 --- a/resources/lang/zh_HK/auth.php +++ b/resources/lang/zh_HK/auth.php @@ -25,4 +25,4 @@ return [ 'failed' => 'These credentials do not match our records.', 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_HK/breadcrumbs.php b/resources/lang/zh_HK/breadcrumbs.php index 6bcf9b862d..54cb260179 100644 --- a/resources/lang/zh_HK/breadcrumbs.php +++ b/resources/lang/zh_HK/breadcrumbs.php @@ -38,4 +38,4 @@ return [ 'createTag' => 'Create new tag', 'edit_tag' => 'Edit tag ":tag"', 'delete_tag' => 'Delete tag ":tag"', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_HK/config.php b/resources/lang/zh_HK/config.php index 58220429bf..0c23d3f6cd 100644 --- a/resources/lang/zh_HK/config.php +++ b/resources/lang/zh_HK/config.php @@ -20,4 +20,4 @@ return [ 'year' => '%Y', 'half_year' => '%B %Y', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_HK/csv.php b/resources/lang/zh_HK/csv.php index 4acb52efdc..6b5abd3431 100644 --- a/resources/lang/zh_HK/csv.php +++ b/resources/lang/zh_HK/csv.php @@ -77,4 +77,4 @@ return [ 'column_tags-space' => 'Tags (space separated)', 'column_account-number' => 'Asset account (account number)', 'column_opposing-number' => 'Opposing account (account number)', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_HK/demo.php b/resources/lang/zh_HK/demo.php index e7f8ea934d..3efa9c60a3 100644 --- a/resources/lang/zh_HK/demo.php +++ b/resources/lang/zh_HK/demo.php @@ -21,4 +21,4 @@ return [ 'import-index' => 'Of course, any CSV file can be imported into Firefly III ', 'import-configure-security' => 'Because of security concerns, your upload has been replaced with a local file.', 'import-configure-configuration' => 'The configuration you see below is correct for the local file.', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_HK/firefly.php b/resources/lang/zh_HK/firefly.php index a91170cd53..d54d34d4a6 100644 --- a/resources/lang/zh_HK/firefly.php +++ b/resources/lang/zh_HK/firefly.php @@ -959,4 +959,4 @@ return [ 'import_finished_intro' => 'The import has finished! You can now see the new transactions in Firefly.', 'import_finished_text_without_link' => 'It seems there is no tag that points to all your imported transactions. Please look for your imported data in the menu on the left, under "Transactions".', 'import_finished_text_with_link' => 'You can find a list of your imported transactions on the page of the <a href="tags/show/:tag">tag that was created for this import</a>.', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_HK/form.php b/resources/lang/zh_HK/form.php index ff44cbf9ef..271005bc4f 100644 --- a/resources/lang/zh_HK/form.php +++ b/resources/lang/zh_HK/form.php @@ -181,4 +181,4 @@ return [ 'payment_date' => 'Payment date', 'invoice_date' => 'Invoice date', 'internal_reference' => 'Internal reference', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_HK/help.php b/resources/lang/zh_HK/help.php index 61210ffe41..5198e5510f 100644 --- a/resources/lang/zh_HK/help.php +++ b/resources/lang/zh_HK/help.php @@ -30,4 +30,4 @@ return [ 'main-content-end-text' => 'Remember that every page has a small question mark at the right top. Click it to get help about the page you\'re on.', 'index' => 'index', 'home' => 'home', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_HK/list.php b/resources/lang/zh_HK/list.php index 90625d54e6..91254e401d 100644 --- a/resources/lang/zh_HK/list.php +++ b/resources/lang/zh_HK/list.php @@ -86,4 +86,4 @@ return [ 'budget_count' => 'Number of budgets', 'rule_and_groups_count' => 'Number of rules and rule groups', 'tags_count' => 'Number of tags', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_HK/pagination.php b/resources/lang/zh_HK/pagination.php index 4eeab21dee..9e61a6cfcc 100644 --- a/resources/lang/zh_HK/pagination.php +++ b/resources/lang/zh_HK/pagination.php @@ -14,4 +14,4 @@ return [ 'previous' => '« Previous', 'next' => 'Next »', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_HK/passwords.php b/resources/lang/zh_HK/passwords.php index 2e11aa92dc..8e1b72506b 100644 --- a/resources/lang/zh_HK/passwords.php +++ b/resources/lang/zh_HK/passwords.php @@ -16,4 +16,4 @@ return [ 'sent' => 'We have e-mailed your password reset link!', 'reset' => 'Your password has been reset!', 'blocked' => 'Nice try though.', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_HK/validation.php b/resources/lang/zh_HK/validation.php index addd0e6f39..ea21edefe4 100644 --- a/resources/lang/zh_HK/validation.php +++ b/resources/lang/zh_HK/validation.php @@ -88,4 +88,4 @@ return [ 'in_array' => 'The :attribute field does not exist in :other.', 'present' => 'The :attribute field must be present.', 'amount_zero' => 'The total amount cannot be zero', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_TW/auth.php b/resources/lang/zh_TW/auth.php index 7a20761eea..8278daa0a3 100644 --- a/resources/lang/zh_TW/auth.php +++ b/resources/lang/zh_TW/auth.php @@ -25,4 +25,4 @@ return [ 'failed' => '帳號或密碼錯誤。', 'throttle' => '登入失敗次數過多,請等待 :seconds 秒後再試。', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_TW/breadcrumbs.php b/resources/lang/zh_TW/breadcrumbs.php index d9fe1aac5b..a5fe3d1755 100644 --- a/resources/lang/zh_TW/breadcrumbs.php +++ b/resources/lang/zh_TW/breadcrumbs.php @@ -38,4 +38,4 @@ return [ 'createTag' => '建立新標籤', 'edit_tag' => '編輯標籤 ":tag"', 'delete_tag' => '刪除標籤 ":tag"', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_TW/config.php b/resources/lang/zh_TW/config.php index 8e724f5689..6e4ea6871a 100644 --- a/resources/lang/zh_TW/config.php +++ b/resources/lang/zh_TW/config.php @@ -20,4 +20,4 @@ return [ 'year' => '%Y', 'half_year' => '%B %Y', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_TW/csv.php b/resources/lang/zh_TW/csv.php index e94574c27b..f87f951bba 100644 --- a/resources/lang/zh_TW/csv.php +++ b/resources/lang/zh_TW/csv.php @@ -77,4 +77,4 @@ return [ 'column_tags-space' => '標籤 (空格分隔)', 'column_account-number' => '資產帳戶 (帳號號碼)', 'column_opposing-number' => '抵銷的帳戶 (帳號號碼)', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_TW/demo.php b/resources/lang/zh_TW/demo.php index e7f8ea934d..3efa9c60a3 100644 --- a/resources/lang/zh_TW/demo.php +++ b/resources/lang/zh_TW/demo.php @@ -21,4 +21,4 @@ return [ 'import-index' => 'Of course, any CSV file can be imported into Firefly III ', 'import-configure-security' => 'Because of security concerns, your upload has been replaced with a local file.', 'import-configure-configuration' => 'The configuration you see below is correct for the local file.', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_TW/firefly.php b/resources/lang/zh_TW/firefly.php index 9fddfcf9d8..22ad08e150 100644 --- a/resources/lang/zh_TW/firefly.php +++ b/resources/lang/zh_TW/firefly.php @@ -959,4 +959,4 @@ return [ 'import_finished_intro' => 'The import has finished! You can now see the new transactions in Firefly.', 'import_finished_text_without_link' => 'It seems there is no tag that points to all your imported transactions. Please look for your imported data in the menu on the left, under "Transactions".', 'import_finished_text_with_link' => 'You can find a list of your imported transactions on the page of the <a href="tags/show/:tag">tag that was created for this import</a>.', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_TW/form.php b/resources/lang/zh_TW/form.php index 1c04418238..9014884592 100644 --- a/resources/lang/zh_TW/form.php +++ b/resources/lang/zh_TW/form.php @@ -181,4 +181,4 @@ return [ 'payment_date' => 'Payment date', 'invoice_date' => 'Invoice date', 'internal_reference' => 'Internal reference', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_TW/help.php b/resources/lang/zh_TW/help.php index 69a62018c7..8175c162da 100644 --- a/resources/lang/zh_TW/help.php +++ b/resources/lang/zh_TW/help.php @@ -30,4 +30,4 @@ return [ 'main-content-end-text' => '每一頁在右上方有一個小問號。按一下它可以取得與頁面相關説明。', 'index' => '首頁', 'home' => '首頁', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_TW/list.php b/resources/lang/zh_TW/list.php index d5df766461..d90f353626 100644 --- a/resources/lang/zh_TW/list.php +++ b/resources/lang/zh_TW/list.php @@ -86,4 +86,4 @@ return [ 'budget_count' => 'Number of budgets', 'rule_and_groups_count' => 'Number of rules and rule groups', 'tags_count' => 'Number of tags', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_TW/pagination.php b/resources/lang/zh_TW/pagination.php index a197fb74af..d87b6631ad 100644 --- a/resources/lang/zh_TW/pagination.php +++ b/resources/lang/zh_TW/pagination.php @@ -14,4 +14,4 @@ return [ 'previous' => '« 上一頁', 'next' => '下一頁 »', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_TW/passwords.php b/resources/lang/zh_TW/passwords.php index ae5f0cc2a9..4f28fe3fb0 100644 --- a/resources/lang/zh_TW/passwords.php +++ b/resources/lang/zh_TW/passwords.php @@ -16,4 +16,4 @@ return [ 'sent' => '我們已經將密碼重置連結發送至您的電郵!', 'reset' => '你的密碼已經被重置!', 'blocked' => '好一個嘗試。', -]; \ No newline at end of file +]; diff --git a/resources/lang/zh_TW/validation.php b/resources/lang/zh_TW/validation.php index f1593f0b18..de535f4652 100644 --- a/resources/lang/zh_TW/validation.php +++ b/resources/lang/zh_TW/validation.php @@ -88,4 +88,4 @@ return [ 'in_array' => 'The :attribute field does not exist in :other.', 'present' => 'The :attribute field must be present.', 'amount_zero' => 'The total amount cannot be zero', -]; \ No newline at end of file +]; diff --git a/resources/views/accounts/create.twig b/resources/views/accounts/create.twig index 07e55d4f55..171ae13b1e 100644 --- a/resources/views/accounts/create.twig +++ b/resources/views/accounts/create.twig @@ -76,4 +76,4 @@ {% block styles %} <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/resources/views/accounts/edit.twig b/resources/views/accounts/edit.twig index 29cd807ac2..e01e7f98ee 100644 --- a/resources/views/accounts/edit.twig +++ b/resources/views/accounts/edit.twig @@ -86,4 +86,4 @@ {% block styles %} <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/resources/views/demo/accounts/index.twig b/resources/views/demo/accounts/index.twig index 1e42bbc675..9d3cf18a02 100644 --- a/resources/views/demo/accounts/index.twig +++ b/resources/views/demo/accounts/index.twig @@ -1 +1 @@ -{{ trans('demo.accounts-index') }} \ No newline at end of file +{{ trans('demo.accounts-index') }} diff --git a/resources/views/demo/budgets/index.twig b/resources/views/demo/budgets/index.twig index 737932b3df..5717ecfce4 100644 --- a/resources/views/demo/budgets/index.twig +++ b/resources/views/demo/budgets/index.twig @@ -1 +1 @@ -{{ trans('demo.budgets-index') }} \ No newline at end of file +{{ trans('demo.budgets-index') }} diff --git a/resources/views/demo/currencies/index.twig b/resources/views/demo/currencies/index.twig index 9d155e1181..1ea152a0f2 100644 --- a/resources/views/demo/currencies/index.twig +++ b/resources/views/demo/currencies/index.twig @@ -1 +1 @@ -{{ trans('demo.currencies-index') }} \ No newline at end of file +{{ trans('demo.currencies-index') }} diff --git a/resources/views/demo/home.twig b/resources/views/demo/home.twig index 84f855689a..85268e795a 100644 --- a/resources/views/demo/home.twig +++ b/resources/views/demo/home.twig @@ -1 +1 @@ -{{ trans('demo.index', {asset: route('accounts.index', ['asset']), budgets: route('budgets.index'), reports: route('reports.index')})|raw }} \ No newline at end of file +{{ trans('demo.index', {asset: route('accounts.index', ['asset']), budgets: route('budgets.index'), reports: route('reports.index')})|raw }} diff --git a/resources/views/demo/import/configure.twig b/resources/views/demo/import/configure.twig index 1d5adc6a31..a94b4914bb 100644 --- a/resources/views/demo/import/configure.twig +++ b/resources/views/demo/import/configure.twig @@ -1,3 +1,3 @@ {{ trans('demo.import-configure-security') }} <br /><br /> -{{ trans('demo.import-configure-configuration') }} \ No newline at end of file +{{ trans('demo.import-configure-configuration') }} diff --git a/resources/views/demo/import/index.twig b/resources/views/demo/import/index.twig index 00e5a8d4ce..31fe3ab386 100644 --- a/resources/views/demo/import/index.twig +++ b/resources/views/demo/import/index.twig @@ -1 +1 @@ -{{ trans('demo.import-index') }} \ No newline at end of file +{{ trans('demo.import-index') }} diff --git a/resources/views/demo/index.twig b/resources/views/demo/index.twig index 84f855689a..85268e795a 100644 --- a/resources/views/demo/index.twig +++ b/resources/views/demo/index.twig @@ -1 +1 @@ -{{ trans('demo.index', {asset: route('accounts.index', ['asset']), budgets: route('budgets.index'), reports: route('reports.index')})|raw }} \ No newline at end of file +{{ trans('demo.index', {asset: route('accounts.index', ['asset']), budgets: route('budgets.index'), reports: route('reports.index')})|raw }} diff --git a/resources/views/demo/piggy-banks/index.twig b/resources/views/demo/piggy-banks/index.twig index ea776d4f25..7c0415fc31 100644 --- a/resources/views/demo/piggy-banks/index.twig +++ b/resources/views/demo/piggy-banks/index.twig @@ -1 +1 @@ -{{ trans('demo.piggy-banks-index') }} \ No newline at end of file +{{ trans('demo.piggy-banks-index') }} diff --git a/resources/views/demo/reports/index.twig b/resources/views/demo/reports/index.twig index c7d5d9aab4..535fb63c44 100644 --- a/resources/views/demo/reports/index.twig +++ b/resources/views/demo/reports/index.twig @@ -4,4 +4,4 @@ {{ trans('demo.reports-index-examples', { one: route('reports.report.default', ['1,2,3','currentMonthStart','currentMonthEnd']), two: route('reports.report.default', ['1,2,3','20160101','20161231']), - three: route('reports.report.budget', ['1,2,3','2,1','20160101','20161231'])})|raw }} \ No newline at end of file + three: route('reports.report.budget', ['1,2,3','2,1','20160101','20161231'])})|raw }} diff --git a/resources/views/demo/transactions/index.twig b/resources/views/demo/transactions/index.twig index 8a212172ce..247ad41218 100644 --- a/resources/views/demo/transactions/index.twig +++ b/resources/views/demo/transactions/index.twig @@ -1 +1 @@ -{{ trans('demo.transactions-index') }} \ No newline at end of file +{{ trans('demo.transactions-index') }} diff --git a/resources/views/export/index.twig b/resources/views/export/index.twig index afe83f4c9c..3ebcc538ea 100644 --- a/resources/views/export/index.twig +++ b/resources/views/export/index.twig @@ -107,4 +107,4 @@ {% block styles %} <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/resources/views/javascript/variables.twig b/resources/views/javascript/variables.twig index 5fe2a12f60..6e02953236 100644 --- a/resources/views/javascript/variables.twig +++ b/resources/views/javascript/variables.twig @@ -27,4 +27,4 @@ var noDataForChart = '{{ trans('firefly.no_data_for_chart')|escape }}'; var showFullList = '{{ trans('firefly.show_full_list') }}'; var showOnlyTop = '{{ trans('firefly.show_only_top',{number:listLength}) }}'; var accountingConfig = {{ accounting|json_encode|raw }}; -var token = '{{ csrf_token() }}'; \ No newline at end of file +var token = '{{ csrf_token() }}'; diff --git a/resources/views/piggy-banks/create.twig b/resources/views/piggy-banks/create.twig index e144dc9838..b9e132dea0 100644 --- a/resources/views/piggy-banks/create.twig +++ b/resources/views/piggy-banks/create.twig @@ -63,4 +63,4 @@ {% block styles %} <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/resources/views/piggy-banks/edit.twig b/resources/views/piggy-banks/edit.twig index df25b8aebc..85f225c05b 100644 --- a/resources/views/piggy-banks/edit.twig +++ b/resources/views/piggy-banks/edit.twig @@ -65,4 +65,4 @@ {% block styles %} <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/resources/views/preferences/index.twig b/resources/views/preferences/index.twig index 5432d9887f..0b00562525 100644 --- a/resources/views/preferences/index.twig +++ b/resources/views/preferences/index.twig @@ -281,4 +281,4 @@ {% block styles %} <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/resources/views/reports/options/category.twig b/resources/views/reports/options/category.twig index 126467ca26..665e60421e 100644 --- a/resources/views/reports/options/category.twig +++ b/resources/views/reports/options/category.twig @@ -5,4 +5,4 @@ {% for category in categories %} <option value="{{ category.id }}" label="{{ category.name }}">{{ category.name }}</option> {% endfor %} -</select> \ No newline at end of file +</select> diff --git a/resources/views/rules/rule-group/select-transactions.twig b/resources/views/rules/rule-group/select-transactions.twig index e33b186cd9..b12e7fb14f 100644 --- a/resources/views/rules/rule-group/select-transactions.twig +++ b/resources/views/rules/rule-group/select-transactions.twig @@ -49,4 +49,4 @@ {% block styles %} <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/resources/views/tags/create.twig b/resources/views/tags/create.twig index 8d85dcc86a..bf78d6c3c6 100644 --- a/resources/views/tags/create.twig +++ b/resources/views/tags/create.twig @@ -91,4 +91,4 @@ {% block styles %} <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/resources/views/tags/edit.twig b/resources/views/tags/edit.twig index 4012de4137..dcf7557d85 100644 --- a/resources/views/tags/edit.twig +++ b/resources/views/tags/edit.twig @@ -94,4 +94,4 @@ {% block styles %} <link href="css/jquery-ui/jquery-ui.structure.min.css" type="text/css" rel="stylesheet" media="all"> <link href="css/jquery-ui/jquery-ui.theme.min.css" type="text/css" rel="stylesheet" media="all"> -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/tests/CreatesApplication.php b/tests/CreatesApplication.php index 8d8a77e99d..547152f6a9 100644 --- a/tests/CreatesApplication.php +++ b/tests/CreatesApplication.php @@ -19,4 +19,4 @@ trait CreatesApplication return $app; } -} \ No newline at end of file +} diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php index c23bfc63d5..486dc271a3 100644 --- a/tests/Feature/ExampleTest.php +++ b/tests/Feature/ExampleTest.php @@ -20,4 +20,4 @@ class ExampleTest extends TestCase $response->assertStatus(200); } -} \ No newline at end of file +} diff --git a/tests/TestCase.php b/tests/TestCase.php index 03cde96b56..51b7cf0735 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -12,4 +12,4 @@ use Illuminate\Foundation\Testing\TestCase as BaseTestCase; abstract class TestCase extends BaseTestCase { use CreatesApplication; -} \ No newline at end of file +} diff --git a/tests/Unit/ExampleTest.php b/tests/Unit/ExampleTest.php index 239346394a..5663bb49f8 100644 --- a/tests/Unit/ExampleTest.php +++ b/tests/Unit/ExampleTest.php @@ -17,4 +17,4 @@ class ExampleTest extends TestCase { $this->assertTrue(true); } -} \ No newline at end of file +} From b2026106ec32c59e4bfe5a3cfea29b0840891da4 Mon Sep 17 00:00:00 2001 From: James Cole <JC5@users.noreply.github.com> Date: Mon, 6 Feb 2017 08:29:18 +0100 Subject: [PATCH 655/709] Update config.php, see #521 --- resources/lang/en_US/config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lang/en_US/config.php b/resources/lang/en_US/config.php index 0c23d3f6cd..b5ed958eca 100644 --- a/resources/lang/en_US/config.php +++ b/resources/lang/en_US/config.php @@ -10,7 +10,7 @@ */ return [ - 'locale' => 'en, English, en_US, en_US.utf8', + 'locale' => 'en, English, en_US, en_US.utf8, en_US.UTF-8', 'month' => '%B %Y', 'month_and_day' => '%B %e, %Y', 'date_time' => '%B %e, %Y, @ %T', From 1285a88660b2d000710d6a060c38e186bd4c460a Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Wed, 8 Feb 2017 22:07:47 +0100 Subject: [PATCH 656/709] Different value for title field [skip ci] --- resources/views/list/piggy-banks.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/list/piggy-banks.twig b/resources/views/list/piggy-banks.twig index c754498b8f..2d6556f483 100644 --- a/resources/views/list/piggy-banks.twig +++ b/resources/views/list/piggy-banks.twig @@ -32,7 +32,7 @@ </div> </td> <td> - <a href="{{ route('piggy-banks.show', piggyBank.id) }}" title="{{ piggyBank.order }}">{{ piggyBank.name }}</a> + <a href="{{ route('piggy-banks.show', piggyBank.id) }}" title="{{ piggyBank.name }}">{{ piggyBank.name }}</a> </td> <td style="text-align: right;"> <span title="Saved so far" style="text-align:right;">{{ piggyBank.savedSoFar|formatAmount }}</span> From 3914796e4eef6ad54f7375064c5b4fd7b9bd98bb Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Fri, 10 Feb 2017 17:21:44 +0100 Subject: [PATCH 657/709] Experimental new transfer filter. --- app/Helpers/Collector/JournalCollector.php | 42 +++++++--------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index 6e2d7a0d8e..1d6a042cb3 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -618,7 +618,7 @@ class JournalCollector implements JournalCollectorInterface * account, chances are the set include double entries: transfers get selected * on both the source, and then again on the destination account. * - * This method filters them out. + * This method filters them out by removing transfers that have been selected twice. * * @param Collection $set * @@ -627,36 +627,18 @@ class JournalCollector implements JournalCollectorInterface private function filterTransfers(Collection $set): Collection { if ($this->filterTransfers) { - $set = $set->filter( - function (Transaction $transaction) { - if (!($transaction->transaction_type_type === TransactionType::TRANSFER && bccomp($transaction->transaction_amount, '0') === -1)) { - - Log::debug( - sprintf( - 'Included journal #%d (transaction #%d) because its a %s with amount %f', - $transaction->transaction_journal_id, - $transaction->id, - $transaction->transaction_type_type, - $transaction->transaction_amount - ) - ); - - return $transaction; - } - - Log::debug( - sprintf( - 'Removed journal #%d (transaction #%d) because its a %s with amount %f', - $transaction->transaction_journal_id, - $transaction->id, - $transaction->transaction_type_type, - $transaction->transaction_amount - ) - ); - - return false; + $count = []; + $new = new Collection; + /** @var Transaction $transaction */ + foreach($set as $transaction) { + $journalId =$transaction->transaction_journal_id; + if(!isset($count[$journalId]) ) { + // not yet counted? add to new set and count it: + $new->push($transaction); + $count[$journalId] = 1; } - ); + } + return $new; } return $set; From 0cbed2d5d231b6a90c92b83b596ab0dafe142988 Mon Sep 17 00:00:00 2001 From: Christian Musa <christianmusa@gmail.com> Date: Sat, 11 Feb 2017 00:33:20 -0300 Subject: [PATCH 658/709] Improve docker support - Add docker-compose file - Add environment variable to initialize the database - Add custom entrypoint that generates the .env file based on the environment variables - Update Dockerfile (gettext-base package) --- .env.docker | 55 ++++++++++++++++++++++++++++++++++++++++++++ Dockerfile | 3 +++ docker-compose.yml | 33 ++++++++++++++++++++++++++ docker/entrypoint.sh | 16 +++++++++++++ 4 files changed, 107 insertions(+) create mode 100644 .env.docker create mode 100644 docker-compose.yml create mode 100755 docker/entrypoint.sh diff --git a/.env.docker b/.env.docker new file mode 100644 index 0000000000..69765bff51 --- /dev/null +++ b/.env.docker @@ -0,0 +1,55 @@ +APP_ENV=${FF_APP_ENV} +APP_DEBUG=false +APP_FORCE_SSL=false +APP_FORCE_ROOT= +APP_KEY=${FF_APP_KEY} +APP_LOG=daily +APP_LOG_LEVEL=warning +APP_URL=http://localhost + +DB_CONNECTION=mysql +DB_HOST=${FF_DB_HOST} +DB_PORT=3306 +DB_DATABASE=${FF_DB_NAME} +DB_USERNAME=${FF_DB_USER} +DB_PASSWORD=${FF_DB_PASSWORD} + +BROADCAST_DRIVER=log +CACHE_DRIVER=file +SESSION_DRIVER=file +QUEUE_DRIVER=sync + +COOKIE_PATH="/" +COOKIE_DOMAIN= +COOKIE_SECURE=false + +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_DRIVER=smtp +MAIL_HOST=mailtrap.io +MAIL_PORT=2525 +MAIL_FROM=changeme@example.com +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null + +SEND_REGISTRATION_MAIL=true +SEND_ERROR_MESSAGE=true +SHOW_INCOMPLETE_TRANSLATIONS=false + +CACHE_PREFIX=firefly + +GOOGLE_MAPS_API_KEY= +ANALYTICS_ID= +SITE_OWNER=mail@example.com +USE_ENCRYPTION=true + +PUSHER_KEY= +PUSHER_SECRET= +PUSHER_APP_ID= + +DEMO_USERNAME= +DEMO_PASSWORD= + diff --git a/Dockerfile b/Dockerfile index 2311881871..7d00a3724b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,7 @@ RUN apt-get update -y && \ libxml2-dev \ libsqlite3-dev \ libbz2-dev \ + gettext-base \ locales && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* @@ -44,3 +45,5 @@ WORKDIR /var/www/firefly-iii RUN composer install --no-scripts --no-dev USER root + +ENTRYPOINT ["/var/www/firefly-iii/docker/entrypoint.sh"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000..cdf765d487 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,33 @@ +version: '2' + +services: + firefly-db: + image: mysql:8 + environment: + - MYSQL_DATABASE=firefly_db + - MYSQL_USER=firefly_db + - MYSQL_PASSWORD=firefly_db_secret + - MYSQL_RANDOM_ROOT_PASSWORD=yes + volumes: + - firefly-storage:/var/lib/mysql + + firefly-app: + image: firefly-iii + build: + context: . + environment: + - INIT_DATABASE=yes + - FF_DB_HOST=firefly-db + - FF_DB_NAME=firefly_db + - FF_DB_USER=firefly_db + - FF_DB_PASSWORD=firefly_db_secret + - FF_APP_KEY=SomeRandomStringOf32CharsExactly + - FF_APP_ENV=development + ports: + - "80:80" + links: + - firefly-db + +volumes: + firefly-storage: + driver: local \ No newline at end of file diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100755 index 0000000000..2ae2404c2c --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +cat .env.docker | envsubst > .env + +if [ "${INIT_DATABASE:="no"}" = "yes" ]; then + echo "Init database detected, checking mysql status" + # depends on your machine, but it may take a file to boot mysql container the first time + until php artisan firefly:verify &>/dev/null + do + echo "waiting mysql" + sleep 10 + done + php artisan migrate:refresh --seed +fi + +exec apache2-foreground \ No newline at end of file From aa2d78f36ab288472b3f844520c26f16451fe144 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 11 Feb 2017 09:34:04 +0100 Subject: [PATCH 659/709] Expand filter --- app/Helpers/Collector/JournalCollector.php | 24 ++++++++++++++++------ app/Support/Steam.php | 19 ++++++++++++++--- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index 1d6a042cb3..f1de985b9e 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -32,6 +32,7 @@ use Illuminate\Database\Query\JoinClause; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use Log; +use Steam; /** * Maybe this is a good idea after all... @@ -235,7 +236,7 @@ class JournalCollector implements JournalCollectorInterface /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); $repository->setUser($this->user); - $accounts = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]); + $accounts = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]); if ($accounts->count() > 0) { $accountIds = $accounts->pluck('id')->toArray(); $this->query->whereIn('transactions.account_id', $accountIds); @@ -628,16 +629,27 @@ class JournalCollector implements JournalCollectorInterface { if ($this->filterTransfers) { $count = []; - $new = new Collection; + $new = new Collection; /** @var Transaction $transaction */ - foreach($set as $transaction) { - $journalId =$transaction->transaction_journal_id; - if(!isset($count[$journalId]) ) { + foreach ($set as $transaction) { + if ($transaction->transaction_type_type !== TransactionType::TRANSFER) { + $new->push($transaction); + continue; + } + // make property string: + $journalId = $transaction->transaction_journal_id; + $amount = Steam::positive($transaction->transaction_amount); + $source = $transaction->account_id; + $dest = $transaction->opposing_account_id; + $key = $journalId . $source . $dest . $amount; + + if (!isset($count[$key])) { // not yet counted? add to new set and count it: $new->push($transaction); - $count[$journalId] = 1; + $count[$key] = 1; } } + return $new; } diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 0185a93caf..669c2e468b 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -26,7 +26,6 @@ use FireflyIII\Models\Transaction; class Steam { - /** * * @param \FireflyIII\Models\Account $account @@ -202,8 +201,6 @@ class Steam return $list; } - // parse PHP size: - /** * @param $string * @@ -239,4 +236,20 @@ class Steam } + // parse PHP size: + + /** + * @param string $amount + * + * @return string + */ + public function positive(string $amount): string + { + if (bccomp($amount, '0') === 1) { + $amount = bcmul($amount, '-1'); + } + + return $amount; + } + } From 5e47492318624a4388861c8f305a1f04575a5977 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 11 Feb 2017 10:05:58 +0100 Subject: [PATCH 660/709] Clean up budget report partial --- app/Helpers/Collection/Budget.php | 193 ------------------ app/Helpers/Collection/BudgetLine.php | 161 --------------- app/Helpers/Report/BudgetReportHelper.php | 63 +++--- .../Report/BudgetReportHelperInterface.php | 5 +- resources/views/reports/partials/budgets.twig | 96 ++++----- 5 files changed, 78 insertions(+), 440 deletions(-) delete mode 100644 app/Helpers/Collection/Budget.php delete mode 100644 app/Helpers/Collection/BudgetLine.php diff --git a/app/Helpers/Collection/Budget.php b/app/Helpers/Collection/Budget.php deleted file mode 100644 index f590cb6216..0000000000 --- a/app/Helpers/Collection/Budget.php +++ /dev/null @@ -1,193 +0,0 @@ -<?php -/** - * Budget.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -declare(strict_types = 1); -namespace FireflyIII\Helpers\Collection; - -use Illuminate\Support\Collection; - -/** - * - * Class Budget - * - * @package FireflyIII\Helpers\Collection - */ -class Budget -{ - /** @var Collection */ - protected $budgetLines; - /** @var string */ - protected $budgeted = '0'; - /** @var string */ - protected $left = '0'; - /** @var string */ - protected $overspent = '0'; - /** @var string */ - protected $spent = '0'; - - /** - * - */ - public function __construct() - { - $this->budgetLines = new Collection; - } - - /** - * @param BudgetLine $budgetLine - * - * @return Budget - */ - public function addBudgetLine(BudgetLine $budgetLine): Budget - { - $this->budgetLines->push($budgetLine); - - return $this; - } - - /** - * @param string $add - * - * @return Budget - */ - public function addBudgeted(string $add): Budget - { - $this->budgeted = bcadd($this->budgeted, $add); - - return $this; - } - - /** - * @param string $add - * - * @return Budget - */ - public function addLeft(string $add): Budget - { - $this->left = bcadd($this->left, $add); - - return $this; - } - - /** - * @param string $add - * - * @return Budget - */ - public function addOverspent(string $add): Budget - { - $this->overspent = bcadd($this->overspent, $add); - - return $this; - } - - /** - * @param string $add - * - * @return Budget - */ - public function addSpent(string $add): Budget - { - $this->spent = bcadd($this->spent, $add); - - return $this; - } - - /** - * @return \Illuminate\Support\Collection - */ - public function getBudgetLines(): Collection - { - return $this->budgetLines; - } - - /** - * @return string - */ - public function getBudgeted(): string - { - return $this->budgeted; - } - - /** - * @param string $budgeted - * - * @return Budget - */ - public function setBudgeted(string $budgeted): Budget - { - $this->budgeted = $budgeted; - - return $this; - } - - /** - * @return string - */ - public function getLeft(): string - { - return $this->left; - } - - /** - * @param string $left - * - * @return Budget - */ - public function setLeft(string $left): Budget - { - $this->left = $left; - - return $this; - } - - /** - * @return string - */ - public function getOverspent(): string - { - return $this->overspent; - } - - /** - * @param string $overspent - * - * @return Budget - */ - public function setOverspent(string $overspent): Budget - { - $this->overspent = $overspent; - - return $this; - } - - /** - * @return string - */ - public function getSpent(): string - { - return $this->spent; - } - - /** - * @param string $spent - * - * @return Budget - */ - public function setSpent(string $spent): Budget - { - $this->spent = $spent; - - return $this; - } - - -} diff --git a/app/Helpers/Collection/BudgetLine.php b/app/Helpers/Collection/BudgetLine.php deleted file mode 100644 index 03bf59f8ff..0000000000 --- a/app/Helpers/Collection/BudgetLine.php +++ /dev/null @@ -1,161 +0,0 @@ -<?php -/** - * BudgetLine.php - * Copyright (C) 2016 thegrumpydictator@gmail.com - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - -declare(strict_types = 1); -namespace FireflyIII\Helpers\Collection; - -use FireflyIII\Models\Budget as BudgetModel; -use FireflyIII\Models\BudgetLimit; - -/** - * - * Class BudgetLine - * - * @package FireflyIII\Helpers\Collection - */ -class BudgetLine -{ - - /** @var BudgetModel */ - protected $budget; - /** @var BudgetLimit */ - protected $budgetLimit; - /** @var string */ - protected $budgeted = '0'; - /** @var string */ - protected $left = '0'; - /** @var string */ - protected $overspent = '0'; - /** @var string */ - protected $spent = '0'; - - /** - * @return BudgetModel - */ - public function getBudget(): BudgetModel - { - return $this->budget ?? new BudgetModel; - } - - /** - * @param BudgetModel $budget - * - * @return BudgetLine - */ - public function setBudget(BudgetModel $budget): BudgetLine - { - $this->budget = $budget; - - return $this; - } - - /** - * @return BudgetLimit - */ - public function getBudgetLimit(): BudgetLimit - { - return $this->budgetLimit ?? new BudgetLimit; - } - - /** - * @param BudgetLimit $budgetLimit - * - * @return BudgetLimit - */ - public function setBudgetLimit(BudgetLimit $budgetLimit): BudgetLine - { - $this->budgetLimit = $budgetLimit; - - return $this; - } - - /** - * @return string - */ - public function getBudgeted(): string - { - return $this->budgeted; - } - - /** - * @param string $budgeted - * - * @return BudgetLine - */ - public function setBudgeted(string $budgeted): BudgetLine - { - $this->budgeted = $budgeted; - - return $this; - } - - /** - * @return string - */ - public function getLeft(): string - { - return $this->left; - } - - /** - * @param string $left - * - * @return BudgetLine - */ - public function setLeft(string $left): BudgetLine - { - $this->left = $left; - - return $this; - } - - /** - * @return string - */ - public function getOverspent(): string - { - return $this->overspent; - } - - /** - * @param string $overspent - * - * @return BudgetLine - */ - public function setOverspent(string $overspent): BudgetLine - { - $this->overspent = $overspent; - - return $this; - } - - /** - * @return string - */ - public function getSpent(): string - { - return $this->spent; - } - - /** - * @param string $spent - * - * @return BudgetLine - */ - public function setSpent(string $spent): BudgetLine - { - $this->spent = $spent; - - return $this; - } - - -} diff --git a/app/Helpers/Report/BudgetReportHelper.php b/app/Helpers/Report/BudgetReportHelper.php index dff9d99d74..8025b0a159 100644 --- a/app/Helpers/Report/BudgetReportHelper.php +++ b/app/Helpers/Report/BudgetReportHelper.php @@ -15,8 +15,6 @@ namespace FireflyIII\Helpers\Report; use Carbon\Carbon; -use FireflyIII\Helpers\Collection\Budget as BudgetCollection; -use FireflyIII\Helpers\Collection\BudgetLine; use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; @@ -48,44 +46,63 @@ class BudgetReportHelper implements BudgetReportHelperInterface * @param Carbon $end * @param Collection $accounts * - * @return BudgetCollection + * @return array */ - public function getBudgetReport(Carbon $start, Carbon $end, Collection $accounts): BudgetCollection + public function getBudgetReport(Carbon $start, Carbon $end, Collection $accounts): array { - $object = new BudgetCollection; - $set = $this->repository->getBudgets(); + $set = $this->repository->getBudgets(); + $array = []; /** @var Budget $budget */ foreach ($set as $budget) { $budgetLimits = $this->repository->getBudgetLimits($budget, $start, $end); if ($budgetLimits->count() == 0) { // no budget limit(s) for this budget + $spent = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end);// spent for budget in time range - if ($spent > 0) { - $budgetLine = new BudgetLine; - $budgetLine->setBudget($budget)->setOverspent($spent); - $object->addOverspent($spent)->addBudgetLine($budgetLine); + if (bccomp($spent, '0') === -1) { + $line = [ + 'type' => 'budget', + 'id' => $budget->id, + 'name' => $budget->name, + 'budgeted' => '0', + 'spent' => $spent, + 'left' => '0', + 'overspent' => '0', + ]; + $array[] = $line; } continue; } /** @var BudgetLimit $budgetLimit */ foreach ($budgetLimits as $budgetLimit) { // one or more repetitions for budget - $data = $this->calculateExpenses($budget, $budgetLimit, $accounts); - $budgetLine = new BudgetLine; - $budgetLine->setBudget($budget)->setBudgetLimit($budgetLimit) - ->setLeft($data['left'])->setSpent($data['expenses'])->setOverspent($data['overspent']) - ->setBudgeted(strval($budgetLimit->amount)); - - $object->addBudgeted(strval($budgetLimit->amount))->addSpent($data['spent']) - ->addLeft($data['left'])->addOverspent($data['overspent'])->addBudgetLine($budgetLine); + $data = $this->calculateExpenses($budget, $budgetLimit, $accounts); + $line = [ + 'type' => 'budget-line', + 'start' => $budgetLimit->start_date, + 'end' => $budgetLimit->end_date, + 'limit' => $budgetLimit->id, + 'id' => $budget->id, + 'name' => $budget->name, + 'budgeted' => strval($budgetLimit->amount), + 'spent' => $data['expenses'], + 'left' => $data['left'], + 'overspent' => $data['overspent'], + ]; + $array[] = $line; } } - $noBudget = $this->repository->spentInPeriodWoBudget($accounts, $start, $end); // stuff outside of budgets - $budgetLine = new BudgetLine; - $budgetLine->setOverspent($noBudget)->setSpent($noBudget); - $object->addOverspent($noBudget)->addBudgetLine($budgetLine); + $noBudget = $this->repository->spentInPeriodWoBudget($accounts, $start, $end); // stuff outside of budgets + $line = [ + 'type' => 'no-budget', + 'budgeted' => '0', + 'spent' => $noBudget, + 'left' => '0', + 'overspent' => '0', + ]; + $array[] = $line; - return $object; + return $array; } /** diff --git a/app/Helpers/Report/BudgetReportHelperInterface.php b/app/Helpers/Report/BudgetReportHelperInterface.php index 07234f9694..a64d47d39f 100644 --- a/app/Helpers/Report/BudgetReportHelperInterface.php +++ b/app/Helpers/Report/BudgetReportHelperInterface.php @@ -15,7 +15,6 @@ namespace FireflyIII\Helpers\Report; use Carbon\Carbon; -use FireflyIII\Helpers\Collection\Budget as BudgetCollection; use Illuminate\Support\Collection; /** @@ -31,9 +30,9 @@ interface BudgetReportHelperInterface * @param Carbon $end * @param Collection $accounts * - * @return BudgetCollection + * @return array */ - public function getBudgetReport(Carbon $start, Carbon $end, Collection $accounts): BudgetCollection; + public function getBudgetReport(Carbon $start, Carbon $end, Collection $accounts): array; /** * @param Carbon $start diff --git a/resources/views/reports/partials/budgets.twig b/resources/views/reports/partials/budgets.twig index be4b938c4b..97fef8c762 100644 --- a/resources/views/reports/partials/budgets.twig +++ b/resources/views/reports/partials/budgets.twig @@ -11,26 +11,33 @@ </tr> </thead> <tbody> - {% for budgetLine in budgets.getBudgetLines %} - <tr> + {% set sum_budgeted = 0 %} + {% set sum_spent = 0 %} + {% set sum_left = 0 %} + {% set sum_overspent = 0 %} + {% for line in budgets %} - {% if budgetLine.getBudget.id %} - <td data-value="{{ budgetLine.getBudget.name }}"> - <a href="{{ route('budgets.show',budgetLine.getBudget.id) }}">{{ budgetLine.getBudget.name }}</a> - </td> - {% else %} + {% set sum_budgeted = sum_budgeted + line.budgeted %} + {% set sum_spent = sum_spent + line.spent %} + {% set sum_left = sum_left + line.left %} + {% set sum_overspent = sum_overspent + line.overspent %} + + <tr> + {% if line.type == 'no-budget' %} <td data-value="zzzzzzz"> <em>{{ 'no_budget'|_ }}</em> </td> + {% else %} + <td data-value="{{ line.name }}"> + <a href="{{ route('budgets.show',line.id) }}">{{ line.name }}</a> + </td> {% endif %} - - - {% if budgetLine.getBudgetLimit.id %} - <td class="hidden-xs" data-value="{{ budgetLine.getBudgetLimit.start_date.format('Y-m-d') }}"> - <a href="{{ route('budgets.show.limit', [budgetLine.getBudget.id, budgetLine.getBudgetLimit.id]) }}"> - {{ budgetLine.getBudgetLimit.start_date.formatLocalized(monthAndDayFormat) }} + {% if line.type == 'budget-line' %} + <td class="hidden-xs" data-value="{{ line.start.format('Y-m-d') }}"> + <a href="{{ route('budgets.show.limit', [line.id, line.limit]) }}"> + {{ line.start.formatLocalized(monthAndDayFormat) }} — - {{ budgetLine.getBudgetLimit.end_date.formatLocalized(monthAndDayFormat) }} + {{ line.end.formatLocalized(monthAndDayFormat) }} </a> </td> {% else %} @@ -38,47 +45,23 @@ </td> {% endif %} - - - {% if budgetLine.getBudgetLimit.id %} - <td data-value="{{ budgetLine.getBudgetLimit.amount }}" style="text-align: right;"> - {{ budgetLine.getBudgetLimit.amount|formatAmount }} - </td> - {% else %} - <td data-value="0" style="text-align: right;"> - {{ 0|formatAmount }} - </td> - {% endif %} - - <td data-value="{{ budgetLine.getSpent }}" style="text-align: right;"> - {% if budgetLine.getSpent != 0 %} - {{ budgetLine.getSpent|formatAmount }} - <!-- <i class="fa fa-fw text-muted fa-info-circle firefly-info-button" - data-location="budget-spent-amount" data-budget-id="{{ budgetLine.getBudget.id }}"></i> - --> - {% endif %} - - {% if budgetLine.getSpent == 0 %} - {{ budgetLine.getSpent|formatAmount }} - {% endif %} + <td data-value="{{ line.budgeted }}" style="text-align: right;"> + {{ line.budgeted|formatAmount }} + </td> + <td data-value="{{ line.spent }}" style="text-align: right;"> + {{ line.spent|formatAmount }} </td> <td> - {% if budgetLine.getSpent != 0 %} + {% if line.spent != 0 %} <i class="fa fa-fw text-muted fa-info-circle firefly-info-button" - data-location="budget-spent-amount" data-budget-id="{{ budgetLine.getBudget.id }}"></i> - + data-location="budget-spent-amount" data-budget-id="{{ line.id }}"></i> {% endif %} </td> - - <td data-value="{{ budgetLine.getLeft }}" style="text-align: right;"> - {% if(budgetLine.getOverspent == 0) %} - {{ budgetLine.getLeft|formatAmount }} - {% endif %} + <td data-value="{{ line.left }}" style="text-align: right;"> + {{ line.left|formatAmount }} </td> - <td data-value="{{ budgetLine.getOverspent }}" style="text-align: right;"> - {% if budgetLine.getOverspent != 0 %} - {{ budgetLine.getOverspent|formatAmount }} - {% endif %} + <td data-value="{{ line.overspent }}" style="text-align: right;"> + {{ line.overspent|formatAmount }} </td> </tr> {% endfor %} @@ -87,18 +70,11 @@ <tr> <td><em>{{ 'sum'|_ }}</em></td> <td class="hidden-xs"> </td> - <td style="text-align: right;">{{ budgets.getBudgeted|formatAmount }}</td> - <td style="text-align: right;"> - {% if budgets.getSpent != 0 %} - <span class="text-danger">{{ budgets.getSpent|formatAmountPlain }}</span> - {% endif %} - {% if budgets.getSpent == 0 %} - {{ budgets.getSpent|formatAmount }} - {% endif %} - </td> + <td style="text-align: right;">{{ sum_budgeted|formatAmount }}</td> + <td style="text-align: right;">{{ sum_spent|formatAmount }}</td> <td> </td> - <td style="text-align: right;">{{ budgets.getLeft|formatAmount }}</td> - <td style="text-align: right;"><span class="text-danger">{{ budgets.getOverspent|formatAmountPlain }}</span></td> + <td style="text-align: right;">{{ sum_left|formatAmount }}</td> + <td style="text-align: right;">{{ sum_overspent|formatAmount }}</td> </tr> </tfoot> </table> From 8e89899070eb9febaaf131037a0bd43f5229a906 Mon Sep 17 00:00:00 2001 From: James Cole <JC5@users.noreply.github.com> Date: Sat, 11 Feb 2017 10:11:57 +0100 Subject: [PATCH 661/709] Add EOF --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index cdf765d487..a7c42b18a0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,4 +30,4 @@ services: volumes: firefly-storage: - driver: local \ No newline at end of file + driver: local From 0be3dd4fe441366176b2f22dcdb18d99db044e07 Mon Sep 17 00:00:00 2001 From: James Cole <JC5@users.noreply.github.com> Date: Sat, 11 Feb 2017 10:12:11 +0100 Subject: [PATCH 662/709] Add EOF --- docker/entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 2ae2404c2c..0baf22e862 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -13,4 +13,4 @@ if [ "${INIT_DATABASE:="no"}" = "yes" ]; then php artisan migrate:refresh --seed fi -exec apache2-foreground \ No newline at end of file +exec apache2-foreground From 4c2d9e0eee756f699a512bfa496cdd301fa1ec91 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 11 Feb 2017 15:52:55 +0100 Subject: [PATCH 663/709] Small code cleanup. --- .github/CONTRIBUTING.md | 2 +- app/Generator/Chart/Basic/ChartJsGenerator.php | 7 ++----- app/Helpers/Chart/MetaPieChart.php | 6 ++---- app/Import/ImportStorage.php | 18 ++---------------- app/Rules/Triggers/AmountExactly.php | 2 +- app/Rules/Triggers/AmountLess.php | 2 +- app/Rules/Triggers/AmountMore.php | 2 +- app/Support/Steam.php | 2 +- 8 files changed, 11 insertions(+), 30 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 3ebecdd310..612661a00f 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -16,4 +16,4 @@ Take the time to read the [installation guide FAQ](https://firefly-iii.github.io ## Pull requests -I can only accept pull requests against the `develop` branch, never the `master` branch +I can only accept pull requests against the `develop` branch, never the `master` branch. diff --git a/app/Generator/Chart/Basic/ChartJsGenerator.php b/app/Generator/Chart/Basic/ChartJsGenerator.php index 85cff4d4f6..b7588d2da2 100644 --- a/app/Generator/Chart/Basic/ChartJsGenerator.php +++ b/app/Generator/Chart/Basic/ChartJsGenerator.php @@ -14,6 +14,7 @@ declare(strict_types = 1); namespace FireflyIII\Generator\Chart\Basic; use FireflyIII\Support\ChartColour; +use Steam; /** * Class ChartJsGenerator @@ -108,11 +109,7 @@ class ChartJsGenerator implements GeneratorInterface foreach ($data as $key => $value) { // make larger than 0 - if (bccomp($value, '0') === -1) { - $value = bcmul($value, '-1'); - } - - $chartData['datasets'][0]['data'][] = $value; + $chartData['datasets'][0]['data'][] = floatval(Steam::positive($value)); $chartData['datasets'][0]['backgroundColor'][] = ChartColour::getColour($index); $chartData['labels'][] = $key; $index++; diff --git a/app/Helpers/Chart/MetaPieChart.php b/app/Helpers/Chart/MetaPieChart.php index 4290720405..c2a87fbe95 100644 --- a/app/Helpers/Chart/MetaPieChart.php +++ b/app/Helpers/Chart/MetaPieChart.php @@ -21,6 +21,7 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\User; use Illuminate\Support\Collection; +use Steam; /** * Class MetaPieChart @@ -268,10 +269,7 @@ class MetaPieChart implements MetaPieChartInterface $object = $repository->find(intval($objectId)); $names[$objectId] = $object->name; } - if (bccomp($amount, '0') === -1) { - $amount = bcmul($amount, '-1'); - } - + $amount = Steam::positive($amount); $this->total = bcadd($this->total, $amount); $chartData[$names[$objectId]] = $amount; } diff --git a/app/Import/ImportStorage.php b/app/Import/ImportStorage.php index 12b5e33a6c..ec68385e0d 100644 --- a/app/Import/ImportStorage.php +++ b/app/Import/ImportStorage.php @@ -27,6 +27,7 @@ use FireflyIII\Rules\Processor; use FireflyIII\User; use Illuminate\Support\Collection; use Log; +use Steam; /** * Class ImportStorage @@ -197,21 +198,6 @@ class ImportStorage } - /** - * @param float $amount - * - * @return string - */ - private function makePositive(float $amount): string - { - $amount = strval($amount); - if (bccomp($amount, '0', 4) === -1) { // left is larger than right - $amount = bcmul($amount, '-1'); - } - - return $amount; - } - /** * @param $entry * @@ -370,7 +356,7 @@ class ImportStorage $journal = $this->storeJournal($entry); - $amount = $this->makePositive($entry->fields['amount']); + $amount = Steam::positive($entry->fields['amount']); $accounts = $this->storeAccounts($entry); // create new transactions. This is something that needs a rewrite for multiple/split transactions. diff --git a/app/Rules/Triggers/AmountExactly.php b/app/Rules/Triggers/AmountExactly.php index cef4d8315d..e7984deb02 100644 --- a/app/Rules/Triggers/AmountExactly.php +++ b/app/Rules/Triggers/AmountExactly.php @@ -60,7 +60,7 @@ final class AmountExactly extends AbstractTrigger implements TriggerInterface { $amount = $journal->destination_amount ?? TransactionJournal::amountPositive($journal); $compare = $this->triggerValue; - $result = bccomp($amount, $compare, 4); + $result = bccomp($amount, $compare); if ($result === 0) { Log::debug(sprintf('RuleTrigger AmountExactly for journal #%d: %d matches %d exactly, so return true', $journal->id, $amount, $compare)); diff --git a/app/Rules/Triggers/AmountLess.php b/app/Rules/Triggers/AmountLess.php index cbf4e61ac3..1c856c6796 100644 --- a/app/Rules/Triggers/AmountLess.php +++ b/app/Rules/Triggers/AmountLess.php @@ -60,7 +60,7 @@ final class AmountLess extends AbstractTrigger implements TriggerInterface { $amount = $journal->destination_amount ?? TransactionJournal::amountPositive($journal); $compare = $this->triggerValue; - $result = bccomp($amount, $compare, 4); + $result = bccomp($amount, $compare); if ($result === -1) { Log::debug(sprintf('RuleTrigger AmountLess for journal #%d: %d is less than %d, so return true', $journal->id, $amount, $compare)); diff --git a/app/Rules/Triggers/AmountMore.php b/app/Rules/Triggers/AmountMore.php index d44cca0071..d8a7ce1643 100644 --- a/app/Rules/Triggers/AmountMore.php +++ b/app/Rules/Triggers/AmountMore.php @@ -66,7 +66,7 @@ final class AmountMore extends AbstractTrigger implements TriggerInterface { $amount = $journal->destination_amount ?? TransactionJournal::amountPositive($journal); $compare = $this->triggerValue; - $result = bccomp($amount, $compare, 4); + $result = bccomp($amount, $compare); if ($result === 1) { Log::debug(sprintf('RuleTrigger AmountMore for journal #%d: %d is more than %d, so return true', $journal->id, $amount, $compare)); diff --git a/app/Support/Steam.php b/app/Support/Steam.php index 669c2e468b..870b55566c 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -245,7 +245,7 @@ class Steam */ public function positive(string $amount): string { - if (bccomp($amount, '0') === 1) { + if (bccomp($amount, '0') === -1) { $amount = bcmul($amount, '-1'); } From b5a005dcc57d44b31fde40c7f467c44157ddfc75 Mon Sep 17 00:00:00 2001 From: James Cole <thegrumpydictator@gmail.com> Date: Sat, 11 Feb 2017 16:32:03 +0100 Subject: [PATCH 664/709] Better 404 page [skip ci] --- resources/views/errors/404.twig | 287 +++++--------------------------- 1 file changed, 40 insertions(+), 247 deletions(-) diff --git a/resources/views/errors/404.twig b/resources/views/errors/404.twig index 41481b8501..d68a915eb6 100644 --- a/resources/views/errors/404.twig +++ b/resources/views/errors/404.twig @@ -1,254 +1,47 @@ <!DOCTYPE html> <html> <head> - <meta charset="UTF-8"/> - <meta name="robots" content="noindex,nofollow"/> - <style> - /* Copyright (c) 2010, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.com/yui/license.html */ - html { - color: #000; - background: #FFF; - } - - body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, textarea, p, blockquote, th, td { - margin: 0; - padding: 0; - } - - table { - border-collapse: collapse; - border-spacing: 0; - } - - fieldset, img { - border: 0; - } - - address, caption, cite, code, dfn, em, strong, th, var { - font-style: normal; - font-weight: normal; - } - - li { - list-style: none; - } - - caption, th { - text-align: left; - } - - h1, h2, h3, h4, h5, h6 { - font-size: 100%; - font-weight: normal; - } - - q:before, q:after { - content: ''; - } - - abbr, acronym { - border: 0; - font-variant: normal; - } - - sup { - vertical-align: text-top; - } - - sub { - vertical-align: text-bottom; - } - - input, textarea, select { - font-family: inherit; - font-size: inherit; - font-weight: inherit; - } - - input, textarea, select { - *font-size: 100%; - } - - legend { - color: #000; - } - - html { - background: #eee; - padding: 10px - } - - img { - border: 0; - } - - #sf-resetcontent { - width: 970px; - margin: 0 auto; - } - - .sf-reset { - font: 11px Verdana, Arial, sans-serif; - color: #333 - } - - .sf-reset .clear { - clear: both; - height: 0; - font-size: 0; - line-height: 0; - } - - .sf-reset .clear_fix:after { - display: block; - height: 0; - clear: both; - visibility: hidden; - } - - .sf-reset .clear_fix { - display: inline-block; - } - - .sf-reset * html .clear_fix { - height: 1%; - } - - .sf-reset .clear_fix { - display: block; - } - - .sf-reset, .sf-reset .block { - margin: auto - } - - .sf-reset abbr { - border-bottom: 1px dotted #000; - cursor: help; - } - - .sf-reset p { - font-size: 14px; - line-height: 20px; - color: #868686; - padding-bottom: 20px - } - - .sf-reset strong { - font-weight: bold; - } - - .sf-reset a { - color: #6c6159; - cursor: default; - } - - .sf-reset a img { - border: none; - } - - .sf-reset a:hover { - text-decoration: underline; - } - - .sf-reset em { - font-style: italic; - } - - .sf-reset h1, .sf-reset h2 { - font: 20px Georgia, "Times New Roman", Times, serif - } - - .sf-reset .exception_counter { - background-color: #fff; - color: #333; - padding: 6px; - float: left; - margin-right: 10px; - display: block; - } - - .sf-reset .exception_title { - margin-left: 3em; - margin-bottom: 0.7em; - display: block; - } - - .sf-reset .exception_message { - margin-left: 3em; - display: block; - } - - .sf-reset .traces li { - font-size: 12px; - padding: 2px 4px; - list-style-type: decimal; - margin-left: 20px; - } - - .sf-reset .block { - background-color: #FFFFFF; - padding: 10px 28px; - margin-bottom: 20px; - -webkit-border-bottom-right-radius: 16px; - -webkit-border-bottom-left-radius: 16px; - -moz-border-radius-bottomright: 16px; - -moz-border-radius-bottomleft: 16px; - border-bottom-right-radius: 16px; - border-bottom-left-radius: 16px; - border-bottom: 1px solid #ccc; - border-right: 1px solid #ccc; - border-left: 1px solid #ccc; - } - - .sf-reset .block_exception { - background-color: #ddd; - color: #333; - padding: 20px; - -webkit-border-top-left-radius: 16px; - -webkit-border-top-right-radius: 16px; - -moz-border-radius-topleft: 16px; - -moz-border-radius-topright: 16px; - border-top-left-radius: 16px; - border-top-right-radius: 16px; - border-top: 1px solid #ccc; - border-right: 1px solid #ccc; - border-left: 1px solid #ccc; - overflow: hidden; - word-wrap: break-word; - } - - .sf-reset a { - background: none; - color: #868686; - text-decoration: none; - } - - .sf-reset a:hover { - background: none; - color: #313131; - text-decoration: underline; - } - - .sf-reset ol { - padding: 10px 0; - } - - .sf-reset h1 { - background-color: #FFFFFF; - padding: 15px 28px; - margin-bottom: 20px; - -webkit-border-radius: 10px; - -moz-border-radius: 10px; - border-radius: 10px; - border: 1px solid #ccc; - } - </style> + <meta charset="UTF-8"> + <meta name="robots" content="noindex, nofolllow, noarchive, noodp, NoImageIndex, noydir"> + <title>404 + + + + + + + + {% include('partials.favicons') %} - -

    -

    Sorry, the page you are looking for could not be found.

    + +
    + +
    +
    +

    404 — Firefly III cannot find this page.

    +
    +
    + +
    +
    +

    + The page you have requested does not exist. Please check that you have not entered + the wrong URL. Did you make a typo perhaps? +

    +
    +
    +
    +
    +

    + If you are sure this page should exist, please open a ticket on + Github. +

    +
    +
    + - + \ No newline at end of file From dcc2b9c1cbe9709dc99fc52791c84144af9ff8b0 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 11 Feb 2017 18:35:16 +0100 Subject: [PATCH 665/709] This fixes #559 --- app/Http/Requests/AccountFormRequest.php | 4 ++-- resources/views/accounts/create.twig | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Http/Requests/AccountFormRequest.php b/app/Http/Requests/AccountFormRequest.php index cbd07f46ee..cadf22b600 100644 --- a/app/Http/Requests/AccountFormRequest.php +++ b/app/Http/Requests/AccountFormRequest.php @@ -77,11 +77,11 @@ class AccountFormRequest extends Request return [ 'id' => $idRule, 'name' => $nameRule, - 'openingBalance' => 'numeric', + 'openingBalance' => 'numeric|required_with:openingBalanceDate', + 'openingBalanceDate' => 'date|required_with:openingBalance', 'iban' => 'iban', 'BIC' => 'bic', 'virtualBalance' => 'numeric', - 'openingBalanceDate' => 'date', 'currency_id' => 'exists:transaction_currencies,id', 'accountNumber' => 'between:1,255|uniqueAccountNumberForUser', 'accountRole' => 'in:' . $accountRoles, diff --git a/resources/views/accounts/create.twig b/resources/views/accounts/create.twig index 171ae13b1e..2063b86e79 100644 --- a/resources/views/accounts/create.twig +++ b/resources/views/accounts/create.twig @@ -40,7 +40,7 @@ {% if what == 'asset' %} {{ ExpandedForm.balance('openingBalance') }} - {{ ExpandedForm.date('openingBalanceDate', phpdate('Y-m-d')) }} + {{ ExpandedForm.date('openingBalanceDate') }} {{ ExpandedForm.select('accountRole', roles,null,{'helpText' : 'asset_account_role_help'|_}) }} {{ ExpandedForm.balance('virtualBalance') }} {% endif %} From 78074a5a5496253a209b250d28f299956171408c Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 11 Feb 2017 18:38:38 +0100 Subject: [PATCH 666/709] Code for #553 --- app/Http/Controllers/AccountController.php | 1 - app/Http/Controllers/BillController.php | 1 - app/Http/Controllers/BudgetController.php | 3 --- app/Http/Controllers/CategoryController.php | 1 - app/Http/Controllers/TransactionController.php | 2 -- 5 files changed, 8 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 6ba0cc2248..66b2ff26a0 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -311,7 +311,6 @@ class AccountController extends Controller // replace with journal collector: /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->setLimit($pageSize)->setPage($page); $journals = $collector->getPaginatedJournals(); $journals->setPath('accounts/show/' . $account->id . '/' . $date); diff --git a/app/Http/Controllers/BillController.php b/app/Http/Controllers/BillController.php index c6aa3c0509..ae95ba23ef 100644 --- a/app/Http/Controllers/BillController.php +++ b/app/Http/Controllers/BillController.php @@ -209,7 +209,6 @@ class BillController extends Controller // use collector: /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->setLimit($pageSize)->setPage($page)->withBudgetInformation() ->withCategoryInformation(); $journals = $collector->getPaginatedJournals(); diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index d85670dbfe..f21bc904fa 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -203,7 +203,6 @@ class BudgetController extends Controller // collector /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withoutBudget(); $journals = $collector->getPaginatedJournals(); $journals->setPath('/budgets/list/noBudget'); @@ -245,7 +244,6 @@ class BudgetController extends Controller // collector: /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page)->withCategoryInformation(); $journals = $collector->getPaginatedJournals(); $journals->setPath('/budgets/show/' . $budget->id); @@ -283,7 +281,6 @@ class BudgetController extends Controller // collector: /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector->setAllAssetAccounts()->setRange($budgetLimit->start_date, $budgetLimit->end_date) ->setBudget($budget)->setLimit($pageSize)->setPage($page)->withCategoryInformation(); $journals = $collector->getPaginatedJournals(); diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index ab8583af1a..2224cb3512 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -158,7 +158,6 @@ class CategoryController extends Controller // new collector: /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector->setAllAssetAccounts()->setRange($start, $end)->withoutCategory();//->groupJournals(); $journals = $collector->getJournals(); $subTitle = trans( diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index ba38605b8f..3557144c94 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -73,7 +73,6 @@ class TransactionController extends Controller /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts(); $collector->setRange($start, $end)->withBudgetInformation()->withCategoryInformation(); @@ -124,7 +123,6 @@ class TransactionController extends Controller $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->withBudgetInformation()->withCategoryInformation(); // do not filter transfers if $what = transfer. From abf0fdcf35dd1a42f0ad2466ce0d0ab094a3378a Mon Sep 17 00:00:00 2001 From: Enrico Lamperti Date: Sat, 11 Feb 2017 17:38:04 -0300 Subject: [PATCH 667/709] Fix smooth curves in graphs --- public/js/ff/charts.defaults.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/public/js/ff/charts.defaults.js b/public/js/ff/charts.defaults.js index c670667dd1..24095df4da 100644 --- a/public/js/ff/charts.defaults.js +++ b/public/js/ff/charts.defaults.js @@ -11,6 +11,11 @@ /** global: accounting */ var defaultChartOptions = { + elements: { + line : { + cubicInterpolationMode: 'monotone' + } + }, scales: { xAxes: [ { From 1a204d31e76a6e5d87fe8d95aa77c1fdcdb006bc Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 12 Feb 2017 10:58:37 +0100 Subject: [PATCH 668/709] Small fix for opening balance issues in reports. --- app/Repositories/Account/AccountTasker.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/Repositories/Account/AccountTasker.php b/app/Repositories/Account/AccountTasker.php index d43902e094..e5c81eb211 100644 --- a/app/Repositories/Account/AccountTasker.php +++ b/app/Repositories/Account/AccountTasker.php @@ -126,13 +126,15 @@ class AccountTasker implements AccountTaskerInterface ]; // get first journal date: - $first = $repository->oldestJournal($account); + $first = $repository->oldestJournal($account); + Log::debug(sprintf('Date of first journal for %s is %s', $account->name, $first->date->format('Y-m-d'))); $entry['start_balance'] = $startSet[$account->id] ?? '0'; $entry['end_balance'] = $endSet[$account->id] ?? '0'; - if (!is_null($first->id) && $yesterday < $first->date && $end >= $first->date) { - // something about balance? + + // first journal exists, and is on start, then this is the actual opening balance: + if (!is_null($first->id) && $first->date->isSameDay($start)) { $entry['start_balance'] = $first->transactions()->where('account_id', $account->id)->first()->amount; - Log::debug(sprintf('Account was opened before %s, so opening balance is %f', $yesterday->format('Y-m-d'), $entry['start_balance'])); + Log::debug(sprintf('Account %s was opened on %s, so opening balance is %f', $account->name, $start->format('Y-m-d'), $entry['start_balance'])); } $return['start'] = bcadd($return['start'], $entry['start_balance']); $return['end'] = bcadd($return['end'], $entry['end_balance']); From 5528663727d80cd6f47e9e270ffeadf02c00460c Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 12 Feb 2017 11:25:17 +0100 Subject: [PATCH 669/709] Re-implemented basic account controller tests. --- phpunit.coverage.xml | 9 +- phpunit.xml | 9 +- .../Controllers/AccountControllerTest.php | 213 ++++++++++++++++++ tests/Feature/ExampleTest.php | 21 +- tests/TestCase.php | 77 ++++++- 5 files changed, 319 insertions(+), 10 deletions(-) create mode 100644 tests/Feature/Controllers/AccountControllerTest.php diff --git a/phpunit.coverage.xml b/phpunit.coverage.xml index 75e3afd143..d643a5d6ec 100755 --- a/phpunit.coverage.xml +++ b/phpunit.coverage.xml @@ -10,11 +10,12 @@ beStrictAboutOutputDuringTests="true" stopOnFailure="true"> - - ./tests/acceptance + + ./tests/Feature - - ./tests/unit + + + ./tests/Unit diff --git a/phpunit.xml b/phpunit.xml index c096c7a945..b0e315533c 100755 --- a/phpunit.xml +++ b/phpunit.xml @@ -10,11 +10,12 @@ beStrictAboutOutputDuringTests="true" stopOnFailure="true"> - - ./tests/acceptance + + ./tests/Feature - - ./tests/unit + + + ./tests/Unit diff --git a/tests/Feature/Controllers/AccountControllerTest.php b/tests/Feature/Controllers/AccountControllerTest.php new file mode 100644 index 0000000000..37c9b79740 --- /dev/null +++ b/tests/Feature/Controllers/AccountControllerTest.php @@ -0,0 +1,213 @@ +be($this->user()); + $response = $this->get(route('accounts.create', ['asset'])); + $response->assertStatus(200); + // has bread crumb + $response->assertSee('
    - + From c8f6b42ce65ab058a7d36fa84c00c266fd3aac91 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 15 Feb 2017 22:02:02 +0100 Subject: [PATCH 702/709] Fix tests --- app/Generator/Report/Audit/MonthReportGenerator.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/Generator/Report/Audit/MonthReportGenerator.php b/app/Generator/Report/Audit/MonthReportGenerator.php index d210f8b5d3..25f255f3a7 100644 --- a/app/Generator/Report/Audit/MonthReportGenerator.php +++ b/app/Generator/Report/Audit/MonthReportGenerator.php @@ -129,6 +129,16 @@ class MonthReportGenerator implements ReportGeneratorInterface return $this; } + /** + * @param Collection $tags + * + * @return ReportGeneratorInterface + */ + public function setTags(Collection $tags): ReportGeneratorInterface + { + return $this; + } + /** * @param Account $account * @param Carbon $date From 8bfcc3315a82aad09464211ab479dce5961f5b80 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 16 Feb 2017 21:01:22 +0100 Subject: [PATCH 703/709] This fixes #572 --- app/Http/Controllers/Auth/TwoFactorController.php | 10 +++++++--- app/Http/Controllers/JavascriptController.php | 4 +++- app/Http/Controllers/PreferencesController.php | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/Auth/TwoFactorController.php b/app/Http/Controllers/Auth/TwoFactorController.php index d48f5906f2..bf3b425161 100644 --- a/app/Http/Controllers/Auth/TwoFactorController.php +++ b/app/Http/Controllers/Auth/TwoFactorController.php @@ -17,6 +17,7 @@ use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\TokenFormRequest; +use Illuminate\Http\Request; use Log; use Preferences; use Session; @@ -30,11 +31,14 @@ class TwoFactorController extends Controller { /** - * @return mixed + * @param Request $request + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View * @throws FireflyException */ - public function index() + public function index(Request $request) { + $user = auth()->user(); // to make sure the validator in the next step gets the secret, we push it in session @@ -50,7 +54,7 @@ class TwoFactorController extends Controller if (strlen(strval($secret)) === 0) { throw new FireflyException('Your two factor authentication secret is empty, which it cannot be at this point. Please check the log files.'); } - Session::flash('two-factor-secret', $secret); + $request->session()->flash('two-factor-secret', $secret); return view('auth.two-factor', compact('user', 'title')); } diff --git a/app/Http/Controllers/JavascriptController.php b/app/Http/Controllers/JavascriptController.php index 719371598e..6e63e57a43 100644 --- a/app/Http/Controllers/JavascriptController.php +++ b/app/Http/Controllers/JavascriptController.php @@ -13,6 +13,7 @@ namespace FireflyIII\Http\Controllers; use Amount; use FireflyIII\Exceptions\FireflyException; +use Illuminate\Http\Request; use Navigation; use Preferences; use Session; @@ -28,7 +29,7 @@ class JavascriptController extends Controller /** * */ - public function variables() + public function variables(Request $request) { $picker = $this->getDateRangePicker(); $start = Session::get('start'); @@ -52,6 +53,7 @@ class JavascriptController extends Controller 'localeconv' => $localeconv, 'language' => $lang, ]; + $request->session()->keep(['two-factor-secret']); return response() ->view('javascript.variables', $data, 200) diff --git a/app/Http/Controllers/PreferencesController.php b/app/Http/Controllers/PreferencesController.php index f4bd94c4b5..4373d61204 100644 --- a/app/Http/Controllers/PreferencesController.php +++ b/app/Http/Controllers/PreferencesController.php @@ -56,9 +56,9 @@ class PreferencesController extends Controller { $domain = $this->getDomain(); /** @noinspection PhpMethodParametersCountMismatchInspection */ - $secret = $google2fa->generateSecretKey(16, auth()->user()->id); + $secret = $google2fa->generateSecretKey(32, auth()->user()->id); Session::flash('two-factor-secret', $secret); - $image = $google2fa->getQRCodeInline('Firefly III at ' . $domain, null, $secret, 150); + $image = $google2fa->getQRCodeInline('Firefly III at ' . $domain, auth()->user()->email, $secret, 150); return view('preferences.code', compact('image')); From 4fbf0291e669daec53459d78b51fa0a5bf903933 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 16 Feb 2017 21:06:31 +0100 Subject: [PATCH 704/709] Updated composer things. --- composer.json | 10 ++-------- composer.lock | 48 +++++++++++++++++++++++------------------------- 2 files changed, 25 insertions(+), 33 deletions(-) diff --git a/composer.json b/composer.json index 8e493a3031..aa09eb9b44 100755 --- a/composer.json +++ b/composer.json @@ -49,7 +49,7 @@ "php": ">=7.0.0", "ext-intl": "*", "laravel/framework": "5.4.*", - "davejamesmiller/laravel-breadcrumbs": "3.1", + "davejamesmiller/laravel-breadcrumbs": "3.*", "watson/validating": "3.*", "doctrine/dbal": "^2.5", "league/commonmark": "0.15.*", @@ -105,11 +105,5 @@ }, "config": { "preferred-install": "dist" - }, - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/firefly-iii/laravel-breadcrumbs.git" - } - ] + } } diff --git a/composer.lock b/composer.lock index fea0f26c74..d7fca941ad 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "bccf2c2fdac584664dd6c952f1d91ba8", + "content-hash": "254ffef07304b93679b48182a179123f", "packages": [ { "name": "bacon/bacon-qr-code", @@ -104,16 +104,16 @@ }, { "name": "davejamesmiller/laravel-breadcrumbs", - "version": "3.1", + "version": "3.0.2", "source": { "type": "git", - "url": "https://github.com/firefly-iii/laravel-breadcrumbs.git", - "reference": "afebafc321432188b10dafc7b4c072501687f3d0" + "url": "https://github.com/davejamesmiller/laravel-breadcrumbs.git", + "reference": "6ca5a600003ecb52a5b5af14dad82033058604e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/firefly-iii/laravel-breadcrumbs/zipball/afebafc321432188b10dafc7b4c072501687f3d0", - "reference": "afebafc321432188b10dafc7b4c072501687f3d0", + "url": "https://api.github.com/repos/davejamesmiller/laravel-breadcrumbs/zipball/6ca5a600003ecb52a5b5af14dad82033058604e1", + "reference": "6ca5a600003ecb52a5b5af14dad82033058604e1", "shasum": "" }, "require": { @@ -133,6 +133,7 @@ "DaveJamesMiller\\Breadcrumbs\\": "src/" } }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT License" ], @@ -148,10 +149,7 @@ "keywords": [ "laravel" ], - "support": { - "source": "https://github.com/firefly-iii/laravel-breadcrumbs/tree/3.1" - }, - "time": "2017-01-30T07:52:50+00:00" + "time": "2017-01-30T21:16:53+00:00" }, { "name": "doctrine/annotations", @@ -667,16 +665,16 @@ }, { "name": "laravel/framework", - "version": "v5.4.11", + "version": "v5.4.12", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "9cee8db07cfac13a3a49d17388c9ced4c52e17ac" + "reference": "707f32d32dce58232f7a860e0a1d62caf6f9dbfc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/9cee8db07cfac13a3a49d17388c9ced4c52e17ac", - "reference": "9cee8db07cfac13a3a49d17388c9ced4c52e17ac", + "url": "https://api.github.com/repos/laravel/framework/zipball/707f32d32dce58232f7a860e0a1d62caf6f9dbfc", + "reference": "707f32d32dce58232f7a860e0a1d62caf6f9dbfc", "shasum": "" }, "require": { @@ -792,7 +790,7 @@ "framework", "laravel" ], - "time": "2017-02-10T19:40:54+00:00" + "time": "2017-02-15T14:31:32+00:00" }, { "name": "laravelcollective/html", @@ -1585,16 +1583,16 @@ }, { "name": "swiftmailer/swiftmailer", - "version": "v5.4.5", + "version": "v5.4.6", "source": { "type": "git", "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "cd142238a339459b10da3d8234220963f392540c" + "reference": "81fdccfaf8bdc5d5d7a1ef6bb3a61bbb1a6c4a3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/cd142238a339459b10da3d8234220963f392540c", - "reference": "cd142238a339459b10da3d8234220963f392540c", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/81fdccfaf8bdc5d5d7a1ef6bb3a61bbb1a6c4a3e", + "reference": "81fdccfaf8bdc5d5d7a1ef6bb3a61bbb1a6c4a3e", "shasum": "" }, "require": { @@ -1635,7 +1633,7 @@ "mail", "mailer" ], - "time": "2016-12-29T10:02:40+00:00" + "time": "2017-02-13T07:52:53+00:00" }, { "name": "symfony/console", @@ -2738,16 +2736,16 @@ }, { "name": "barryvdh/laravel-ide-helper", - "version": "v2.2.3", + "version": "v2.3.0", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-ide-helper.git", - "reference": "a7fc2ec489aada6062d3a63ddc915004a21e38af" + "reference": "555d3e37009bdb78f5d8bcea6eb8a816529a5cfa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/a7fc2ec489aada6062d3a63ddc915004a21e38af", - "reference": "a7fc2ec489aada6062d3a63ddc915004a21e38af", + "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/555d3e37009bdb78f5d8bcea6eb8a816529a5cfa", + "reference": "555d3e37009bdb78f5d8bcea6eb8a816529a5cfa", "shasum": "" }, "require": { @@ -2800,7 +2798,7 @@ "phpstorm", "sublime" ], - "time": "2017-01-05T21:20:42+00:00" + "time": "2017-02-13T19:20:12+00:00" }, { "name": "barryvdh/reflection-docblock", From fe54b2fa3a3b77e9ec5405e83778b6627c571c31 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 16 Feb 2017 22:13:04 +0100 Subject: [PATCH 705/709] Names are now links [skip ci] --- CHANGELOG.md | 115 ++++++++++++++++++++++++++------------------------- 1 file changed, 58 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91120fe9b2..ab72b06288 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,17 +3,18 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## [4.3.3] - 2017-02-02 + +## [4.3.4] - 2017-02-02 ### Fixed -- Fixed bug #550, reported by @worldworm! -- Fixed bug #551, reported by @t-me! +- Fixed bug #550, reported by [worldworm](https://github.com/worldworm)! +- Fixed bug #551, reported by [t-me](https://github.com/t-me)! ## [4.3.3] - 2017-01-30 _The 100th release of Firefly!_ ### Added -- Add locales to Docker (#534) by @elohmeier. +- Add locales to Docker (#534) by [elohmeier](https://github.com/elohmeier). - Optional database encryption. On by default. - Datepicker for Firefox and other browsers. - New instruction block for updating and installing. @@ -44,35 +45,35 @@ An intermediate release because something in the Twig and Twigbridge libraries i ### Fixed - The password reset routine was broken. -- Issue #522, thanks to @xpfgsyb -- Issue #524, thanks to @worldworm -- Issue #526, thanks to @worldworm -- Issue #528, thanks to @skibbipl +- Issue #522, thanks to [xpfgsyb](https://github.com/xpfgsyb) +- Issue #524, thanks to [worldworm](https://github.com/worldworm) +- Issue #526, thanks to [worldworm](https://github.com/worldworm) +- Issue #528, thanks to [skibbipl](https://github.com/skibbipl) - Various other fixes. ## [4.3.1] - 2017-01-04 ### Added - Support for Russian and Polish. - Support for a proper demo website. -- Support for custom decimal places in currencies (#506, suggested by @xpfgsyb). -- Most amounts are now right-aligned (#511, suggested by @xpfgsyb). +- Support for custom decimal places in currencies (#506, suggested by [xpfgsyb](https://github.com/xpfgsyb)). +- Most amounts are now right-aligned (#511, suggested by [xpfgsyb](https://github.com/xpfgsyb)). - German is now a "complete" language, more than 75% translated! ### Changed - **[New Github repository!](github.com/firefly-iii/firefly-iii)** - Better category overview. -- #502, thanks to @zjean +- #502, thanks to [zjean](https://github.com/zjean) ### Removed - Removed a lot of administration functions. - Removed ability to activate users. ### Fixed -- #501, thanks to @zjean -- #513, thanks to @skibbipl +- #501, thanks to [zjean](https://github.com/zjean) +- #513, thanks to [skibbipl](https://github.com/skibbipl) ### Security -- #519, thanks to @xpfgsyb +- #519, thanks to [xpfgsyb](https://github.com/xpfgsyb) ## [4.3.0] - 2015-12-26 ### Added @@ -105,7 +106,7 @@ An intermediate release because something in the Twig and Twigbridge libraries i ### Fixed - Various bugs -- Issue #472 thanks to @zjean +- Issue #472 thanks to [zjean](https://github.com/zjean) ## [4.2.1] - 2016-12-09 ### Added @@ -140,10 +141,10 @@ An intermediate release because something in the Twig and Twigbridge libraries i ### Fixed - Issue #408 - Various issues with split journals -- Issue #414, thx @zjean -- Issue #419, thx @schwalberich -- Issue #422, thx @xzaz -- Various import bugs, such as #416 (@zjean) +- Issue #414, thx [zjean](https://github.com/zjean) +- Issue #419, thx [schwalberich](https://github.com/schwalberich) +- Issue #422, thx [xzaz](https://github.com/xzaz) +- Various import bugs, such as #416 ([zjean](https://github.com/zjean)) ### Security @@ -165,9 +166,9 @@ An intermediate release because something in the Twig and Twigbridge libraries i ### Fixed - Made all pages more mobile friendly. -- Fixed #395 found by @marcoveeneman. -- Fixed #398 found by @marcoveeneman. -- Fixed #401 found by @marcoveeneman. +- Fixed #395 found by [marcoveeneman](https://github.com/marcoveeneman). +- Fixed #398 found by [marcoveeneman](https://github.com/marcoveeneman). +- Fixed #401 found by [marcoveeneman](https://github.com/marcoveeneman). - Many optimizations. - Updated many libraries. - Various bugs found by myself. @@ -179,13 +180,13 @@ An intermediate release because something in the Twig and Twigbridge libraries i ### Changed - Greatly expanded help pages and their function. -- Built a new transaction collector, which I think was the idea of @roberthorlings originally. +- Built a new transaction collector, which I think was the idea of [roberthorlings](https://github.com/roberthorlings) originally. - Rebuilt seach engine. ### Fixed -- #375, thanks to @schoentoon which made it impossible to resurrect currencies. -- #370 thanks to @ksmolder -- #378, thanks to @HomelessAvatar +- #375, thanks to [schoentoon](https://github.com/schoentoon) which made it impossible to resurrect currencies. +- #370 thanks to [ksmolder](https://github.com/ksmolder) +- #378, thanks to [HomelessAvatar](https://github.com/HomelessAvatar) ## [4.1.5] - 2016-11-01 ### Changed @@ -198,7 +199,7 @@ An intermediate release because something in the Twig and Twigbridge libraries i ## [4.1.4] - 2016-10-30 ### Added -- New Dockerfile thanks to @schoentoon +- New Dockerfile thanks to [schoentoon](https://github.com/schoentoon) - Added changing the destination account as rule action. - Added changing the source account as rule action. - Can convert transactions into different types. @@ -211,10 +212,10 @@ An intermediate release because something in the Twig and Twigbridge libraries i - Change error message to refer to solution. ### Fixed -- #367 thanks to @HungryFeline -- #366 thanks to @3mz3t -- #362 and #341 thanks to @bnw -- #355 thanks to @roberthorlings +- #367 thanks to [HungryFeline](https://github.com/HungryFeline) +- #366 thanks to [3mz3t](https://github.com/3mz3t) +- #362 and #341 thanks to [bnw](https://github.com/bnw) +- #355 thanks to [roberthorlings](https://github.com/roberthorlings) ## [4.1.3] - 2016-10-22 ### Fixed @@ -246,29 +247,29 @@ An intermediate release because something in the Twig and Twigbridge libraries i ### Fixed - #357, where non utf-8 files would break Firefly. -- Tab delimiter is not properly loaded from import configuration (@roberthorlings) +- Tab delimiter is not properly loaded from import configuration ([roberthorlings](https://github.com/roberthorlings)) - System response to yearly bills ## [4.0.2] - 2016-10-14 ### Added -- Added ``intl`` dependency to composer file to ease installation (thanks @telyn) +- Added ``intl`` dependency to composer file to ease installation (thanks [telyn](https://github.com/telyn)) - Added support for Croatian. ### Changed - Updated all copyright notices to refer to the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/) - Fixed #344 -- Fixed #346, thanks to @SanderKleykens +- Fixed #346, thanks to [SanderKleykens](https://github.com/SanderKleykens) - #351 - Did some internal remodelling. ### Fixed -- PostgreSQL compatibility thanks to @SanderKleykens -- @RobertHorlings fixed a bug in the ABN Amro import specific. +- PostgreSQL compatibility thanks to [SanderKleykens](https://github.com/SanderKleykens) +- [roberthorlings](https://github.com/roberthorlings) fixed a bug in the ABN Amro import specific. ## [4.0.1] - 2016-10-04 ### Added -- New ING import specific by @tomwerf +- New ING import specific by [tomwerf](https://github.com/tomwerf) - New Presidents Choice specific to fix #307 - Added some trimming (#335) @@ -283,10 +284,10 @@ An intermediate release because something in the Twig and Twigbridge libraries i ### Fixed - Fixed a bug where incoming transactions would not be properly filtered in several reports. -- #334 by @cyberkov +- #334 by [cyberkov](https://github.com/cyberkov) - #337 - #336 -- #338 found by @roberthorlings +- #338 found by [roberthorlings](https://github.com/roberthorlings) ### Security - Initial release. @@ -297,29 +298,29 @@ An intermediate release because something in the Twig and Twigbridge libraries i ## [4.0.0] - 2015-09-26 ### Added - Upgraded to Laravel 5.3, most other libraries upgraded as well. -- Added GBP as currency, thanks to @Mortalife +- Added GBP as currency, thanks to [Mortalife](https://github.com/Mortalife) ### Changed - Jump to version 4.0.0. - Firefly III is now subject to a [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/) license. Previous versions of this software are still MIT licensed. ### Fixed -- Support for specific decimal places, thanks to @Mortalife +- Support for specific decimal places, thanks to [Mortalife](https://github.com/Mortalife) - Various CSS fixes -- Various bugs, thanks to @fuf, @sandermulders and @vissert +- Various bugs, thanks to [fuf](https://github.com/fuf), [sandermulders](https://github.com/sandermulders) and [vissert](https://github.com/vissert) - Various queries optimized for MySQL 5.7 ## [3.10.4] - 2015-09-14 ### Fixed -- Migration fix by @sandermulders -- Tricky import bug fix thanks to @vissert -- Currency preference will be correctly pulled from user settings, thanks to @fuf +- Migration fix by [sandermulders](https://github.com/sandermulders) +- Tricky import bug fix thanks to [vissert](https://github.com/vissert) +- Currency preference will be correctly pulled from user settings, thanks to [fuf](https://github.com/fuf) - Simplified code for upgrade instructions. ## [3.10.3] - 2016-08-29 ### Added -- More fields for mass-edit, thanks to @Vissert (#282) +- More fields for mass-edit, thanks to [vissert](https://github.com/vissert) (#282) - First start of German translation ### Changed @@ -330,7 +331,7 @@ An intermediate release because something in the Twig and Twigbridge libraries i ### Fixed - A bug in the translation routine broke the import. -- It was possible to destroy your Firefly installation by removing all currencies. Thanks @mondjef +- It was possible to destroy your Firefly installation by removing all currencies. Thanks [mondjef](https://github.com/mondjef) - Translation bugs. - Import bug. @@ -350,8 +351,8 @@ An intermediate release because something in the Twig and Twigbridge libraries i ### Fixed - Bug in the mass edit routines. -- Firefly III over a proxy will now work (see [issue #290](https://github.com/firefly-iii/firefly-iii/issues/290)), thanks @dfiel for reporting. -- Sneaky bug in the import routine, fixed by @Bonno +- Firefly III over a proxy will now work (see [issue #290](https://github.com/firefly-iii/firefly-iii/issues/290)), thanks [dfiel](https://github.com/dfiel) for reporting. +- Sneaky bug in the import routine, fixed by [Bonno](https://github.com/Bonno) ## [3.10.1] - 2016-08-25 ### Added @@ -389,15 +390,15 @@ An intermediate release because something in the Twig and Twigbridge libraries i ### Fixed - Issue #264 - Issue #265 -- Fixed amount calculation problems, #266, thanks @xzaz +- Fixed amount calculation problems, #266, thanks [xzaz](https://github.com/xzaz) - Issue #271 -- Issue #278, #273, thanks @StevenReitsma and @rubella +- Issue #278, #273, thanks [StevenReitsma](https://github.com/StevenReitsma) and [rubella](https://github.com/rubella) - Bug in attachment download routine would report the wrong size to the user's browser. - Various NULL errors fixed. - Various strict typing errors fixed. -- Fixed pagination problems, #276, thanks @xzaz +- Fixed pagination problems, #276, thanks [xzaz](https://github.com/xzaz) - Fixed a bug where an expense would be assigned to a piggy bank if you created a transfer first. -- Bulk update problems, #280, thanks @stickgrinder +- Bulk update problems, #280, thanks [stickgrinder](https://github.com/stickgrinder) - Fixed various problems with amount reporting of split transactions. ## [3.9.1] @@ -406,8 +407,8 @@ An intermediate release because something in the Twig and Twigbridge libraries i ## [3.9.0] ### Added -- @zjean has added code that allows you to force "https://"-URL's. -- @tonicospinelli has added Portuguese (Brazil) translations. +- [zjean](https://github.com/zjean) has added code that allows you to force "https://"-URL's. +- [tonicospinelli](https://github.com/tonicospinelli) has added Portuguese (Brazil) translations. - Firefly III supports the *splitting* of transactions: - A withdrawal (expense) can be split into multiple sub-transactions (with multiple destinations) - Likewise for deposits (incomes). You can set multiple sources. @@ -438,7 +439,7 @@ An intermediate release because something in the Twig and Twigbridge libraries i ### Fixed - Several CSV related bugs. - Several other bugs. -- Bugs fixed by @Bonno. +- Bugs fixed by [Bonno](https://github.com/Bonno). ## [3.8.3] - 2016-04-17 ### Added @@ -485,7 +486,7 @@ An intermediate release because something in the Twig and Twigbridge libraries i ### Added - Two factor authentication, thanks to the excellent work of [zjean](https://github.com/zjean). - A new chart showing your net worth in year and multi-year reports. -- You can now see if your current or future rules actually match any transactions, thanks to the excellent work of @roberthorlings. +- You can now see if your current or future rules actually match any transactions, thanks to the excellent work of [roberthorlings](https://github.com/roberthorlings). - New date fields for transactions. They are not used yet in reports or anything, but they can be filled in. - New routine to export your data. - Firefly III will mail the site owner when blocked users try to login, or when blocked domains are used in registrations. From fa39330ceb819a3fabb08c0550595c29093d7414 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 16 Feb 2017 22:30:46 +0100 Subject: [PATCH 706/709] New change log [skip ci] --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab72b06288..73c018a7db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,29 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [4.3.5] - 2017-02-19 +### Added +- Beta support for Sandstorm.IO +- Docker support by [@schoentoon](https://github.com/schoentoon), [@elohmeier](https://github.com/elohmeier), [@patrickkostjens](https://github.com/patrickkostjens) and [@crash7](https://github.com/crash7)! +### Changed +- Updated to laravel 5.4! +- User friendly error message +- Updated locales to support more operating systems, first reported in #536 by [dabenzel](https://github.com/dabenzel) +- Updated budget report +- Improved 404 page +- Smooth curves, improved by [elamperti](https://github.com/elamperti). + +### Fixed +- #549 +- #553 +- Fixed #559 reported by [elamperti](https://github.com/elamperti). +- #565, as reported by a user over the mail +- #566, as reported by [dspeckmann](https://github.com/dspeckmann) +- #567, as reported by [winsomniak](https://github.com/winsomniak) +- #569, as reported by [winsomniak](https://github.com/winsomniak) +- #572, as reported by [zjean](https://github.com/zjean) +- Many issues with the transaction filters which will fix reports (they tended to display the wrong amount). ## [4.3.4] - 2017-02-02 ### Fixed From 8460ee2dc4d3db72e9b88f2cf9aea4518a9a8284 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 16 Feb 2017 22:30:57 +0100 Subject: [PATCH 707/709] Small fix in read me [skip ci] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5005beca67..5013f18eab 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Firefly works on the principle that if you know where you're money is going, you - Firefly can import any CSV file, so migrating from other systems is easy. - Firefly runs on your own server, so you are fully in control of your data. Remember, there is no such thing as "the cloud", it’s just somebody else’s computer! -- Firefly has lots of features without becoming fancy or bloated. +- Firefly has lots of features without being fancy or bloated. - If you feel you're missing something you can just ask me and I'll add it! Firefly is pretty awesome. [You can read more about Firefly III, and its features, on the Github Pages](https://firefly-iii.github.io/). From 4ff5f526ba4e3afb36c0891282698a5d38abd572 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 16 Feb 2017 22:33:32 +0100 Subject: [PATCH 708/709] Add new lines to files. --- app/Http/Middleware/StartFireflySession.php | 2 +- app/Providers/FireflySessionProvider.php | 2 +- resources/views/errors/404.twig | 2 +- resources/views/reports/options/budget.twig | 2 +- tests/Feature/Controllers/AccountControllerTest.php | 2 +- tests/Feature/Controllers/Admin/ConfigurationControllerTest.php | 2 +- tests/Feature/Controllers/Admin/HomeControllerTest.php | 2 +- tests/Feature/Controllers/Admin/UserControllerTest.php | 2 +- tests/Feature/Controllers/AttachmentControllerTest.php | 2 +- tests/Feature/Controllers/Auth/TwoFactorControllerTest.php | 2 +- tests/Feature/Controllers/BillControllerTest.php | 2 +- tests/Feature/Controllers/BudgetControllerTest.php | 2 +- tests/Feature/Controllers/CategoryControllerTest.php | 2 +- tests/Feature/Controllers/Chart/AccountControllerTest.php | 2 +- tests/Feature/Controllers/Chart/BillControllerTest.php | 2 +- tests/Feature/Controllers/Chart/BudgetControllerTest.php | 2 +- tests/Feature/Controllers/Chart/CategoryControllerTest.php | 2 +- .../Feature/Controllers/Chart/CategoryReportControllerTest.php | 2 +- tests/Feature/Controllers/Chart/PiggyBankControllerTest.php | 2 +- tests/Feature/Controllers/Chart/ReportControllerTest.php | 2 +- tests/Feature/Controllers/CurrencyControllerTest.php | 2 +- tests/Feature/Controllers/ExportControllerTest.php | 2 +- tests/Feature/Controllers/HelpControllerTest.php | 2 +- tests/Feature/Controllers/HomeControllerTest.php | 2 +- tests/Feature/Controllers/ImportControllerTest.php | 2 +- tests/Feature/Controllers/JsonControllerTest.php | 2 +- tests/Feature/Controllers/NewUserControllerTest.php | 2 +- tests/Feature/Controllers/PiggyBankControllerTest.php | 2 +- tests/Feature/Controllers/Popup/ReportControllerTest.php | 2 +- tests/Feature/Controllers/PreferencesControllerTest.php | 2 +- tests/Feature/Controllers/ProfileControllerTest.php | 2 +- tests/Feature/Controllers/Report/AccountControllerTest.php | 2 +- tests/Feature/Controllers/Report/BalanceControllerTest.php | 2 +- tests/Feature/Controllers/Report/BudgetControllerTest.php | 2 +- tests/Feature/Controllers/Report/CategoryControllerTest.php | 2 +- tests/Feature/Controllers/Report/OperationsControllerTest.php | 2 +- tests/Feature/Controllers/ReportControllerTest.php | 2 +- tests/Feature/Controllers/RuleControllerTest.php | 2 +- tests/Feature/Controllers/RuleGroupControllerTest.php | 2 +- tests/Feature/Controllers/SearchControllerTest.php | 2 +- tests/Feature/Controllers/TagControllerTest.php | 2 +- tests/Feature/Controllers/Transaction/ConvertControllerTest.php | 2 +- tests/Feature/Controllers/Transaction/MassControllerTest.php | 2 +- tests/Feature/Controllers/Transaction/SingleControllerTest.php | 2 +- tests/Feature/Controllers/Transaction/SplitControllerTest.php | 2 +- tests/Feature/Controllers/TransactionControllerTest.php | 2 +- 46 files changed, 46 insertions(+), 46 deletions(-) diff --git a/app/Http/Middleware/StartFireflySession.php b/app/Http/Middleware/StartFireflySession.php index 121491118d..ba6c518a08 100644 --- a/app/Http/Middleware/StartFireflySession.php +++ b/app/Http/Middleware/StartFireflySession.php @@ -52,4 +52,4 @@ class StartFireflySession extends StartSession } } -} \ No newline at end of file +} diff --git a/app/Providers/FireflySessionProvider.php b/app/Providers/FireflySessionProvider.php index 77eb25f0da..47dfb1bfe7 100644 --- a/app/Providers/FireflySessionProvider.php +++ b/app/Providers/FireflySessionProvider.php @@ -61,4 +61,4 @@ class FireflySessionProvider extends ServiceProvider } ); } -} \ No newline at end of file +} diff --git a/resources/views/errors/404.twig b/resources/views/errors/404.twig index d68a915eb6..d84ff35ca6 100644 --- a/resources/views/errors/404.twig +++ b/resources/views/errors/404.twig @@ -44,4 +44,4 @@ - \ No newline at end of file + diff --git a/resources/views/reports/options/budget.twig b/resources/views/reports/options/budget.twig index ee85764b89..103363f156 100644 --- a/resources/views/reports/options/budget.twig +++ b/resources/views/reports/options/budget.twig @@ -7,4 +7,4 @@ {% endfor %} - \ No newline at end of file + diff --git a/tests/Feature/Controllers/AccountControllerTest.php b/tests/Feature/Controllers/AccountControllerTest.php index aa4f23e378..b283f3e5d6 100644 --- a/tests/Feature/Controllers/AccountControllerTest.php +++ b/tests/Feature/Controllers/AccountControllerTest.php @@ -210,4 +210,4 @@ class AccountControllerTest extends TestCase $response->assertSee('
    {{ trans('csv.column') }} #{{ loop.index }} + {% if data.columnHeaders[i] == '' %} + {{ trans('csv.column') }} #{{ loop.index }} + {% else %} + {{ data.columnHeaders[i] }} + {% endif %} + {% if data.columns[i]|length == 0 %} {{ trans('csv.no_example_data') }} From fa54763425c7dd3efd29c1da281bd513ec6ad9c5 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 15 Feb 2017 15:22:04 +0100 Subject: [PATCH 692/709] This fixes #570 --- app/Providers/AppServiceProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 56ff3467cf..d8dd3843f5 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -33,7 +33,7 @@ class AppServiceProvider extends ServiceProvider // force root URL. $forcedUrl = env('APP_FORCE_ROOT', ''); - if (strlen($forcedUrl) > 0) { + if (strlen(strval($forcedUrl)) > 0) { URL::forceRootUrl($forcedUrl); } From f0783df1239f2255ed4c7e2426f03ef8170bfc13 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 15 Feb 2017 15:24:01 +0100 Subject: [PATCH 693/709] This should fix #566 --- app/Http/Controllers/Auth/LoginController.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 016be84894..04e8abcc06 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -30,13 +30,6 @@ class LoginController extends Controller use AuthenticatesUsers; - /** - * Where to redirect users after login / registration. - * - * @var string - */ - protected $redirectTo = '/'; - /** * Create a new controller instance. * @@ -100,6 +93,14 @@ class LoginController extends Controller return redirect('/'); } + /** + * @return string + */ + public function redirectTo(): string + { + return route('index'); + } + /** * Show the application login form. * From 636b371b86ec10f1093a625b18215c6556984d73 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 15 Feb 2017 16:20:16 +0100 Subject: [PATCH 694/709] Fixes #553 --- app/Generator/Report/Audit/MonthReportGenerator.php | 1 - app/Generator/Report/Budget/MonthReportGenerator.php | 1 - app/Generator/Report/Category/MonthReportGenerator.php | 2 -- app/Helpers/Chart/MetaPieChart.php | 2 -- app/Helpers/Report/ReportHelper.php | 1 - app/Http/Controllers/Chart/BudgetController.php | 1 - app/Http/Controllers/Chart/BudgetReportController.php | 3 --- app/Http/Controllers/Chart/CategoryReportController.php | 6 ------ app/Http/Controllers/Popup/ReportController.php | 7 ------- app/Import/Setup/CsvSetup.php | 2 -- app/Jobs/ExecuteRuleGroupOnExistingTransactions.php | 2 +- 11 files changed, 1 insertion(+), 27 deletions(-) diff --git a/app/Generator/Report/Audit/MonthReportGenerator.php b/app/Generator/Report/Audit/MonthReportGenerator.php index 02cc9d08c2..d210f8b5d3 100644 --- a/app/Generator/Report/Audit/MonthReportGenerator.php +++ b/app/Generator/Report/Audit/MonthReportGenerator.php @@ -140,7 +140,6 @@ class MonthReportGenerator implements ReportGeneratorInterface /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector->setAccounts(new Collection([$account]))->setRange($this->start, $this->end); $journals = $collector->getJournals(); $journals = $journals->reverse(); diff --git a/app/Generator/Report/Budget/MonthReportGenerator.php b/app/Generator/Report/Budget/MonthReportGenerator.php index 3ab5227ecc..d3166ead4e 100644 --- a/app/Generator/Report/Budget/MonthReportGenerator.php +++ b/app/Generator/Report/Budget/MonthReportGenerator.php @@ -186,7 +186,6 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) ->setTypes([TransactionType::WITHDRAWAL]) ->setBudgets($this->budgets)->withOpposingAccount()->disableFilter(); diff --git a/app/Generator/Report/Category/MonthReportGenerator.php b/app/Generator/Report/Category/MonthReportGenerator.php index 865121006e..d137b6e8b5 100644 --- a/app/Generator/Report/Category/MonthReportGenerator.php +++ b/app/Generator/Report/Category/MonthReportGenerator.php @@ -196,7 +196,6 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) ->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) ->setCategories($this->categories)->withOpposingAccount()->disableFilter(); @@ -220,7 +219,6 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) ->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) ->setCategories($this->categories)->withOpposingAccount(); diff --git a/app/Helpers/Chart/MetaPieChart.php b/app/Helpers/Chart/MetaPieChart.php index c2a87fbe95..66addbd0b1 100644 --- a/app/Helpers/Chart/MetaPieChart.php +++ b/app/Helpers/Chart/MetaPieChart.php @@ -99,7 +99,6 @@ class MetaPieChart implements MetaPieChartInterface if ($this->collectOtherObjects && $direction === 'income') { /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector->setAccounts($this->accounts)->setRange($this->start, $this->end)->setTypes([TransactionType::DEPOSIT]); $journals = $collector->getJournals(); $sum = strval($journals->sum('transaction_amount')); @@ -205,7 +204,6 @@ class MetaPieChart implements MetaPieChartInterface } /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector->setAccounts($this->accounts); $collector->setRange($this->start, $this->end); $collector->setTypes($types); diff --git a/app/Helpers/Report/ReportHelper.php b/app/Helpers/Report/ReportHelper.php index f49805cd1b..2dfb605ee3 100644 --- a/app/Helpers/Report/ReportHelper.php +++ b/app/Helpers/Report/ReportHelper.php @@ -69,7 +69,6 @@ class ReportHelper implements ReportHelperInterface $repository = app(BillRepositoryInterface::class); $bills = $repository->getBillsForAccounts($accounts); $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector->setAccounts($accounts)->setRange($start, $end)->setBills($bills); $journals = $collector->getJournals(); $collection = new BillCollection; diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index 2c792fa908..cf99421aee 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -417,7 +417,6 @@ class BudgetController extends Controller // collector /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $types = [TransactionType::WITHDRAWAL]; $collector->setAllAssetAccounts()->setTypes($types)->setRange($start, $end)->withoutBudget(); $journals = $collector->getJournals(); diff --git a/app/Http/Controllers/Chart/BudgetReportController.php b/app/Http/Controllers/Chart/BudgetReportController.php index b5ab26355c..d570eae6b8 100644 --- a/app/Http/Controllers/Chart/BudgetReportController.php +++ b/app/Http/Controllers/Chart/BudgetReportController.php @@ -80,7 +80,6 @@ class BudgetReportController extends Controller $helper = app(MetaPieChartInterface::class); $helper->setAccounts($accounts); $helper->setBudgets($budgets); - $helper->setUser(auth()->user()); $helper->setStart($start); $helper->setEnd($end); $helper->setCollectOtherObjects(intval($others) === 1); @@ -106,7 +105,6 @@ class BudgetReportController extends Controller $helper = app(MetaPieChartInterface::class); $helper->setAccounts($accounts); $helper->setBudgets($budgets); - $helper->setUser(auth()->user()); $helper->setStart($start); $helper->setEnd($end); $helper->setCollectOtherObjects(intval($others) === 1); @@ -240,7 +238,6 @@ class BudgetReportController extends Controller { /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) ->setBudgets($budgets)->withOpposingAccount()->disableFilter(); $accountIds = $accounts->pluck('id')->toArray(); diff --git a/app/Http/Controllers/Chart/CategoryReportController.php b/app/Http/Controllers/Chart/CategoryReportController.php index 0cd50c308c..94f6cfb370 100644 --- a/app/Http/Controllers/Chart/CategoryReportController.php +++ b/app/Http/Controllers/Chart/CategoryReportController.php @@ -80,7 +80,6 @@ class CategoryReportController extends Controller $helper = app(MetaPieChartInterface::class); $helper->setAccounts($accounts); $helper->setCategories($categories); - $helper->setUser(auth()->user()); $helper->setStart($start); $helper->setEnd($end); $helper->setCollectOtherObjects(intval($others) === 1); @@ -105,7 +104,6 @@ class CategoryReportController extends Controller $helper = app(MetaPieChartInterface::class); $helper->setAccounts($accounts); $helper->setCategories($categories); - $helper->setUser(auth()->user()); $helper->setStart($start); $helper->setEnd($end); $helper->setCollectOtherObjects(intval($others) === 1); @@ -130,7 +128,6 @@ class CategoryReportController extends Controller $helper = app(MetaPieChartInterface::class); $helper->setAccounts($accounts); $helper->setCategories($categories); - $helper->setUser(auth()->user()); $helper->setStart($start); $helper->setEnd($end); $helper->setCollectOtherObjects(intval($others) === 1); @@ -156,7 +153,6 @@ class CategoryReportController extends Controller $helper = app(MetaPieChartInterface::class); $helper->setAccounts($accounts); $helper->setCategories($categories); - $helper->setUser(auth()->user()); $helper->setStart($start); $helper->setEnd($end); $helper->setCollectOtherObjects(intval($others) === 1); @@ -285,7 +281,6 @@ class CategoryReportController extends Controller { /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) ->setCategories($categories)->withOpposingAccount()->disableFilter(); $accountIds = $accounts->pluck('id')->toArray(); @@ -307,7 +302,6 @@ class CategoryReportController extends Controller { /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) ->setCategories($categories)->withOpposingAccount(); $accountIds = $accounts->pluck('id')->toArray(); diff --git a/app/Http/Controllers/Popup/ReportController.php b/app/Http/Controllers/Popup/ReportController.php index 7bd8803fa5..a2f3505923 100644 --- a/app/Http/Controllers/Popup/ReportController.php +++ b/app/Http/Controllers/Popup/ReportController.php @@ -104,7 +104,6 @@ class ReportController extends Controller case ($role === BalanceLine::ROLE_DEFAULTROLE && !is_null($budget->id)): /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector ->setAccounts(new Collection([$account])) ->setRange($attributes['startDate'], $attributes['endDate']) @@ -116,7 +115,6 @@ class ReportController extends Controller $budget->name = strval(trans('firefly.no_budget')); /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector ->setAccounts(new Collection([$account])) ->setTypes($types) @@ -127,7 +125,6 @@ class ReportController extends Controller case ($role === BalanceLine::ROLE_DIFFROLE): /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector ->setAccounts(new Collection([$account])) ->setTypes($types) @@ -173,7 +170,6 @@ class ReportController extends Controller $budget = $repository->find(intval($attributes['budgetId'])); /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector ->setAccounts($attributes['accounts']) @@ -208,7 +204,6 @@ class ReportController extends Controller $types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]; /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector->setAccounts($attributes['accounts'])->setTypes($types) ->setRange($attributes['startDate'], $attributes['endDate']) ->setCategory($category); @@ -236,7 +231,6 @@ class ReportController extends Controller $types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]; /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setTypes($types); $journals = $collector->getJournals(); $report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report @@ -273,7 +267,6 @@ class ReportController extends Controller $types = [TransactionType::DEPOSIT, TransactionType::TRANSFER]; /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); $collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setTypes($types); $journals = $collector->getJournals(); $report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report diff --git a/app/Import/Setup/CsvSetup.php b/app/Import/Setup/CsvSetup.php index ddf17a5ca2..3d0fc10ed3 100644 --- a/app/Import/Setup/CsvSetup.php +++ b/app/Import/Setup/CsvSetup.php @@ -182,8 +182,6 @@ class CsvSetup implements SetupInterface { /** @var AccountRepositoryInterface $repository */ $repository = app(AccountRepositoryInterface::class); - $repository->setUser(auth()->user()); - $importId = $data['csv_import_account'] ?? 0; $account = $repository->find(intval($importId)); diff --git a/app/Jobs/ExecuteRuleGroupOnExistingTransactions.php b/app/Jobs/ExecuteRuleGroupOnExistingTransactions.php index 15a852414e..0ac899ffcb 100644 --- a/app/Jobs/ExecuteRuleGroupOnExistingTransactions.php +++ b/app/Jobs/ExecuteRuleGroupOnExistingTransactions.php @@ -157,7 +157,7 @@ class ExecuteRuleGroupOnExistingTransactions extends Job implements ShouldQueue { /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setUser(auth()->user()); + $collector->setUser($this->user); $collector->setAccounts($this->accounts)->setRange($this->startDate, $this->endDate); return $collector->getJournals(); From 395a7bb33ca5431715a55dc05d29b3ca451fc234 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 15 Feb 2017 17:07:56 +0100 Subject: [PATCH 695/709] This should fix the tests --- app/Helpers/Chart/MetaPieChart.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/Helpers/Chart/MetaPieChart.php b/app/Helpers/Chart/MetaPieChart.php index 66addbd0b1..6c340b5389 100644 --- a/app/Helpers/Chart/MetaPieChart.php +++ b/app/Helpers/Chart/MetaPieChart.php @@ -261,7 +261,6 @@ class MetaPieChart implements MetaPieChartInterface $chartData = []; $names = []; $repository = app($this->repositories[$type]); - $repository->setUser($this->user); foreach ($array as $objectId => $amount) { if (!isset($names[$objectId])) { $object = $repository->find(intval($objectId)); From 893f2d2c55253a05f3c62611777f88c5dff629b8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 15 Feb 2017 17:17:15 +0100 Subject: [PATCH 696/709] Include code climate config [skip ci] --- .codeclimate.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .codeclimate.yml diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 0000000000..c521815c72 --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,2 @@ +exclude_paths: +- "resources/lang/**" \ No newline at end of file From 07ecb3f0e8f69037826bfeb5520cfcefff205d5c Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 15 Feb 2017 17:23:56 +0100 Subject: [PATCH 697/709] Expand code climate config [skip ci] --- .codeclimate.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.codeclimate.yml b/.codeclimate.yml index c521815c72..09a13940ca 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -1,2 +1,15 @@ +engines: + csslint: + enabled: true + duplication: + enabled: true + eslint: + enabled: true + fixme: + enabled: true + phpcodesniffer: + enabled: true + phpmd: + enabled: true exclude_paths: - "resources/lang/**" \ No newline at end of file From e8f76d896b7e1893a3fbd5370daa1e65fbe538f1 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 15 Feb 2017 17:25:36 +0100 Subject: [PATCH 698/709] Expand code climate config [skip ci] --- .codeclimate.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.codeclimate.yml b/.codeclimate.yml index 09a13940ca..56c3c3b858 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -3,6 +3,10 @@ engines: enabled: true duplication: enabled: true + config: + languages: + - php + - javascript eslint: enabled: true fixme: From ae06f1b8f011ea03f516b257c6f5d5190828afb8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 15 Feb 2017 18:16:07 +0100 Subject: [PATCH 699/709] Yeah never mind. [skip ci] --- .codeclimate.yml | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 .codeclimate.yml diff --git a/.codeclimate.yml b/.codeclimate.yml deleted file mode 100644 index 56c3c3b858..0000000000 --- a/.codeclimate.yml +++ /dev/null @@ -1,19 +0,0 @@ -engines: - csslint: - enabled: true - duplication: - enabled: true - config: - languages: - - php - - javascript - eslint: - enabled: true - fixme: - enabled: true - phpcodesniffer: - enabled: true - phpmd: - enabled: true -exclude_paths: -- "resources/lang/**" \ No newline at end of file From a5f8aa914f29e4c31d7e1226506878784a94971a Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 15 Feb 2017 20:07:10 +0100 Subject: [PATCH 700/709] Build code for tag report. --- .../Report/Budget/MonthReportGenerator.php | 10 +++ .../Report/Category/MonthReportGenerator.php | 10 +++ .../Report/ReportGeneratorInterface.php | 7 ++ .../Report/Standard/MonthReportGenerator.php | 10 +++ .../Standard/MultiYearReportGenerator.php | 10 +++ .../Report/Standard/YearReportGenerator.php | 10 +++ app/Http/Controllers/ReportController.php | 69 +++++++++++++++++++ app/Http/Requests/ReportFormRequest.php | 24 ++++++- app/Support/Binder/TagList.php | 52 ++++++++++++++ config/firefly.php | 3 +- public/js/ff/reports/index.js | 62 +++++++---------- resources/lang/en_US/firefly.php | 9 ++- resources/views/reports/index.twig | 1 + resources/views/reports/options/budget.twig | 18 ++--- resources/views/reports/options/category.twig | 19 ++--- resources/views/reports/options/tag.twig | 10 +++ routes/web.php | 1 + 17 files changed, 265 insertions(+), 60 deletions(-) create mode 100644 app/Support/Binder/TagList.php create mode 100644 resources/views/reports/options/tag.twig 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']); } From 8f5289b7dc5db89961805f4abd3f22107c9a7108 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 15 Feb 2017 21:55:50 +0100 Subject: [PATCH 701/709] Fixed some transaction list filter issues. --- app/Helpers/Collector/JournalCollector.php | 14 +++++---- .../Controllers/TransactionController.php | 31 ++++++------------- app/Support/Twig/General.php | 15 +++++++++ resources/views/list/journals-tasker.twig | 21 ++++++++++--- 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index f1de985b9e..945f604f6c 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -506,6 +506,7 @@ class JournalCollector implements JournalCollectorInterface { $this->joinOpposingTables(); + /** $accountIds = $this->accountIds; $this->query->where( function (EloquentBuilder $q1) use ($accountIds) { @@ -535,6 +536,7 @@ class JournalCollector implements JournalCollectorInterface ); } ); + * **/ return $this; } @@ -637,12 +639,12 @@ class JournalCollector implements JournalCollectorInterface continue; } // make property string: - $journalId = $transaction->transaction_journal_id; - $amount = Steam::positive($transaction->transaction_amount); - $source = $transaction->account_id; - $dest = $transaction->opposing_account_id; - $key = $journalId . $source . $dest . $amount; - + $journalId = $transaction->transaction_journal_id; + $amount = Steam::positive($transaction->transaction_amount); + $accountIds = [intval($transaction->account_id), intval($transaction->opposing_account_id)]; + sort($accountIds); + $key = $journalId . '-' . join(',', $accountIds) . '-' . $amount; + Log::debug(sprintf('Key is %s', $key)); if (!isset($count[$key])) { // not yet counted? add to new set and count it: $new->push($transaction); diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 3557144c94..b0ef6013ab 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -73,15 +73,10 @@ class TransactionController extends Controller /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts(); - $collector->setRange($start, $end)->withBudgetInformation()->withCategoryInformation(); - - // do not filter transfers if $what = transfer. - if (!in_array($what, ['transfer', 'transfers'])) { - Log::debug('Also get opposing account info.'); - $collector->withOpposingAccount(); - } - + $collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->setRange($start, $end)->withBudgetInformation() + ->withCategoryInformation(); + $collector->withOpposingAccount(); + $collector->disableInternalFilter(); $journals = $collector->getPaginatedJournals(); $journals->setPath('transactions/' . $what); @@ -122,14 +117,11 @@ class TransactionController extends Controller $subTitle = sprintf('%s (%s)', trans('firefly.title_' . $what), strtolower(trans('firefly.everything'))); $page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page')); + /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); $collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts()->withBudgetInformation()->withCategoryInformation(); - - // do not filter transfers if $what = transfer. - if (!in_array($what, ['transfer', 'transfers'])) { - Log::debug('Also get opposing account info.'); - $collector->withOpposingAccount(); - } + $collector->withOpposingAccount(); + $collector->disableInternalFilter(); $journals = $collector->getPaginatedJournals(); $journals->setPath('transactions/' . $what . '/all'); @@ -165,13 +157,8 @@ class TransactionController extends Controller $collector->setUser(auth()->user()); $collector->setTypes($types)->setLimit($pageSize)->setPage($page)->setAllAssetAccounts(); $collector->setRange($start, $end)->withBudgetInformation()->withCategoryInformation(); - - // do not filter transfers if $what = transfer. - if (!in_array($what, ['transfer', 'transfers'])) { - Log::debug('Also get opposing account info.'); - $collector->withOpposingAccount(); - } - + $collector->withOpposingAccount(); + $collector->disableInternalFilter(); $journals = $collector->getPaginatedJournals(); $journals->setPath('transactions/' . $what . '/' . $date); diff --git a/app/Support/Twig/General.php b/app/Support/Twig/General.php index cfd98fcafc..5d339960fa 100644 --- a/app/Support/Twig/General.php +++ b/app/Support/Twig/General.php @@ -17,6 +17,7 @@ use Carbon\Carbon; use FireflyIII\Models\Account; use FireflyIII\Models\TransactionJournal; use Route; +use Steam; use Twig_Extension; use Twig_SimpleFilter; use Twig_SimpleFunction; @@ -43,6 +44,7 @@ class General extends Twig_Extension $this->balance(), $this->formatFilesize(), $this->mimeIcon(), + ]; } @@ -59,6 +61,7 @@ class General extends Twig_Extension $this->env(), $this->getAmountFromJournal(), $this->activeRouteStrict(), + $this->steamPositive(), $this->activeRoutePartial(), $this->activeRoutePartialWhat(), ]; @@ -288,6 +291,18 @@ class General extends Twig_Extension ); } + /** + * @return Twig_SimpleFunction + */ + protected function steamPositive() + { + return new Twig_SimpleFunction( + 'steam_positive', function (string $str): string { + return Steam::positive($str); + } + ); + } + /** * @return Twig_SimpleFunction */ diff --git a/resources/views/list/journals-tasker.twig b/resources/views/list/journals-tasker.twig index 184ab9431f..7502719e27 100644 --- a/resources/views/list/journals-tasker.twig +++ b/resources/views/list/journals-tasker.twig @@ -33,7 +33,11 @@ -
    {% if sorting %}{% endif %}
    +
    {% if sorting %}{% endif %}
    - - {{ formatByCode(transaction.transaction_currency_code, transaction.transaction_amount) }} - - {{ optionalJournalAmount(transaction.journal_id, transaction.transaction_amount, transaction.transaction_currency_code, transaction.transaction_type_type) }} + {% if transaction.transaction_type_type == 'Transfer' %} + + {{ formatByCode(transaction.transaction_currency_code, steam_positive(transaction.transaction_amount)) }} + + {{ optionalJournalAmount(transaction.journal_id, transaction.transaction_amount, transaction.transaction_currency_code, transaction.transaction_type_type) }} + {% else %} + + {{ formatByCode(transaction.transaction_currency_code, transaction.transaction_amount) }} + + {{ optionalJournalAmount(transaction.journal_id, transaction.transaction_amount, transaction.transaction_currency_code, transaction.transaction_type_type) }} + {% endif %}