diff --git a/.ci/phpstan.sh b/.ci/phpstan.sh index 175f7b8c14..01e34546b3 100755 --- a/.ci/phpstan.sh +++ b/.ci/phpstan.sh @@ -1,5 +1,25 @@ #!/usr/bin/env bash +# +# phpstan.sh +# Copyright (c) 2021 james@firefly-iii.org +# +# This file is part of Firefly III (https://github.com/firefly-iii). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + # Install composer packages composer install --no-scripts --no-ansi diff --git a/.ci/phpunit.sh b/.ci/phpunit.sh index 6a2ff64bc9..c96fa69e96 100755 --- a/.ci/phpunit.sh +++ b/.ci/phpunit.sh @@ -1,5 +1,25 @@ #!/usr/bin/env bash +# +# phpunit.sh +# Copyright (c) 2021 james@firefly-iii.org +# +# This file is part of Firefly III (https://github.com/firefly-iii). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + # enable test .env file. cp .ci/.env.ci ../.env diff --git a/.deploy/heroku/.locales b/.deploy/heroku/.locales index d7e31f22c2..e14b3e1dfa 100644 --- a/.deploy/heroku/.locales +++ b/.deploy/heroku/.locales @@ -13,6 +13,7 @@ nb_NO nl_NL pl_PL pt_BR +pt_PT ro_RO ru_RU sk_SK diff --git a/.env.example b/.env.example index bbde8fdc3d..e332ebabee 100644 --- a/.env.example +++ b/.env.example @@ -118,6 +118,7 @@ REDIS_CACHE_DB="1" COOKIE_PATH="/" COOKIE_DOMAIN= COOKIE_SECURE=false +COOKIE_SAMESITE=lax # If you want Firefly III to mail you, update these settings # For instructions, see: https://docs.firefly-iii.org/advanced-installation/email diff --git a/.gitignore b/.gitignore index 5bf61812fc..789f14aecd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /node_modules /frontend/node_modules /frontend/fonts +/frontend/images /public/hot /public/storage /storage/*.key diff --git a/app/Api/V1/Controllers/Autocomplete/AccountController.php b/app/Api/V1/Controllers/Autocomplete/AccountController.php index 9dfec18862..1121928794 100644 --- a/app/Api/V1/Controllers/Autocomplete/AccountController.php +++ b/app/Api/V1/Controllers/Autocomplete/AccountController.php @@ -90,7 +90,7 @@ class AccountController extends Controller } $return[] = [ - 'id' => $account->id, + 'id' => (string)$account->id, 'name' => $account->name, 'name_with_balance' => $nameWithBalance, 'type' => $account->accountType->type, diff --git a/app/Api/V1/Controllers/Autocomplete/BillController.php b/app/Api/V1/Controllers/Autocomplete/BillController.php index 890996dd2b..2038883f2e 100644 --- a/app/Api/V1/Controllers/Autocomplete/BillController.php +++ b/app/Api/V1/Controllers/Autocomplete/BillController.php @@ -69,7 +69,7 @@ class BillController extends Controller $filtered = $result->map( static function (Bill $item) { return [ - 'id' => $item->id, + 'id' => (string)$item->id, 'name' => $item->name, ]; } diff --git a/app/Api/V1/Controllers/Autocomplete/BudgetController.php b/app/Api/V1/Controllers/Autocomplete/BudgetController.php index 066a1b2c15..616989d2f2 100644 --- a/app/Api/V1/Controllers/Autocomplete/BudgetController.php +++ b/app/Api/V1/Controllers/Autocomplete/BudgetController.php @@ -69,7 +69,7 @@ class BudgetController extends Controller $filtered = $result->map( static function (Budget $item) { return [ - 'id' => $item->id, + 'id' => (string)$item->id, 'name' => $item->name, ]; } diff --git a/app/Api/V1/Controllers/Autocomplete/CategoryController.php b/app/Api/V1/Controllers/Autocomplete/CategoryController.php index 878defc648..ab05ebf504 100644 --- a/app/Api/V1/Controllers/Autocomplete/CategoryController.php +++ b/app/Api/V1/Controllers/Autocomplete/CategoryController.php @@ -68,7 +68,7 @@ class CategoryController extends Controller $filtered = $result->map( static function (Category $item) { return [ - 'id' => $item->id, + 'id' => (string)$item->id, 'name' => $item->name, ]; } diff --git a/app/Api/V1/Controllers/Autocomplete/CurrencyController.php b/app/Api/V1/Controllers/Autocomplete/CurrencyController.php index 5f17ca3bb2..fe6cc5648f 100644 --- a/app/Api/V1/Controllers/Autocomplete/CurrencyController.php +++ b/app/Api/V1/Controllers/Autocomplete/CurrencyController.php @@ -71,7 +71,7 @@ class CurrencyController extends Controller /** @var TransactionCurrency $currency */ foreach ($collection as $currency) { $result[] = [ - 'id' => $currency->id, + 'id' => (string) $currency->id, 'name' => sprintf('%s (%s)', $currency->name, $currency->code), 'code' => $currency->code, 'symbol' => $currency->symbol, @@ -96,7 +96,7 @@ class CurrencyController extends Controller /** @var TransactionCurrency $currency */ foreach ($collection as $currency) { $result[] = [ - 'id' => $currency->id, + 'id' => (string) $currency->id, 'name' => $currency->name, 'code' => $currency->code, 'symbol' => $currency->symbol, diff --git a/app/Api/V1/Controllers/Autocomplete/ObjectGroupController.php b/app/Api/V1/Controllers/Autocomplete/ObjectGroupController.php index 4ab1885c4f..bfa257dca4 100644 --- a/app/Api/V1/Controllers/Autocomplete/ObjectGroupController.php +++ b/app/Api/V1/Controllers/Autocomplete/ObjectGroupController.php @@ -71,7 +71,7 @@ class ObjectGroupController extends Controller /** @var ObjectGroup $account */ foreach ($result as $objectGroup) { $return[] = [ - 'id' => $objectGroup->id, + 'id' => (string)$objectGroup->id, 'name' => $objectGroup->title, 'title' => $objectGroup->title, ]; diff --git a/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php b/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php index fc2c682a91..c1499c9f25 100644 --- a/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php +++ b/app/Api/V1/Controllers/Autocomplete/PiggyBankController.php @@ -78,7 +78,7 @@ class PiggyBankController extends Controller foreach ($piggies as $piggy) { $currency = $this->accountRepository->getAccountCurrency($piggy->account) ?? $defaultCurrency; $response[] = [ - 'id' => $piggy->id, + 'id' => (string)$piggy->id, 'name' => $piggy->name, 'currency_id' => $currency->id, 'currency_name' => $currency->name, @@ -106,7 +106,7 @@ class PiggyBankController extends Controller $currency = $this->accountRepository->getAccountCurrency($piggy->account) ?? $defaultCurrency; $currentAmount = $this->piggyRepository->getRepetition($piggy)->currentamount ?? '0'; $response[] = [ - 'id' => $piggy->id, + 'id' => (string)$piggy->id, 'name' => $piggy->name, 'name_with_balance' => sprintf( '%s (%s / %s)', $piggy->name, app('amount')->formatAnything($currency, $currentAmount, false), diff --git a/app/Api/V1/Controllers/Autocomplete/RecurrenceController.php b/app/Api/V1/Controllers/Autocomplete/RecurrenceController.php index 75785eec08..01f2584a29 100644 --- a/app/Api/V1/Controllers/Autocomplete/RecurrenceController.php +++ b/app/Api/V1/Controllers/Autocomplete/RecurrenceController.php @@ -25,11 +25,56 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete; use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest; +use FireflyIII\Models\Rule; +use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; +use Illuminate\Http\JsonResponse; /** * Class RecurrenceController */ class RecurrenceController extends Controller { + private RecurringRepositoryInterface $repository; + + + /** + * RecurrenceController constructor. + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->repository = app(RecurringRepositoryInterface::class); + $this->repository->setUser(auth()->user()); + + return $next($request); + } + ); + } + + /** + * @param AutocompleteRequest $request + * + * @return JsonResponse + */ + public function recurring(AutocompleteRequest $request): JsonResponse + { + $data = $request->getData(); + $rules = $this->repository->searchRecurrence($data['query'], $data['limit']); + $response = []; + + /** @var Rule $rule */ + foreach ($rules as $rule) { + $response[] = [ + 'id' => (string)$rule->id, + 'name' => $rule->title, + 'description' => $rule->description, + ]; + } + + return response()->json($response); + } } diff --git a/app/Api/V1/Controllers/Autocomplete/RuleController.php b/app/Api/V1/Controllers/Autocomplete/RuleController.php index fd7453d05a..cbd9022ed2 100644 --- a/app/Api/V1/Controllers/Autocomplete/RuleController.php +++ b/app/Api/V1/Controllers/Autocomplete/RuleController.php @@ -25,11 +25,56 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete; use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest; +use FireflyIII\Models\Rule; +use FireflyIII\Repositories\Rule\RuleRepositoryInterface; +use Illuminate\Http\JsonResponse; /** * Class RuleController */ class RuleController extends Controller { + private RuleRepositoryInterface $repository; + + + /** + * RuleController constructor. + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->repository = app(RuleRepositoryInterface::class); + $this->repository->setUser(auth()->user()); + + return $next($request); + } + ); + } + + /** + * @param AutocompleteRequest $request + * + * @return JsonResponse + */ + public function rules(AutocompleteRequest $request): JsonResponse + { + $data = $request->getData(); + $rules = $this->repository->searchRule($data['query'], $data['limit']); + $response = []; + + /** @var Rule $rule */ + foreach ($rules as $rule) { + $response[] = [ + 'id' => (string)$rule->id, + 'name' => $rule->title, + 'description' => $rule->description, + ]; + } + + return response()->json($response); + } } diff --git a/app/Api/V1/Controllers/Autocomplete/RuleGroupController.php b/app/Api/V1/Controllers/Autocomplete/RuleGroupController.php index bbc598e40b..d67ae1c01c 100644 --- a/app/Api/V1/Controllers/Autocomplete/RuleGroupController.php +++ b/app/Api/V1/Controllers/Autocomplete/RuleGroupController.php @@ -25,11 +25,55 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete; use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest; +use FireflyIII\Models\RuleGroup; +use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; +use Illuminate\Http\JsonResponse; /** * Class RuleGroupController */ class RuleGroupController extends Controller { + private RuleGroupRepositoryInterface $repository; + + /** + * RuleGroupController constructor. + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->repository = app(RuleGroupRepositoryInterface::class); + $this->repository->setUser(auth()->user()); + + return $next($request); + } + ); + } + + /** + * @param AutocompleteRequest $request + * + * @return JsonResponse + */ + public function ruleGroups(AutocompleteRequest $request): JsonResponse + { + $data = $request->getData(); + $groups = $this->repository->searchRuleGroup($data['query'], $data['limit']); + $response = []; + + /** @var RuleGroup $group */ + foreach ($groups as $group) { + $response[] = [ + 'id' => (string)$group->id, + 'name' => $group->title, + 'description' => $group->description, + ]; + } + + return response()->json($response); + } } diff --git a/app/Api/V1/Controllers/Autocomplete/TagController.php b/app/Api/V1/Controllers/Autocomplete/TagController.php index 5b2aeabc67..ef26cd12fe 100644 --- a/app/Api/V1/Controllers/Autocomplete/TagController.php +++ b/app/Api/V1/Controllers/Autocomplete/TagController.php @@ -71,7 +71,7 @@ class TagController extends Controller /** @var Tag $tag */ foreach ($result as $tag) { $array[] = [ - 'id' => $tag->id, + 'id' => (string)$tag->id, 'name' => $tag->tag, 'tag' => $tag->tag, ]; diff --git a/app/Api/V1/Controllers/Autocomplete/TransactionController.php b/app/Api/V1/Controllers/Autocomplete/TransactionController.php index 7a0a9b480b..680e72bba4 100644 --- a/app/Api/V1/Controllers/Autocomplete/TransactionController.php +++ b/app/Api/V1/Controllers/Autocomplete/TransactionController.php @@ -81,8 +81,8 @@ class TransactionController extends Controller /** @var TransactionJournal $journal */ foreach ($filtered as $journal) { $array[] = [ - 'id' => $journal->id, - 'transaction_group_id' => $journal->transaction_group_id, + 'id' => (string)$journal->id, + 'transaction_group_id' => (string)$journal->transaction_group_id, 'name' => $journal->description, 'description' => $journal->description, ]; @@ -120,8 +120,8 @@ class TransactionController extends Controller /** @var TransactionJournal $journal */ foreach ($result as $journal) { $array[] = [ - 'id' => $journal->id, - 'transaction_group_id' => $journal->transaction_group_id, + 'id' => (string)$journal->id, + 'transaction_group_id' => (string)$journal->transaction_group_id, 'name' => sprintf('#%d: %s', $journal->transaction_group_id, $journal->description), 'description' => sprintf('#%d: %s', $journal->transaction_group_id, $journal->description), ]; diff --git a/app/Api/V1/Controllers/Autocomplete/TransactionTypeController.php b/app/Api/V1/Controllers/Autocomplete/TransactionTypeController.php index 5dcee6c308..38faca2036 100644 --- a/app/Api/V1/Controllers/Autocomplete/TransactionTypeController.php +++ b/app/Api/V1/Controllers/Autocomplete/TransactionTypeController.php @@ -69,7 +69,7 @@ class TransactionTypeController extends Controller foreach ($types as $type) { // different key for consistency. $array[] = [ - 'id' => $type->id, + 'id' =>(string) $type->id, 'name' => $type->type, 'type' => $type->type, ]; diff --git a/app/Api/V1/Controllers/BudgetLimitController.php b/app/Api/V1/Controllers/BudgetLimitController.php deleted file mode 100644 index a6dc66e3ff..0000000000 --- a/app/Api/V1/Controllers/BudgetLimitController.php +++ /dev/null @@ -1,259 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Api\V1\Controllers; - - -use FireflyIII\Api\V1\Requests\BudgetLimitStoreRequest; -use FireflyIII\Api\V1\Requests\BudgetLimitUpdateRequest; -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Helpers\Collector\GroupCollectorInterface; -use FireflyIII\Models\BudgetLimit; -use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; -use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; -use FireflyIII\Support\Http\Api\TransactionFilter; -use FireflyIII\Transformers\BudgetLimitTransformer; -use FireflyIII\Transformers\TransactionGroupTransformer; -use FireflyIII\User; -use Illuminate\Http\JsonResponse; -use Illuminate\Http\Request; -use Illuminate\Pagination\LengthAwarePaginator; -use Illuminate\Support\Collection; -use League\Fractal\Pagination\IlluminatePaginatorAdapter; -use League\Fractal\Resource\Collection as FractalCollection; -use League\Fractal\Resource\Item; - -/** - * Class BudgetLimitController. - */ -class BudgetLimitController extends Controller -{ - use TransactionFilter; - - private BudgetLimitRepositoryInterface $blRepository; - private BudgetRepositoryInterface $repository; - - - /** - * BudgetLimitController constructor. - * - * @codeCoverageIgnore - */ - public function __construct() - { - parent::__construct(); - $this->middleware( - function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); - $this->repository = app(BudgetRepositoryInterface::class); - $this->blRepository = app(BudgetLimitRepositoryInterface::class); - $this->repository->setUser($user); - $this->blRepository->setUser($user); - - return $next($request); - } - ); - } - - /** - * Remove the specified resource from storage. - * - * @param BudgetLimit $budgetLimit - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function delete(BudgetLimit $budgetLimit): JsonResponse - { - $this->blRepository->destroyBudgetLimit($budgetLimit); - - return response()->json([], 204); - } - - /** - * Display a listing of the resource. - * - * @param Request $request - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function index(Request $request): JsonResponse - { - $manager = $this->getManager(); - $manager->parseIncludes('budget'); - $budgetId = (int)($request->get('budget_id') ?? 0); - $budget = $this->repository->findNull($budgetId); - $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - $this->parameters->set('budget_id', $budgetId); - - $collection = new Collection; - if (null === $budget) { - $collection = $this->blRepository->getAllBudgetLimits($this->parameters->get('start'), $this->parameters->get('end')); - } - if (null !== $budget) { - $collection = $this->blRepository->getBudgetLimits($budget, $this->parameters->get('start'), $this->parameters->get('end')); - } - - $count = $collection->count(); - $budgetLimits = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); - $paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.budget_limits.index') . $this->buildParams()); - - /** @var BudgetLimitTransformer $transformer */ - $transformer = app(BudgetLimitTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($budgetLimits, $transformer, 'budget_limits'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Display the specified resource. - * - * @param BudgetLimit $budgetLimit - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function show(BudgetLimit $budgetLimit): JsonResponse - { - $manager = $this->getManager(); - - /** @var BudgetLimitTransformer $transformer */ - $transformer = app(BudgetLimitTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($budgetLimit, $transformer, 'budget_limits'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Store a newly created resource in storage. - * - * @param BudgetLimitStoreRequest $request - * - * @return JsonResponse - * @throws FireflyException - * - */ - public function store(BudgetLimitStoreRequest $request): JsonResponse - { - $data = $request->getAll(); - $data['start_date'] = $data['start']; - $data['end_date'] = $data['end']; - - $budgetLimit = $this->blRepository->store($data); - $manager = $this->getManager(); - - - /** @var BudgetLimitTransformer $transformer */ - $transformer = app(BudgetLimitTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($budgetLimit, $transformer, 'budget_limits'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Show all transactions. - * - * @param Request $request - * @param BudgetLimit $budgetLimit - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function transactions(Request $request, BudgetLimit $budgetLimit): JsonResponse - { - $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - $type = $request->get('type') ?? 'default'; - $this->parameters->set('type', $type); - - $types = $this->mapTransactionTypes($this->parameters->get('type')); - $manager = $this->getManager(); - - /** @var User $admin */ - $admin = auth()->user(); - - // use new group collector: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector - ->setUser($admin) - // filter on budget. - ->setBudget($budgetLimit->budget) - // all info needed for the API: - ->withAPIInformation() - // set page size: - ->setLimit($pageSize) - // set page to retrieve - ->setPage($this->parameters->get('page')) - // set types of transactions to return. - ->setTypes($types); - - $collector->setRange($budgetLimit->start_date, $budgetLimit->end_date); - $collector->setTypes($types); - $paginator = $collector->getPaginatedGroups(); - $paginator->setPath(route('api.v1.budget_limits.transactions', [$budgetLimit->id]) . $this->buildParams()); - $transactions = $paginator->getCollection(); - - /** @var TransactionGroupTransformer $transformer */ - $transformer = app(TransactionGroupTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($transactions, $transformer, 'transactions'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Update the specified resource in storage. - * - * @param BudgetLimitUpdateRequest $request - * @param BudgetLimit $budgetLimit - * - * @return JsonResponse - */ - public function update(BudgetLimitUpdateRequest $request, BudgetLimit $budgetLimit): JsonResponse - { - $data = $request->getAll(); - $budgetLimit = $this->blRepository->update($budgetLimit, $data); - $manager = $this->getManager(); - - /** @var BudgetLimitTransformer $transformer */ - $transformer = app(BudgetLimitTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($budgetLimit, $transformer, 'budget_limits'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } -} diff --git a/app/Api/V1/Controllers/Chart/AccountController.php b/app/Api/V1/Controllers/Chart/AccountController.php index f604fc7ee4..062748cfe5 100644 --- a/app/Api/V1/Controllers/Chart/AccountController.php +++ b/app/Api/V1/Controllers/Chart/AccountController.php @@ -26,10 +26,9 @@ namespace FireflyIII\Api\V1\Controllers\Chart; use Carbon\Carbon; use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Api\V1\Requests\DateRequest; +use FireflyIII\Api\V1\Requests\Data\DateRequest; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; -use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\Http\Api\ApiSupport; @@ -44,8 +43,7 @@ class AccountController extends Controller use ApiSupport; private CurrencyRepositoryInterface $currencyRepository; - - private AccountRepositoryInterface $repository; + private AccountRepositoryInterface $repository; /** @@ -71,92 +69,6 @@ class AccountController extends Controller ); } - /** - * @param DateRequest $request - * - * @return JsonResponse - */ - public function expenseOverview(DateRequest $request): JsonResponse - { - // parameters for chart: - $dates = $request->getAll(); - /** @var Carbon $start */ - $start = $dates['start']; - /** @var Carbon $end */ - $end = $dates['end']; - - $start->subDay(); - - // prep some vars: - $currencies = []; - $chartData = []; - $tempData = []; - - // grab all accounts and names - $accounts = $this->repository->getAccountsByType([AccountType::EXPENSE]); - $accountNames = $this->extractNames($accounts); - $startBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $start); - $endBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $end); - - // loop the end balances. This is an array for each account ($expenses) - foreach ($endBalances as $accountId => $expenses) { - $accountId = (int) $accountId; - // loop each expense entry (each entry can be a different currency). - foreach ($expenses as $currencyId => $endAmount) { - $currencyId = (int) $currencyId; - - // see if there is an accompanying start amount. - // grab the difference and find the currency. - $startAmount = $startBalances[$accountId][$currencyId] ?? '0'; - $diff = bcsub($endAmount, $startAmount); - $currencies[$currencyId] = $currencies[$currencyId] ?? $this->currencyRepository->findNull($currencyId); - if (0 !== bccomp($diff, '0')) { - // store the values in a temporary array. - $tempData[] = [ - 'name' => $accountNames[$accountId], - 'difference' => $diff, - 'diff_float' => (float) $diff, - 'currency_id' => $currencyId, - ]; - } - } - } - - // sort temp array by amount. - $amounts = array_column($tempData, 'diff_float'); - array_multisort($amounts, SORT_DESC, $tempData); - - // loop all found currencies and build the data array for the chart. - /** - * @var int $currencyId - * @var TransactionCurrency $currency - */ - foreach ($currencies as $currencyId => $currency) { - $currentSet = [ - 'label' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]), - 'currency_id' => $currency->id, - 'currency_code' => $currency->code, - 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => $currency->decimal_places, - 'type' => 'bar', // line, area or bar - 'yAxisID' => 0, // 0, 1, 2 - 'entries' => $this->expandNames($tempData), - ]; - $chartData[$currencyId] = $currentSet; - } - - // loop temp data and place data in correct array: - foreach ($tempData as $entry) { - $currencyId = $entry['currency_id']; - $name = $entry['name']; - $chartData[$currencyId]['entries'][$name] = round((float) $entry['difference'], $chartData[$currencyId]['currency_decimal_places']); - } - $chartData = array_values($chartData); - - return response()->json($chartData); - } - - /** * @param DateRequest $request * @@ -193,7 +105,7 @@ class AccountController extends Controller } $currentSet = [ 'label' => $account->name, - 'currency_id' => $currency->id, + 'currency_id' => (string)$currency->id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, 'currency_decimal_places' => $currency->decimal_places, @@ -206,11 +118,11 @@ class AccountController extends Controller /** @var Carbon $currentStart */ $currentStart = clone $start; $range = app('steam')->balanceInRange($account, $start, clone $end); - $previous = round((float) array_values($range)[0], 12); + $previous = round((float)array_values($range)[0], 12); while ($currentStart <= $end) { $format = $currentStart->format('Y-m-d'); $label = $currentStart->format('Y-m-d'); - $balance = array_key_exists($format, $range) ? round((float) $range[$format], 12) : $previous; + $balance = array_key_exists($format, $range) ? round((float)$range[$format], 12) : $previous; $previous = $balance; $currentStart->addDay(); $currentSet['entries'][$label] = $balance; @@ -220,91 +132,4 @@ class AccountController extends Controller return response()->json($chartData); } - - /** - * @param DateRequest $request - * - * @return JsonResponse - */ - public function revenueOverview(DateRequest $request): JsonResponse - { - // parameters for chart: - $dates = $request->getAll(); - /** @var Carbon $start */ - $start = $dates['start']; - /** @var Carbon $end */ - $end = $dates['end']; - - $start->subDay(); - - // prep some vars: - $currencies = []; - $chartData = []; - $tempData = []; - - // grab all accounts and names - $accounts = $this->repository->getAccountsByType([AccountType::REVENUE]); - $accountNames = $this->extractNames($accounts); - $startBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $start); - $endBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $end); - - // loop the end balances. This is an array for each account ($expenses) - foreach ($endBalances as $accountId => $expenses) { - $accountId = (int) $accountId; - // loop each expense entry (each entry can be a different currency). - foreach ($expenses as $currencyId => $endAmount) { - $currencyId = (int) $currencyId; - - // see if there is an accompanying start amount. - // grab the difference and find the currency. - $startAmount = $startBalances[$accountId][$currencyId] ?? '0'; - $diff = bcsub($endAmount, $startAmount); - $currencies[$currencyId] = $currencies[$currencyId] ?? $this->currencyRepository->findNull($currencyId); - if (0 !== bccomp($diff, '0')) { - // store the values in a temporary array. - $tempData[] = [ - 'name' => $accountNames[$accountId], - 'difference' => bcmul($diff, '-1'), - // For some reason this line is never covered in code coverage: - 'diff_float' => ((float) $diff) * -1, // @codeCoverageIgnore - 'currency_id' => $currencyId, - ]; - } - } - } - - // sort temp array by amount. - $amounts = array_column($tempData, 'diff_float'); - array_multisort($amounts, SORT_DESC, $tempData); - - // loop all found currencies and build the data array for the chart. - /** - * @var int $currencyId - * @var TransactionCurrency $currency - */ - foreach ($currencies as $currencyId => $currency) { - $currentSet = [ - 'label' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]), - 'currency_id' => $currency->id, - 'currency_code' => $currency->code, - 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => $currency->decimal_places, - 'type' => 'bar', // line, area or bar - 'yAxisID' => 0, // 0, 1, 2 - 'entries' => $this->expandNames($tempData), - ]; - $chartData[$currencyId] = $currentSet; - } - - // loop temp data and place data in correct array: - foreach ($tempData as $entry) { - $currencyId = $entry['currency_id']; - $name = $entry['name']; - $chartData[$currencyId]['entries'][$name] = round((float) $entry['difference'], $chartData[$currencyId]['currency_decimal_places']); - } - $chartData = array_values($chartData); - - return response()->json($chartData); - } - } diff --git a/app/Api/V1/Controllers/Chart/AvailableBudgetController.php b/app/Api/V1/Controllers/Chart/AvailableBudgetController.php deleted file mode 100644 index 0d9ba69a34..0000000000 --- a/app/Api/V1/Controllers/Chart/AvailableBudgetController.php +++ /dev/null @@ -1,115 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Api\V1\Controllers\Chart; - - -use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Models\AvailableBudget; -use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; -use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; -use FireflyIII\User; -use Illuminate\Http\JsonResponse; - -/** - * Class AvailableBudgetController - */ -class AvailableBudgetController extends Controller -{ - private OperationsRepositoryInterface $opsRepository; - private BudgetRepositoryInterface $repository; - - /** - * AvailableBudgetController constructor. - * - * @codeCoverageIgnore - */ - public function __construct() - { - parent::__construct(); - $this->middleware( - function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); - $this->repository = app(BudgetRepositoryInterface::class); - $this->opsRepository = app(OperationsRepositoryInterface::class); - $this->repository->setUser($user); - $this->opsRepository->setUser($user); - - return $next($request); - } - ); - } - - /** - * @param AvailableBudget $availableBudget - * - * @return JsonResponse - */ - public function overview(AvailableBudget $availableBudget): JsonResponse - { - $currency = $availableBudget->transactionCurrency; - $budgets = $this->repository->getActiveBudgets(); - $newBudgetInformation = $this->opsRepository->sumExpenses($availableBudget->start_date, $availableBudget->end_date, null, $budgets); - $spent = '0'; - - foreach ($newBudgetInformation as $currencyId => $info) { - if ($currencyId === (int) $availableBudget->transaction_currency_id) { - $spent = $info['sum']; - } - } - - $left = bcadd($availableBudget->amount, $spent); - // left less than zero? Set to zero. - if (-1 === bccomp($left, '0')) { - $left = '0'; - } - - $chartData = [ - [ - 'label' => trans('firefly.spent'), - 'currency_id' => $currency->id, - 'currency_code' => $currency->code, - 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => $currency->decimal_places, - 'type' => 'pie', - 'yAxisID' => 0, // 0, 1, 2 - 'entries' => [$spent * -1], - ], - [ - 'label' => trans('firefly.left'), - 'currency_id' => $currency->id, - 'currency_code' => $currency->code, - 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => $currency->decimal_places, - 'type' => 'line', // line, area or bar - 'yAxisID' => 0, // 0, 1, 2 - 'entries' => [round((float) $left, $currency->decimal_places)], - ], - ]; - - return response()->json($chartData); - } - -} diff --git a/app/Api/V1/Controllers/Chart/BudgetController.php b/app/Api/V1/Controllers/Chart/BudgetController.php deleted file mode 100644 index 4e153e0161..0000000000 --- a/app/Api/V1/Controllers/Chart/BudgetController.php +++ /dev/null @@ -1,297 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Api\V1\Controllers\Chart; - - -use Carbon\Carbon; -use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Api\V1\Requests\DateRequest; -use FireflyIII\Models\Budget; -use FireflyIII\Models\BudgetLimit; -use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; -use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; -use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; -use Illuminate\Http\JsonResponse; -use Illuminate\Support\Collection; - -/** - * Class BudgetController - */ -class BudgetController extends Controller -{ - private BudgetLimitRepositoryInterface $blRepository; - - private OperationsRepositoryInterface $opsRepository; - - private BudgetRepositoryInterface $repository; - - - /** - * BudgetController constructor. - * - * @codeCoverageIgnore - */ - public function __construct() - { - parent::__construct(); - - $this->middleware( - function ($request, $next) { - $this->repository = app(BudgetRepositoryInterface::class); - $this->opsRepository = app(OperationsRepositoryInterface::class); - $this->blRepository = app(BudgetLimitRepositoryInterface::class); - - return $next($request); - } - ); - } - - /** - * [ - * 'label' => 'label for entire set' - * 'currency_id' => 0, - * 'currency_code' => 'EUR', - * 'currency_symbol' => '$', - * 'currency_decimal_places' => 2, - * 'type' => 'bar', // line, area or bar - * 'yAxisID' => 0, // 0, 1, 2 - * 'entries' => ['a' => 1, 'b' => 4], - * ], - * - * @param DateRequest $request - * - * @return JsonResponse - */ - public function overview(DateRequest $request): JsonResponse - { - $dates = $request->getAll(); - $budgets = $this->repository->getActiveBudgets(); - $budgetNames = []; - $currencyNames = []; - $sets = []; - /** @var Budget $budget */ - foreach ($budgets as $budget) { - $expenses = $this->getExpenses($budget, $dates['start'], $dates['end']); - $expenses = $this->filterNulls($expenses); - foreach ($expenses as $set) { - $budgetNames[] = $set['budget_name']; - $currencyNames[] = $set['currency_name']; - $sets[] = $set; - } - } - $budgetNames = array_unique($budgetNames); - $currencyNames = array_unique($currencyNames); - $basic = $this->createSets($budgetNames, $currencyNames); - $filled = $this->fillSets($basic, $sets); - $keys = array_values($filled); - - return response()->json($keys); - } - - /** - * @param Collection $limits - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - protected function getExpenses(Budget $budget, Carbon $start, Carbon $end): array - { - $limits = $this->blRepository->getBudgetLimits($budget, $start, $end); - if (0 === $limits->count()) { - return $this->getExpenseInRange($budget, $start, $end); - } - $arr = []; - /** @var BudgetLimit $limit */ - foreach ($limits as $limit) { - $arr[] = $this->getExpensesForLimit($limit); - } - - return $arr; - } - - /** - * @param Budget $budget - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - private function getExpenseInRange(Budget $budget, Carbon $start, Carbon $end): array - { - $spent = $this->opsRepository->sumExpenses($start, $end, null, new Collection([$budget]), null); - $return = []; - /** @var array $set */ - foreach ($spent as $set) { - $current = [ - 'label' => sprintf('%s (%s)', $budget->name, $set['currency_name']), - 'budget_name' => $budget->name, - 'start_date' => $start->format('Y-m-d'), - 'end_date' => $end->format('Y-m-d'), - 'currency_id' => (int) $set['currency_id'], - 'currency_code' => $set['currency_code'], - 'currency_name' => $set['currency_name'], - 'currency_symbol' => $set['currency_symbol'], - 'currency_decimal_places' => (int) $set['currency_decimal_places'], - 'type' => 'bar', // line, area or bar, - 'entries' => [], - ]; - $sumSpent = bcmul($set['sum'], '-1'); // spent - $current['entries']['spent'] = $sumSpent; - $current['entries']['amount'] = '0'; - $current['entries']['spent_capped'] = $sumSpent; - $current['entries']['left'] = '0'; - $current['entries']['overspent'] = '0'; - $return[] = $current; - } - - return $return; - } - - /** - * @param BudgetLimit $limit - * - * @return array - */ - private function getExpensesForLimit(BudgetLimit $limit): array - { - $budget = $limit->budget; - $spent = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection([$budget]), $limit->transactionCurrency); - $currency = $limit->transactionCurrency; - // when limited to a currency, the count is always one. Or it's empty. - $set = array_shift($spent); - if (null === $set) { - return []; - } - $return = [ - 'label' => sprintf('%s (%s)', $budget->name, $set['currency_name']), - 'budget_name' => $budget->name, - 'start_date' => $limit->start_date->format('Y-m-d'), - 'end_date' => $limit->end_date->format('Y-m-d'), - 'currency_id' => (int) $currency->id, - 'currency_code' => $currency->code, - 'currency_name' => $currency->name, - 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => (int) $currency->decimal_places, - 'type' => 'bar', // line, area or bar, - 'entries' => [], - ]; - $sumSpent = bcmul($set['sum'], '-1'); // spent - $return['entries']['spent'] = $sumSpent; - $return['entries']['amount'] = $limit->amount; - $return['entries']['spent_capped'] = 1 === bccomp($sumSpent, $limit->amount) ? $limit->amount : $sumSpent; - $return['entries']['left'] = 1 === bccomp($limit->amount, $sumSpent) ? bcadd($set['sum'], $limit->amount) : '0'; // left - $return['entries']['overspent'] = 1 === bccomp($limit->amount, $sumSpent) ? '0' : bcmul(bcadd($set['sum'], $limit->amount), '-1'); // overspent - - return $return; - } - - /** - * @param array $expenses - * - * @return array - */ - private function filterNulls(array $expenses): array - { - $return = []; - /** @var array|null $arr */ - foreach ($expenses as $arr) { - if ([] !== $arr) { - $return[] = $arr; - } - } - - return $return; - } - - /** - * @param array $budgetNames - * @param array $currencyNames - * - * @return array - */ - private function createSets(array $budgetNames, array $currencyNames): array - { - $return = []; - foreach ($currencyNames as $currencyName) { - $entries = []; - foreach ($budgetNames as $budgetName) { - $label = sprintf('%s (%s)', $budgetName, $currencyName); - $entries[$label] = '0'; - } - - // left - $return['left'] = [ - 'label' => sprintf('%s (%s)', trans('firefly.left'), $currencyName), - 'data_type' => 'left', - 'currency_name' => $currencyName, - 'type' => 'bar', - 'yAxisID' => 0, // 0, 1, 2 - 'entries' => $entries, - ]; - - // spent_capped - $return['spent_capped'] = [ - 'label' => sprintf('%s (%s)', trans('firefly.spent'), $currencyName), - 'data_type' => 'spent_capped', - 'currency_name' => $currencyName, - 'type' => 'bar', - 'yAxisID' => 0, // 0, 1, 2 - 'entries' => $entries, - ]; - - // overspent - $return['overspent'] = [ - 'label' => sprintf('%s (%s)', trans('firefly.overspent'), $currencyName), - 'data_type' => 'overspent', - 'currency_name' => $currencyName, - 'type' => 'bar', - 'yAxisID' => 0, // 0, 1, 2 - 'entries' => $entries, - ]; - - } - - return $return; - } - - /** - * @param array $basic - * @param array $sets - * - * @return array - */ - private function fillSets(array $basic, array $sets): array - { - foreach ($sets as $set) { - $label = $set['label']; - $basic['spent_capped']['entries'][$label] = $set['entries']['spent_capped']; - $basic['left']['entries'][$label] = $set['entries']['left']; - $basic['overspent']['entries'][$label] = $set['entries']['overspent']; - } - - return $basic; - } - -} diff --git a/app/Api/V1/Controllers/Chart/CategoryController.php b/app/Api/V1/Controllers/Chart/CategoryController.php deleted file mode 100644 index ec4fa83ad4..0000000000 --- a/app/Api/V1/Controllers/Chart/CategoryController.php +++ /dev/null @@ -1,157 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Api\V1\Controllers\Chart; - -use Carbon\Carbon; -use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Api\V1\Requests\DateRequest; -use FireflyIII\Repositories\Category\CategoryRepositoryInterface; -use FireflyIII\Repositories\Category\NoCategoryRepositoryInterface; -use FireflyIII\Repositories\Category\OperationsRepositoryInterface; -use FireflyIII\User; -use Illuminate\Http\JsonResponse; - -/** - * Class CategoryController - */ -class CategoryController extends Controller -{ - private CategoryRepositoryInterface $categoryRepository; - private NoCategoryRepositoryInterface $noCatRepository; - private OperationsRepositoryInterface $opsRepository; - private array $categories; - - /** - * AccountController constructor. - * - * @codeCoverageIgnore - */ - public function __construct() - { - parent::__construct(); - $this->middleware( - function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); - $this->categoryRepository = app(CategoryRepositoryInterface::class); - $this->opsRepository = app(OperationsRepositoryInterface::class); - $this->noCatRepository = app(NoCategoryRepositoryInterface::class); - $this->categories = []; - $this->categoryRepository->setUser($user); - $this->opsRepository->setUser($user); - $this->noCatRepository->setUser($user); - - return $next($request); - } - ); - } - - - /** - * @param DateRequest $request - * - * @return JsonResponse - */ - public function overview(DateRequest $request): JsonResponse - { - // parameters for chart: - $dates = $request->getAll(); - /** @var Carbon $start */ - $start = $dates['start']; - /** @var Carbon $end */ - $end = $dates['end']; - - $tempData = []; - $spentWith = $this->opsRepository->listExpenses($start, $end); - $spentWithout = $this->noCatRepository->listExpenses($start, $end); - - /** @var array $set */ - foreach ([$spentWith, $spentWithout,] as $set) { - $tempData = $this->processArray($tempData, $set); - } - - $chartData = $this->sortArray($tempData); - - return response()->json($chartData); - } - - /** - * @param array $tempData - * @param array $set - * - * @return array - */ - private function processArray(array $tempData, array $set): array - { - foreach ($set as $currency) { - foreach ($currency['categories'] as $category) { - $this->categories[] = $category['name']; - $outKey = sprintf('%d-e', $currency['currency_id']); - $tempData[$outKey] = $tempData[$outKey] ?? [ - 'currency_id' => $currency['currency_id'], - 'label' => (string)trans('firefly.box_spent_in_currency', ['currency' => $currency['currency_name']]), - 'currency_code' => $currency['currency_code'], - 'currency_symbol' => $currency['currency_symbol'], - 'currency_decimal_places' => $currency['currency_decimal_places'], - 'type' => 'bar', // line, area or bar - 'yAxisID' => 0, // 0, 1, 2 - 'entries' => [], - ]; - - foreach ($category['transaction_journals'] as $journal) { - // is it expense or income? - $currentKey = sprintf('%d-%s', $currency['currency_id'], 'e'); - $name = $category['name']; - $tempData[$currentKey]['entries'][$name] = $tempData[$currentKey]['entries'][$name] ?? '0'; - $tempData[$currentKey]['entries'][$name] = bcadd($tempData[$currentKey]['entries'][$name], $journal['amount']); - } - } - } - - return $tempData; - } - - /** - * @param array $tempData - * - * @return array - */ - private function sortArray(array $tempData): array - { - // re-sort every spent array and add 0 for missing entries. - foreach ($tempData as $index => $set) { - $oldSet = $set['entries']; - $newSet = []; - foreach ($this->categories as $category) { - $value = $oldSet[$category] ?? '0'; - $value = -1 === bccomp($value, '0') ? bcmul($value, '-1') : $value; - $newSet[$category] = $value; - } - $tempData[$index]['entries'] = $newSet; - } - - return array_values($tempData); - } -} diff --git a/app/Api/V1/Controllers/ConfigurationController.php b/app/Api/V1/Controllers/ConfigurationController.php deleted file mode 100644 index efc79a3615..0000000000 --- a/app/Api/V1/Controllers/ConfigurationController.php +++ /dev/null @@ -1,117 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Api\V1\Controllers; - -use FireflyIII\Api\V1\Requests\ConfigurationRequest; -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Models\Configuration; -use FireflyIII\Repositories\User\UserRepositoryInterface; -use FireflyIII\User; -use Illuminate\Http\JsonResponse; - -/** - * Class ConfigurationController. - * - * @codeCoverageIgnore - */ -class ConfigurationController extends Controller -{ - /** @var UserRepositoryInterface The user repository */ - private $repository; - - - /** - * ConfigurationController constructor. - */ - public function __construct() - { - parent::__construct(); - $this->middleware( - function ($request, $next) { - $this->repository = app(UserRepositoryInterface::class); - /** @var User $admin */ - $admin = auth()->user(); - - if (!$this->repository->hasRole($admin, 'owner')) { - throw new FireflyException('200005: You need the "owner" role to do this.'); // @codeCoverageIgnore - } - - return $next($request); - } - ); - } - - /** - * Show all configuration. - * - * @return JsonResponse - */ - public function index(): JsonResponse - { - $configData = $this->getConfigData(); - - return response()->json(['data' => $configData])->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Get all config values. - * - * @return array - */ - private function getConfigData(): array - { - /** @var Configuration $isDemoSite */ - $isDemoSite = app('fireflyconfig')->get('is_demo_site'); - /** @var Configuration $updateCheck */ - $updateCheck = app('fireflyconfig')->get('permission_update_check'); - /** @var Configuration $lastCheck */ - $lastCheck = app('fireflyconfig')->get('last_update_check'); - /** @var Configuration $singleUser */ - $singleUser = app('fireflyconfig')->get('single_user_mode'); - - return [ - 'is_demo_site' => null === $isDemoSite ? null : $isDemoSite->data, - 'permission_update_check' => null === $updateCheck ? null : (int) $updateCheck->data, - 'last_update_check' => null === $lastCheck ? null : (int) $lastCheck->data, - 'single_user_mode' => null === $singleUser ? null : $singleUser->data, - ]; - } - - /** - * Update the configuration. - * - * @param ConfigurationRequest $request - * @param string $name - * - * @return JsonResponse - */ - public function update(ConfigurationRequest $request, string $name): JsonResponse - { - $data = $request->getAll(); - app('fireflyconfig')->set($name, $data['value']); - $configData = $this->getConfigData(); - - return response()->json(['data' => $configData])->header('Content-Type', self::CONTENT_TYPE); - } -} diff --git a/app/Api/V1/Controllers/Data/DestroyController.php b/app/Api/V1/Controllers/Data/DestroyController.php index 830cda1dc4..df724abe71 100644 --- a/app/Api/V1/Controllers/Data/DestroyController.php +++ b/app/Api/V1/Controllers/Data/DestroyController.php @@ -1,7 +1,7 @@ getObjects(); diff --git a/app/Api/V1/Controllers/Data/Export/ExportController.php b/app/Api/V1/Controllers/Data/Export/ExportController.php new file mode 100644 index 0000000000..7f4aa32dbf --- /dev/null +++ b/app/Api/V1/Controllers/Data/Export/ExportController.php @@ -0,0 +1,209 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Data\Export; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Data\Export\ExportRequest; +use FireflyIII\Support\Export\ExportDataGenerator; +use FireflyIII\User; +use Illuminate\Http\Response as LaravelResponse; + +/** + * Class ExportController + */ +class ExportController extends Controller +{ + private ExportDataGenerator $exporter; + + /** + * ExportController constructor. + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + /** @var ExportDataGenerator $exporter */ + $this->exporter = app(ExportDataGenerator::class); + $this->exporter->setUser($user); + + return $next($request); + } + ); + } + + + /** + * @param ExportRequest $request + * + * @return LaravelResponse + * @throws \League\Csv\CannotInsertRecord + */ + public function accounts(ExportRequest $request): LaravelResponse + { + $this->exporter->setExportAccounts(true); + + return $this->returnExport('accounts'); + + } + + + /** + * @param ExportRequest $request + * + * @return LaravelResponse + * @throws \League\Csv\CannotInsertRecord + */ + public function bills(ExportRequest $request): LaravelResponse + { + $this->exporter->setExportBills(true); + + return $this->returnExport('bills'); + } + + /** + * @param ExportRequest $request + * + * @return LaravelResponse + * @throws \League\Csv\CannotInsertRecord + */ + public function budgets(ExportRequest $request): LaravelResponse + { + $this->exporter->setExportBudgets(true); + + return $this->returnExport('budgets'); + } + + /** + * @param ExportRequest $request + * + * @return LaravelResponse + * @throws \League\Csv\CannotInsertRecord + */ + public function categories(ExportRequest $request): LaravelResponse + { + $this->exporter->setExportCategories(true); + + return $this->returnExport('categories'); + } + + /** + * @param ExportRequest $request + * + * @return LaravelResponse + * @throws \League\Csv\CannotInsertRecord + */ + public function piggyBanks(ExportRequest $request): LaravelResponse + { + $this->exporter->setExportPiggies(true); + + return $this->returnExport('piggies'); + } + + /** + * @param ExportRequest $request + * + * @return LaravelResponse + * @throws \League\Csv\CannotInsertRecord + */ + public function recurring(ExportRequest $request): LaravelResponse + { + $this->exporter->setExportRecurring(true); + + return $this->returnExport('recurrences'); + } + + /** + * @param ExportRequest $request + * + * @return LaravelResponse + * @throws \League\Csv\CannotInsertRecord + */ + public function rules(ExportRequest $request): LaravelResponse + { + $this->exporter->setExportRules(true); + + return $this->returnExport('rules'); + } + + /** + * @param ExportRequest $request + * + * @return LaravelResponse + * @throws \League\Csv\CannotInsertRecord + */ + public function tags(ExportRequest $request): LaravelResponse + { + $this->exporter->setExportTags(true); + + return $this->returnExport('tags'); + } + + /** + * @param ExportRequest $request + * + * @return LaravelResponse + * @throws \League\Csv\CannotInsertRecord + */ + public function transactions(ExportRequest $request): LaravelResponse + { + $params = $request->getAll(); + $this->exporter->setStart($params['start']); + $this->exporter->setEnd($params['end']); + $this->exporter->setAccounts($params['accounts']); + $this->exporter->setExportTransactions(true); + + return $this->returnExport('transactions'); + } + + /** + * @param string $key + * + * @return LaravelResponse + * @throws \League\Csv\CannotInsertRecord + */ + private function returnExport(string $key): LaravelResponse + { + $date = date('Y-m-d-H-i-s'); + $fileName = sprintf('%s-export-%s.csv', $date, $key); + $data = $this->exporter->export(); + + /** @var LaravelResponse $response */ + $response = response($data[$key]); + $response + ->header('Content-Description', 'File Transfer') + ->header('Content-Type', 'application/octet-stream') + ->header('Content-Disposition', 'attachment; filename=' . $fileName) + ->header('Content-Transfer-Encoding', 'binary') + ->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($data[$key])); + + return $response; + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Insight/Expense/AccountController.php b/app/Api/V1/Controllers/Insight/Expense/AccountController.php new file mode 100644 index 0000000000..b41f203da6 --- /dev/null +++ b/app/Api/V1/Controllers/Insight/Expense/AccountController.php @@ -0,0 +1,128 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Controllers\Insight\Expense; + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Insight\GenericRequest; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Account\OperationsRepositoryInterface; +use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Support\Http\Api\ApiSupport; +use Illuminate\Http\JsonResponse; + +/** + * + * Class AccountController + * + * Shows expense information grouped or limited by date. + * Ie. all expenses grouped by account + currency. + */ +class AccountController extends Controller +{ + use ApiSupport; + + private CurrencyRepositoryInterface $currencyRepository; + private AccountRepositoryInterface $repository; + private OperationsRepositoryInterface $opsRepository; + + /** + * AccountController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $user = auth()->user(); + $this->repository = app(AccountRepositoryInterface::class); + $this->repository->setUser($user); + + $this->currencyRepository = app(CurrencyRepositoryInterface::class); + $this->currencyRepository->setUser($user); + + $this->opsRepository = app(OperationsRepositoryInterface::class); + $this->opsRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function expense(GenericRequest $request): JsonResponse + { + $start = $request->getStart(); + $end = $request->getEnd(); + $assetAccounts = $request->getAssetAccounts(); + $expenseAccounts = $request->getExpenseAccounts(); + $expenses = $this->opsRepository->sumExpenses($start, $end, $assetAccounts, $expenseAccounts); + $result = []; + + /** @var array $expense */ + foreach ($expenses as $expense) { + $result[] = [ + 'difference' => $expense['sum'], + 'difference_float' => (float)$expense['sum'], + 'currency_id' => (string)$expense['currency_id'], + 'currency_code' => $expense['currency_code'], + ]; + } + + return response()->json($result); + } + + /** + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function asset(GenericRequest $request): JsonResponse + { + $start = $request->getStart(); + $end = $request->getEnd(); + $assetAccounts = $request->getAssetAccounts(); + $expenses = $this->opsRepository->sumExpenses($start, $end, $assetAccounts); + $result = []; + + /** @var array $expense */ + foreach ($expenses as $expense) { + $result[] = [ + 'difference' => $expense['sum'], + 'difference_float' => (float)$expense['sum'], + 'currency_id' => (string)$expense['currency_id'], + 'currency_code' => $expense['currency_code'], + ]; + } + + return response()->json($result); + } + +} diff --git a/app/Api/V1/Controllers/Insight/Expense/BillController.php b/app/Api/V1/Controllers/Insight/Expense/BillController.php new file mode 100644 index 0000000000..8cc53cc329 --- /dev/null +++ b/app/Api/V1/Controllers/Insight/Expense/BillController.php @@ -0,0 +1,166 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Insight\Expense; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Insight\GenericRequest; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; +use FireflyIII\Models\TransactionType; +use FireflyIII\Repositories\Bill\BillRepositoryInterface; +use Illuminate\Http\JsonResponse; + +/** + * Class BillController + */ +class BillController extends Controller +{ + private BillRepositoryInterface $repository; + + /** + * BillController constructor. + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $user = auth()->user(); + $this->repository = app(BillRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Expenses per bill, possibly filtered by bill and account. + * + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function bill(GenericRequest $request): JsonResponse + { + $accounts = $request->getAssetAccounts(); + $bills = $request->getBills(); + $start = $request->getStart(); + $end = $request->getEnd(); + $response = []; + + // get all bills: + if (0 === $bills->count()) { + $bills = $this->repository->getBills(); + } + + // collect all expenses in this period (regardless of type) by the given bills and accounts. + $collector = app(GroupCollectorInterface::class); + $collector->setTypes([TransactionType::WITHDRAWAL])->setRange($start, $end)->setSourceAccounts($accounts); + $collector->setBills($bills); + + $genericSet = $collector->getExtractedJournals(); + foreach ($genericSet as $journal) { + $billId = (int)$journal['bill_id']; + $currencyId = (int)$journal['currency_id']; + $foreignCurrencyId = (int)$journal['foreign_currency_id']; + $key = sprintf('%d-%d', $billId, $currencyId); + $foreignKey = sprintf('%d-%d', $billId, $foreignCurrencyId); + + if (0 !== $currencyId) { + $response[$key] = $response[$key] ?? [ + 'id' => (string)$billId, + 'name' => $journal['bill_name'], + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$currencyId, + 'currency_code' => $journal['currency_code'], + ]; + $response[$key]['difference'] = bcadd($response[$key]['difference'], $journal['amount']); + $response[$key]['difference_float'] = (float)$response[$key]['difference']; + } + if (0 !== $foreignCurrencyId) { + $response[$foreignKey] = $response[$foreignKey] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$foreignCurrencyId, + 'currency_code' => $journal['foreign_currency_code'], + ]; + $response[$foreignKey]['difference'] = bcadd($response[$foreignKey]['difference'], $journal['foreign_amount']); + $response[$foreignKey]['difference_float'] = (float)$response[$foreignKey]['difference']; + } + } + + return response()->json(array_values($response)); + } + + /** + * Expenses for no bill filtered by account. + * + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function noBill(GenericRequest $request): JsonResponse + { + $accounts = $request->getAssetAccounts(); + $start = $request->getStart(); + $end = $request->getEnd(); + $response = []; + + // collect all expenses in this period (regardless of type) by the given bills and accounts. + $collector = app(GroupCollectorInterface::class); + $collector->setTypes([TransactionType::WITHDRAWAL])->setRange($start, $end)->setSourceAccounts($accounts); + $collector->withoutBill(); + + $genericSet = $collector->getExtractedJournals(); + + foreach ($genericSet as $journal) { + $currencyId = (int)$journal['currency_id']; + $foreignCurrencyId = (int)$journal['foreign_currency_id']; + + if (0 !== $currencyId) { + $response[$currencyId] = $response[$currencyId] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$currencyId, + 'currency_code' => $journal['currency_code'], + ]; + $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], $journal['amount']); + $response[$currencyId]['difference_float'] = (float)$response[$currencyId]['difference']; + } + if (0 !== $foreignCurrencyId) { + $response[$foreignCurrencyId] = $response[$foreignCurrencyId] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$foreignCurrencyId, + 'currency_code' => $journal['foreign_currency_code'], + ]; + $response[$foreignCurrencyId]['difference'] = bcadd($response[$foreignCurrencyId]['difference'], $journal['foreign_amount']); + $response[$foreignCurrencyId]['difference_float'] = (float)$response[$foreignCurrencyId]['difference']; + } + } + + return response()->json(array_values($response)); + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Insight/Expense/BudgetController.php b/app/Api/V1/Controllers/Insight/Expense/BudgetController.php new file mode 100644 index 0000000000..69deaa6e84 --- /dev/null +++ b/app/Api/V1/Controllers/Insight/Expense/BudgetController.php @@ -0,0 +1,126 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Insight\Expense; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Insight\GenericRequest; +use FireflyIII\Models\Budget; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Repositories\Budget\NoBudgetRepositoryInterface; +use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; +use Illuminate\Http\JsonResponse; +use Illuminate\Support\Collection; + +/** + * Class BudgetController + */ +class BudgetController extends Controller +{ + private OperationsRepositoryInterface $opsRepository; + private BudgetRepositoryInterface $repository; + private NoBudgetRepositoryInterface $noRepository; + + /** + * AccountController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->opsRepository = app(OperationsRepositoryInterface::class); + $this->repository = app(BudgetRepositoryInterface::class); + $this->noRepository = app(NoBudgetRepositoryInterface::class); + $user = auth()->user(); + $this->opsRepository->setUser($user); + $this->repository->setUser($user); + $this->noRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function budget(GenericRequest $request): JsonResponse + { + $start = $request->getStart(); + $end = $request->getEnd(); + $budgets = $request->getBudgets(); + $assetAccounts = $request->getAssetAccounts(); + $result = []; + if (0 === $budgets->count()) { + $budgets = $this->repository->getActiveBudgets(); + } + /** @var Budget $budget */ + foreach ($budgets as $budget) { + $expenses = $this->opsRepository->sumExpenses($start, $end, $assetAccounts, new Collection([$budget]), null); + /** @var array $expense */ + foreach ($expenses as $expense) { + $result[] = [ + 'id' => (string)$budget->id, + 'name' => $budget->name, + 'difference' => $expense['sum'], + 'difference_float' => (float)$expense['sum'], + 'currency_id' => (string)$expense['currency_id'], + 'currency_code' => $expense['currency_code'], + ]; + } + } + + return response()->json($result); + } + + /** + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function noBudget(GenericRequest $request): JsonResponse + { + $start = $request->getStart(); + $end = $request->getEnd(); + $assetAccounts = $request->getAssetAccounts(); + $result = []; + $expenses = $this->noRepository->sumExpenses($start, $end, $assetAccounts, null); + /** @var array $expense */ + foreach ($expenses as $expense) { + $result[] = [ + 'difference' => $expense['sum'], + 'difference_float' => (float)$expense['sum'], + 'currency_id' => (string)$expense['currency_id'], + 'currency_code' => $expense['currency_code'], + ]; + } + + return response()->json($result); + + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Insight/Expense/CategoryController.php b/app/Api/V1/Controllers/Insight/Expense/CategoryController.php new file mode 100644 index 0000000000..7d2b5de843 --- /dev/null +++ b/app/Api/V1/Controllers/Insight/Expense/CategoryController.php @@ -0,0 +1,107 @@ +middleware( + function ($request, $next) { + $this->opsRepository = app(OperationsRepositoryInterface::class); + $this->repository = app(CategoryRepositoryInterface::class); + $this->noRepository = app(NoCategoryRepositoryInterface::class); + $user = auth()->user(); + $this->opsRepository->setUser($user); + $this->repository->setUser($user); + $this->noRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function category(GenericRequest $request): JsonResponse + { + $start = $request->getStart(); + $end = $request->getEnd(); + $categories = $request->getCategories(); + $assetAccounts = $request->getAssetAccounts(); + $result = []; + if (0 === $categories->count()) { + $categories = $this->repository->getCategories(); + } + /** @var Category $category */ + foreach ($categories as $category) { + $expenses = $this->opsRepository->sumExpenses($start, $end, $assetAccounts, new Collection([$category])); + /** @var array $expense */ + foreach ($expenses as $expense) { + $result[] = [ + 'id' => (string)$category->id, + 'name' => $category->name, + 'difference' => $expense['sum'], + 'difference_float' => (float)$expense['sum'], + 'currency_id' => (string)$expense['currency_id'], + 'currency_code' => $expense['currency_code'], + ]; + } + } + + return response()->json($result); + } + + /** + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function noCategory(GenericRequest $request): JsonResponse + { + $start = $request->getStart(); + $end = $request->getEnd(); + $assetAccounts = $request->getAssetAccounts(); + $result = []; + $expenses = $this->noRepository->sumExpenses($start, $end, $assetAccounts); + /** @var array $expense */ + foreach ($expenses as $expense) { + $result[] = [ + 'difference' => $expense['sum'], + 'difference_float' => (float)$expense['sum'], + 'currency_id' => (string)$expense['currency_id'], + 'currency_code' => $expense['currency_code'], + ]; + } + + return response()->json($result); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Insight/Expense/DateController.php b/app/Api/V1/Controllers/Insight/Expense/DateController.php deleted file mode 100644 index ee47dd7b14..0000000000 --- a/app/Api/V1/Controllers/Insight/Expense/DateController.php +++ /dev/null @@ -1,131 +0,0 @@ -. - */ - -namespace FireflyIII\Api\V1\Controllers\Insight\Expense; - -use Carbon\Carbon; -use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Api\V1\Requests\DateRequest; -use FireflyIII\Models\AccountType; -use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; -use FireflyIII\Support\Http\Api\ApiSupport; -use FireflyIII\User; -use Illuminate\Http\JsonResponse; - -/** - * Class DateController - * - * Shows expense information grouped or limited by date. - * Ie. all expenses grouped by account + currency. - */ -class DateController extends Controller -{ - use ApiSupport; - - private CurrencyRepositoryInterface $currencyRepository; - private AccountRepositoryInterface $repository; - - - /** - * AccountController constructor. - * - * @codeCoverageIgnore - */ - public function __construct() - { - parent::__construct(); - $this->middleware( - function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); - $this->repository = app(AccountRepositoryInterface::class); - $this->repository->setUser($user); - - $this->currencyRepository = app(CurrencyRepositoryInterface::class); - $this->currencyRepository->setUser($user); - - return $next($request); - } - ); - } - - /** - * - */ - public function basic(DateRequest $request): JsonResponse - { - // parameters for chart: - $dates = $request->getAll(); - /** @var Carbon $start */ - $start = $dates['start']; - /** @var Carbon $end */ - $end = $dates['end']; - - $start->subDay(); - - // prep some vars: - $currencies = []; - $chartData = []; - $tempData = []; - - // grab all accounts and names - $accounts = $this->repository->getAccountsByType([AccountType::EXPENSE]); - $accountNames = $this->extractNames($accounts); - $startBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $start); - $endBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $end); - - // loop the end balances. This is an array for each account ($expenses) - foreach ($endBalances as $accountId => $expenses) { - $accountId = (int)$accountId; - // loop each expense entry (each entry can be a different currency). - foreach ($expenses as $currencyId => $endAmount) { - $currencyId = (int)$currencyId; - - // see if there is an accompanying start amount. - // grab the difference and find the currency. - $startAmount = $startBalances[$accountId][$currencyId] ?? '0'; - $diff = bcsub($endAmount, $startAmount); - $currencies[$currencyId] = $currencies[$currencyId] ?? $this->currencyRepository->findNull($currencyId); - if (0 !== bccomp($diff, '0')) { - // store the values in a temporary array. - $tempData[] = [ - 'id' => $accountId, - 'name' => $accountNames[$accountId], - 'difference' => bcmul($diff, '-1'), - 'difference_float' => ((float)$diff) * -1, - 'currency_id' => $currencyId, - 'currency_code' => $currencies[$currencyId]->code, - ]; - } - } - } - - - // sort temp array by amount. - $amounts = array_column($tempData, 'difference_float'); - array_multisort($amounts, SORT_ASC, $tempData); - - return response()->json($tempData); - } - -} diff --git a/app/Api/V1/Controllers/Insight/Expense/PeriodController.php b/app/Api/V1/Controllers/Insight/Expense/PeriodController.php new file mode 100644 index 0000000000..0fd6da9c3a --- /dev/null +++ b/app/Api/V1/Controllers/Insight/Expense/PeriodController.php @@ -0,0 +1,82 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Insight\Expense; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Insight\GenericRequest; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; +use FireflyIII\Models\TransactionType; +use Illuminate\Http\JsonResponse; + +/** + * Class PeriodController + */ +class PeriodController extends Controller +{ + + /** + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function total(GenericRequest $request): JsonResponse + { + $accounts = $request->getAssetAccounts(); + $start = $request->getStart(); + $end = $request->getEnd(); + $response = []; + + // collect all expenses in this period (regardless of type) + $collector = app(GroupCollectorInterface::class); + $collector->setTypes([TransactionType::WITHDRAWAL])->setRange($start, $end)->setSourceAccounts($accounts); + $genericSet = $collector->getExtractedJournals(); + foreach ($genericSet as $journal) { + $currencyId = (int)$journal['currency_id']; + $foreignCurrencyId = (int)$journal['foreign_currency_id']; + + if (0 !== $currencyId) { + $response[$currencyId] = $response[$currencyId] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$currencyId, + 'currency_code' => $journal['currency_code'], + ]; + $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], $journal['amount']); + $response[$currencyId]['difference_float'] = (float)$response[$currencyId]['difference']; + } + if (0 !== $foreignCurrencyId) { + $response[$foreignCurrencyId] = $response[$foreignCurrencyId] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$foreignCurrencyId, + 'currency_code' => $journal['foreign_currency_code'], + ]; + $response[$foreignCurrencyId]['difference'] = bcadd($response[$foreignCurrencyId]['difference'], $journal['foreign_amount']); + $response[$foreignCurrencyId]['difference_float'] = (float)$response[$foreignCurrencyId]['difference']; + } + } + + return response()->json(array_values($response)); + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Insight/Expense/TagController.php b/app/Api/V1/Controllers/Insight/Expense/TagController.php new file mode 100644 index 0000000000..98a84457e4 --- /dev/null +++ b/app/Api/V1/Controllers/Insight/Expense/TagController.php @@ -0,0 +1,173 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Insight\Expense; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Insight\GenericRequest; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; +use FireflyIII\Models\TransactionType; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; +use Illuminate\Http\JsonResponse; + +/** + * Class TagController + */ +class TagController extends Controller +{ + private TagRepositoryInterface $repository; + + /** + * TagController constructor. + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $user = auth()->user(); + $this->repository = app(TagRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Expenses per tag, possibly filtered by tag and account. + * + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function tag(GenericRequest $request): JsonResponse + { + $accounts = $request->getAssetAccounts(); + $tags = $request->getTags(); + $start = $request->getStart(); + $end = $request->getEnd(); + $response = []; + + // get all tags: + if (0 === $tags->count()) { + $tags = $this->repository->get(); + } + + // collect all expenses in this period (regardless of type) by the given bills and accounts. + $collector = app(GroupCollectorInterface::class); + $collector->setTypes([TransactionType::WITHDRAWAL])->setRange($start, $end)->setSourceAccounts($accounts); + $collector->setTags($tags); + $genericSet = $collector->getExtractedJournals(); + /** @var array $entry */ + foreach ($genericSet as $journal) { + $currencyId = (int)$journal['currency_id']; + $foreignCurrencyId = (int)$journal['foreign_currency_id']; + + /** @var array $tag */ + foreach ($journal['tags'] as $tag) { + $tagId = $tag['id']; + $key = sprintf('%d-%d', $tagId, $currencyId); + $foreignKey = sprintf('%d-%d', $tagId, $foreignCurrencyId); + + // on currency ID + if (0 !== $currencyId) { + $response[$key] = $response[$key] ?? [ + 'id' => (string)$tagId, + 'name' => $tag['name'], + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$currencyId, + 'currency_code' => $journal['currency_code'], + ]; + $response[$key]['difference'] = bcadd($response[$key]['difference'], $journal['amount']); + $response[$key]['difference_float'] = (float)$response[$key]['difference']; + } + + // on foreign ID + if (0 !== $foreignCurrencyId) { + $response[$foreignKey] = $journal[$foreignKey] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$foreignCurrencyId, + 'currency_code' => $journal['foreign_currency_code'], + ]; + $response[$foreignKey]['difference'] = bcadd($response[$foreignKey]['difference'], $journal['foreign_amount']); + $response[$foreignKey]['difference_float'] = (float)$response[$foreignKey]['difference']; + } + } + } + + return response()->json(array_values($response)); + } + + /** + * Expenses for no tag filtered by account. + * + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function noTag(GenericRequest $request): JsonResponse + { + $accounts = $request->getAssetAccounts(); + $start = $request->getStart(); + $end = $request->getEnd(); + $response = []; + + // collect all expenses in this period (regardless of type) by the given bills and accounts. + $collector = app(GroupCollectorInterface::class); + $collector->setTypes([TransactionType::WITHDRAWAL])->setRange($start, $end)->setSourceAccounts($accounts); + $collector->withoutTags(); + + $genericSet = $collector->getExtractedJournals(); + + foreach ($genericSet as $journal) { + $currencyId = (int)$journal['currency_id']; + $foreignCurrencyId = (int)$journal['foreign_currency_id']; + + if (0 !== $currencyId) { + $response[$currencyId] = $response[$currencyId] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$currencyId, + 'currency_code' => $journal['currency_code'], + ]; + $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], $journal['amount']); + $response[$currencyId]['difference_float'] = (float)$response[$currencyId]['difference']; + } + if (0 !== $foreignCurrencyId) { + $response[$foreignCurrencyId] = $response[$foreignCurrencyId] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$foreignCurrencyId, + 'currency_code' => $journal['foreign_currency_code'], + ]; + $response[$foreignCurrencyId]['difference'] = bcadd($response[$foreignCurrencyId]['difference'], $journal['foreign_amount']); + $response[$foreignCurrencyId]['difference_float'] = (float)$response[$foreignCurrencyId]['difference']; + } + } + + return response()->json(array_values($response)); + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Insight/Income/AccountController.php b/app/Api/V1/Controllers/Insight/Income/AccountController.php new file mode 100644 index 0000000000..8fc49cee7c --- /dev/null +++ b/app/Api/V1/Controllers/Insight/Income/AccountController.php @@ -0,0 +1,131 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Controllers\Insight\Income; + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Insight\GenericRequest; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Account\OperationsRepositoryInterface; +use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Support\Http\Api\ApiSupport; +use Illuminate\Http\JsonResponse; + +/** + * + * Class AccountController + * + * Shows income information grouped or limited by date. + * Ie. all income grouped by account + currency. + * TODO same code as Expense/AccountController. + */ +class AccountController extends Controller +{ + use ApiSupport; + + private CurrencyRepositoryInterface $currencyRepository; + private AccountRepositoryInterface $repository; + private OperationsRepositoryInterface $opsRepository; + + /** + * AccountController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $user = auth()->user(); + $this->repository = app(AccountRepositoryInterface::class); + $this->repository->setUser($user); + + $this->currencyRepository = app(CurrencyRepositoryInterface::class); + $this->currencyRepository->setUser($user); + + $this->opsRepository = app(OperationsRepositoryInterface::class); + $this->opsRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * // TOOD same as + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function revenue(GenericRequest $request): JsonResponse + { + $start = $request->getStart(); + $end = $request->getEnd(); + $assetAccounts = $request->getAssetAccounts(); + $revenueAccounts = $request->getRevenueAccounts(); + $income = $this->opsRepository->sumIncome($start, $end, $assetAccounts, $revenueAccounts); + $result = []; + + /** @var array $entry */ + foreach ($income as $entry) { + $result[] = [ + 'difference' => $entry['sum'], + 'difference_float' => (float)$entry['sum'], + 'currency_id' => (string)$entry['currency_id'], + 'currency_code' => $entry['currency_code'], + ]; + } + + return response()->json($result); + } + + /** + * TODO same code as Expense/AccountController. + * + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function asset(GenericRequest $request): JsonResponse + { + $start = $request->getStart(); + $end = $request->getEnd(); + $assetAccounts = $request->getAssetAccounts(); + $income = $this->opsRepository->sumIncome($start, $end, $assetAccounts); + $result = []; + /** @var array $entry */ + foreach ($income as $entry) { + $result[] = [ + 'difference' => $entry['sum'], + 'difference_float' => (float)$entry['sum'], + 'currency_id' => (string)$entry['currency_id'], + 'currency_code' => $entry['currency_code'], + ]; + } + + return response()->json($result); + } + +} diff --git a/app/Api/V1/Controllers/Insight/Income/CategoryController.php b/app/Api/V1/Controllers/Insight/Income/CategoryController.php new file mode 100644 index 0000000000..c368bd5b9a --- /dev/null +++ b/app/Api/V1/Controllers/Insight/Income/CategoryController.php @@ -0,0 +1,108 @@ +middleware( + function ($request, $next) { + $this->opsRepository = app(OperationsRepositoryInterface::class); + $this->repository = app(CategoryRepositoryInterface::class); + $this->noRepository = app(NoCategoryRepositoryInterface::class); + $user = auth()->user(); + $this->opsRepository->setUser($user); + $this->repository->setUser($user); + $this->noRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function category(GenericRequest $request): JsonResponse + { + $start = $request->getStart(); + $end = $request->getEnd(); + $categories = $request->getCategories(); + $assetAccounts = $request->getAssetAccounts(); + $result = []; + if (0 === $categories->count()) { + $categories = $this->repository->getCategories(); + } + /** @var Category $category */ + foreach ($categories as $category) { + $expenses = $this->opsRepository->sumIncome($start, $end, $assetAccounts, new Collection([$category])); + /** @var array $expense */ + foreach ($expenses as $expense) { + $result[] = [ + 'id' => (string)$category->id, + 'name' => $category->name, + 'difference' => $expense['sum'], + 'difference_float' => (float)$expense['sum'], + 'currency_id' => (string)$expense['currency_id'], + 'currency_code' => $expense['currency_code'], + ]; + } + } + + return response()->json($result); + } + + /** + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function noCategory(GenericRequest $request): JsonResponse + { + $start = $request->getStart(); + $end = $request->getEnd(); + $assetAccounts = $request->getAssetAccounts(); + $result = []; + $expenses = $this->noRepository->sumIncome($start, $end, $assetAccounts); + /** @var array $expense */ + foreach ($expenses as $expense) { + $result[] = [ + 'difference' => $expense['sum'], + 'difference_float' => (float)$expense['sum'], + 'currency_id' => (string)$expense['currency_id'], + 'currency_code' => $expense['currency_code'], + ]; + } + + return response()->json($result); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Insight/Income/DateController.php b/app/Api/V1/Controllers/Insight/Income/DateController.php deleted file mode 100644 index 58cad852d2..0000000000 --- a/app/Api/V1/Controllers/Insight/Income/DateController.php +++ /dev/null @@ -1,131 +0,0 @@ -. - */ - -namespace FireflyIII\Api\V1\Controllers\Insight\Income; - -use Carbon\Carbon; -use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Api\V1\Requests\DateRequest; -use FireflyIII\Models\AccountType; -use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; -use FireflyIII\Support\Http\Api\ApiSupport; -use FireflyIII\User; -use Illuminate\Http\JsonResponse; - -/** - * Class DateController - * - * Shows income information grouped or limited by date. - * Ie. all income grouped by revenue + currency. - */ -class DateController extends Controller -{ - use ApiSupport; - - private CurrencyRepositoryInterface $currencyRepository; - private AccountRepositoryInterface $repository; - - - /** - * AccountController constructor. - * - * @codeCoverageIgnore - */ - public function __construct() - { - parent::__construct(); - $this->middleware( - function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); - $this->repository = app(AccountRepositoryInterface::class); - $this->repository->setUser($user); - - $this->currencyRepository = app(CurrencyRepositoryInterface::class); - $this->currencyRepository->setUser($user); - - return $next($request); - } - ); - } - - /** - * - */ - public function basic(DateRequest $request): JsonResponse - { - // parameters for chart: - $dates = $request->getAll(); - /** @var Carbon $start */ - $start = $dates['start']; - /** @var Carbon $end */ - $end = $dates['end']; - - $start->subDay(); - - // prep some vars: - $currencies = []; - $chartData = []; - $tempData = []; - - // grab all accounts and names - $accounts = $this->repository->getAccountsByType([AccountType::REVENUE]); - $accountNames = $this->extractNames($accounts); - $startBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $start); - $endBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $end); - - // loop the end balances. This is an array for each account ($expenses) - foreach ($endBalances as $accountId => $expenses) { - $accountId = (int)$accountId; - // loop each expense entry (each entry can be a different currency). - foreach ($expenses as $currencyId => $endAmount) { - $currencyId = (int)$currencyId; - - // see if there is an accompanying start amount. - // grab the difference and find the currency. - $startAmount = $startBalances[$accountId][$currencyId] ?? '0'; - $diff = bcsub($endAmount, $startAmount); - $currencies[$currencyId] = $currencies[$currencyId] ?? $this->currencyRepository->findNull($currencyId); - if (0 !== bccomp($diff, '0')) { - // store the values in a temporary array. - $tempData[] = [ - 'id' => $accountId, - 'name' => $accountNames[$accountId], - 'difference' => $diff, - 'difference_float' => (float)$diff, - 'currency_id' => $currencyId, - 'currency_code' => $currencies[$currencyId]->code, - ]; - } - } - } - - - // sort temp array by amount. - $amounts = array_column($tempData, 'difference_float'); - array_multisort($amounts, SORT_ASC, $tempData); - - return response()->json($tempData); - } - -} diff --git a/app/Api/V1/Controllers/Insight/Income/PeriodController.php b/app/Api/V1/Controllers/Insight/Income/PeriodController.php new file mode 100644 index 0000000000..bf43a4fba5 --- /dev/null +++ b/app/Api/V1/Controllers/Insight/Income/PeriodController.php @@ -0,0 +1,82 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Insight\Income; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Insight\GenericRequest; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; +use FireflyIII\Models\TransactionType; +use Illuminate\Http\JsonResponse; + +/** + * Class PeriodController + */ +class PeriodController extends Controller +{ + + /** + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function total(GenericRequest $request): JsonResponse + { + $accounts = $request->getAssetAccounts(); + $start = $request->getStart(); + $end = $request->getEnd(); + $response = []; + + // collect all expenses in this period (regardless of type) + $collector = app(GroupCollectorInterface::class); + $collector->setTypes([TransactionType::DEPOSIT])->setRange($start, $end)->setDestinationAccounts($accounts); + $genericSet = $collector->getExtractedJournals(); + foreach ($genericSet as $journal) { + $currencyId = (int)$journal['currency_id']; + $foreignCurrencyId = (int)$journal['foreign_currency_id']; + + if (0 !== $currencyId) { + $response[$currencyId] = $response[$currencyId] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$currencyId, + 'currency_code' => $journal['currency_code'], + ]; + $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], app('steam')->positive($journal['amount'])); + $response[$currencyId]['difference_float'] = (float)$response[$currencyId]['difference']; + } + if (0 !== $foreignCurrencyId) { + $response[$foreignCurrencyId] = $response[$foreignCurrencyId] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$foreignCurrencyId, + 'currency_code' => $journal['foreign_currency_code'], + ]; + $response[$foreignCurrencyId]['difference'] = bcadd($response[$foreignCurrencyId]['difference'], app('steam')->positive($journal['foreign_amount'])); + $response[$foreignCurrencyId]['difference_float'] = (float)$response[$foreignCurrencyId]['difference']; + } + } + + return response()->json(array_values($response)); + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Insight/Income/TagController.php b/app/Api/V1/Controllers/Insight/Income/TagController.php new file mode 100644 index 0000000000..eca5d01b4f --- /dev/null +++ b/app/Api/V1/Controllers/Insight/Income/TagController.php @@ -0,0 +1,173 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Insight\Income; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Insight\GenericRequest; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; +use FireflyIII\Models\TransactionType; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; +use Illuminate\Http\JsonResponse; + +/** + * Class TagController + */ +class TagController extends Controller +{ + private TagRepositoryInterface $repository; + + /** + * TagController constructor. + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $user = auth()->user(); + $this->repository = app(TagRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Expenses per tag, possibly filtered by tag and account. + * + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function tag(GenericRequest $request): JsonResponse + { + $accounts = $request->getAssetAccounts(); + $tags = $request->getTags(); + $start = $request->getStart(); + $end = $request->getEnd(); + $response = []; + + // get all tags: + if (0 === $tags->count()) { + $tags = $this->repository->get(); + } + + // collect all expenses in this period (regardless of type) by the given bills and accounts. + $collector = app(GroupCollectorInterface::class); + $collector->setTypes([TransactionType::DEPOSIT])->setRange($start, $end)->setDestinationAccounts($accounts); + $collector->setTags($tags); + $genericSet = $collector->getExtractedJournals(); + /** @var array $entry */ + foreach ($genericSet as $journal) { + $currencyId = (int)$journal['currency_id']; + $foreignCurrencyId = (int)$journal['foreign_currency_id']; + + /** @var array $tag */ + foreach ($journal['tags'] as $tag) { + $tagId = $tag['id']; + $key = sprintf('%d-%d', $tagId, $currencyId); + $foreignKey = sprintf('%d-%d', $tagId, $foreignCurrencyId); + + // on currency ID + if (0 !== $currencyId) { + $response[$key] = $response[$key] ?? [ + 'id' => (string)$tagId, + 'name' => $tag['name'], + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$currencyId, + 'currency_code' => $journal['currency_code'], + ]; + $response[$key]['difference'] = bcadd($response[$key]['difference'], app('steam')->positive($journal['amount'])); + $response[$key]['difference_float'] = (float)$response[$key]['difference']; + } + + // on foreign ID + if (0 !== $foreignCurrencyId) { + $response[$foreignKey] = $journal[$foreignKey] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$foreignCurrencyId, + 'currency_code' => $journal['foreign_currency_code'], + ]; + $response[$foreignKey]['difference'] = bcadd($response[$foreignKey]['difference'], app('steam')->positive($journal['foreign_amount'])); + $response[$foreignKey]['difference_float'] = (float)$response[$foreignKey]['difference']; + } + } + } + + return response()->json(array_values($response)); + } + + /** + * Expenses for no tag filtered by account. + * + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function noTag(GenericRequest $request): JsonResponse + { + $accounts = $request->getAssetAccounts(); + $start = $request->getStart(); + $end = $request->getEnd(); + $response = []; + + // collect all expenses in this period (regardless of type) by the given bills and accounts. + $collector = app(GroupCollectorInterface::class); + $collector->setTypes([TransactionType::DEPOSIT])->setRange($start, $end)->setDestinationAccounts($accounts); + $collector->withoutTags(); + + $genericSet = $collector->getExtractedJournals(); + + foreach ($genericSet as $journal) { + $currencyId = (int)$journal['currency_id']; + $foreignCurrencyId = (int)$journal['foreign_currency_id']; + + if (0 !== $currencyId) { + $response[$currencyId] = $response[$currencyId] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$currencyId, + 'currency_code' => $journal['currency_code'], + ]; + $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], app('steam')->positive($journal['amount'])); + $response[$currencyId]['difference_float'] = (float)$response[$currencyId]['difference']; + } + if (0 !== $foreignCurrencyId) { + $response[$foreignCurrencyId] = $response[$foreignCurrencyId] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$foreignCurrencyId, + 'currency_code' => $journal['foreign_currency_code'], + ]; + $response[$foreignCurrencyId]['difference'] = bcadd($response[$foreignCurrencyId]['difference'], app('steam')->positive($journal['foreign_amount'])); + $response[$foreignCurrencyId]['difference_float'] = (float)$response[$foreignCurrencyId]['difference']; + } + } + + return response()->json(array_values($response)); + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Insight/Transfer/AccountController.php b/app/Api/V1/Controllers/Insight/Transfer/AccountController.php new file mode 100644 index 0000000000..ad113a6325 --- /dev/null +++ b/app/Api/V1/Controllers/Insight/Transfer/AccountController.php @@ -0,0 +1,85 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Insight\Transfer; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Insight\GenericRequest; +use FireflyIII\Repositories\Account\OperationsRepositoryInterface; +use FireflyIII\Support\Http\Api\ApiSupport; +use Illuminate\Http\JsonResponse; + +/** + * Class AccountController + */ +class AccountController extends Controller +{ + use ApiSupport; + + private OperationsRepositoryInterface $opsRepository; + + /** + * AccountController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $user = auth()->user(); + $this->opsRepository = app(OperationsRepositoryInterface::class); + $this->opsRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * TODO same code as Expense/AccountController. + * + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function asset(GenericRequest $request): JsonResponse + { + $start = $request->getStart(); + $end = $request->getEnd(); + $assetAccounts = $request->getAssetAccounts(); + $income = $this->opsRepository->sumTransfers($start, $end, $assetAccounts); + $result = []; + /** @var array $entry */ + foreach ($income as $entry) { + $result[] = [ + 'difference' => $entry['sum'], + 'difference_float' => (float)$entry['sum'], + 'currency_id' => (string)$entry['currency_id'], + 'currency_code' => $entry['currency_code'], + ]; + } + + return response()->json($result); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Insight/Transfer/CategoryController.php b/app/Api/V1/Controllers/Insight/Transfer/CategoryController.php new file mode 100644 index 0000000000..1f13918a0f --- /dev/null +++ b/app/Api/V1/Controllers/Insight/Transfer/CategoryController.php @@ -0,0 +1,125 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Insight\Transfer; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Insight\GenericRequest; +use FireflyIII\Models\Category; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Repositories\Category\NoCategoryRepositoryInterface; +use FireflyIII\Repositories\Category\OperationsRepositoryInterface; +use Illuminate\Http\JsonResponse; +use Illuminate\Support\Collection; + +/** + * Class CategoryController + */ +class CategoryController extends Controller +{ + private OperationsRepositoryInterface $opsRepository; + private CategoryRepositoryInterface $repository; + private NoCategoryRepositoryInterface $noRepository; + + /** + * AccountController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->opsRepository = app(OperationsRepositoryInterface::class); + $this->repository = app(CategoryRepositoryInterface::class); + $this->noRepository = app(NoCategoryRepositoryInterface::class); + $user = auth()->user(); + $this->opsRepository->setUser($user); + $this->repository->setUser($user); + $this->noRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function category(GenericRequest $request): JsonResponse + { + $start = $request->getStart(); + $end = $request->getEnd(); + $categories = $request->getCategories(); + $assetAccounts = $request->getAssetAccounts(); + $result = []; + if (0 === $categories->count()) { + $categories = $this->repository->getCategories(); + } + /** @var Category $category */ + foreach ($categories as $category) { + $expenses = $this->opsRepository->sumTransfers($start, $end, $assetAccounts, new Collection([$category])); + /** @var array $expense */ + foreach ($expenses as $expense) { + $result[] = [ + 'id' => (string)$category->id, + 'name' => $category->name, + 'difference' => $expense['sum'], + 'difference_float' => (float)$expense['sum'], + 'currency_id' => (string)$expense['currency_id'], + 'currency_code' => $expense['currency_code'], + ]; + } + } + + return response()->json($result); + } + + /** + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function noCategory(GenericRequest $request): JsonResponse + { + $start = $request->getStart(); + $end = $request->getEnd(); + $assetAccounts = $request->getAssetAccounts(); + $result = []; + $expenses = $this->noRepository->sumTransfers($start, $end, $assetAccounts); + /** @var array $expense */ + foreach ($expenses as $expense) { + $result[] = [ + 'difference' => $expense['sum'], + 'difference_float' => (float)$expense['sum'], + 'currency_id' => (string)$expense['currency_id'], + 'currency_code' => $expense['currency_code'], + ]; + } + + return response()->json($result); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php b/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php new file mode 100644 index 0000000000..b03788c0d3 --- /dev/null +++ b/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php @@ -0,0 +1,82 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Insight\Transfer; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Insight\GenericRequest; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; +use FireflyIII\Models\TransactionType; +use Illuminate\Http\JsonResponse; + +/** + * Class PeriodController + */ +class PeriodController extends Controller +{ + + /** + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function total(GenericRequest $request): JsonResponse + { + $accounts = $request->getAssetAccounts(); + $start = $request->getStart(); + $end = $request->getEnd(); + $response = []; + + // collect all expenses in this period (regardless of type) + $collector = app(GroupCollectorInterface::class); + $collector->setTypes([TransactionType::TRANSFER])->setRange($start, $end)->setDestinationAccounts($accounts); + $genericSet = $collector->getExtractedJournals(); + foreach ($genericSet as $journal) { + $currencyId = (int)$journal['currency_id']; + $foreignCurrencyId = (int)$journal['foreign_currency_id']; + + if (0 !== $currencyId) { + $response[$currencyId] = $response[$currencyId] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$currencyId, + 'currency_code' => $journal['currency_code'], + ]; + $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], app('steam')->positive($journal['amount'])); + $response[$currencyId]['difference_float'] = (float)$response[$currencyId]['difference']; + } + if (0 !== $foreignCurrencyId) { + $response[$foreignCurrencyId] = $response[$foreignCurrencyId] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$foreignCurrencyId, + 'currency_code' => $journal['foreign_currency_code'], + ]; + $response[$foreignCurrencyId]['difference'] = bcadd($response[$foreignCurrencyId]['difference'], app('steam')->positive($journal['foreign_amount'])); + $response[$foreignCurrencyId]['difference_float'] = (float)$response[$foreignCurrencyId]['difference']; + } + } + + return response()->json(array_values($response)); + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Insight/Transfer/TagController.php b/app/Api/V1/Controllers/Insight/Transfer/TagController.php new file mode 100644 index 0000000000..5001cf21bb --- /dev/null +++ b/app/Api/V1/Controllers/Insight/Transfer/TagController.php @@ -0,0 +1,173 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Insight\Transfer; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Insight\GenericRequest; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; +use FireflyIII\Models\TransactionType; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; +use Illuminate\Http\JsonResponse; + +/** + * Class TagController + */ +class TagController extends Controller +{ + private TagRepositoryInterface $repository; + + /** + * TagController constructor. + * TODO lots of copying and pasting here. + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $user = auth()->user(); + $this->repository = app(TagRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Transfers per tag, possibly filtered by tag and account. + * + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function tag(GenericRequest $request): JsonResponse + { + $accounts = $request->getAssetAccounts(); + $tags = $request->getTags(); + $start = $request->getStart(); + $end = $request->getEnd(); + $response = []; + + // get all tags: + if (0 === $tags->count()) { + $tags = $this->repository->get(); + } + + // collect all expenses in this period (regardless of type) by the given bills and accounts. + $collector = app(GroupCollectorInterface::class); + $collector->setTypes([TransactionType::TRANSFER])->setRange($start, $end)->setDestinationAccounts($accounts); + $collector->setTags($tags); + $genericSet = $collector->getExtractedJournals(); + /** @var array $entry */ + foreach ($genericSet as $journal) { + $currencyId = (int)$journal['currency_id']; + $foreignCurrencyId = (int)$journal['foreign_currency_id']; + + /** @var array $tag */ + foreach ($journal['tags'] as $tag) { + $tagId = $tag['id']; + $key = sprintf('%d-%d', $tagId, $currencyId); + $foreignKey = sprintf('%d-%d', $tagId, $foreignCurrencyId); + + // on currency ID + if (0 !== $currencyId) { + $response[$key] = $response[$key] ?? [ + 'id' => (string)$tagId, + 'name' => $tag['name'], + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$currencyId, + 'currency_code' => $journal['currency_code'], + ]; + $response[$key]['difference'] = bcadd($response[$key]['difference'], app('steam')->positive($journal['amount'])); + $response[$key]['difference_float'] = (float)$response[$key]['difference']; + } + + // on foreign ID + if (0 !== $foreignCurrencyId) { + $response[$foreignKey] = $journal[$foreignKey] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$foreignCurrencyId, + 'currency_code' => $journal['foreign_currency_code'], + ]; + $response[$foreignKey]['difference'] = bcadd($response[$foreignKey]['difference'], app('steam')->positive($journal['foreign_amount'])); + $response[$foreignKey]['difference_float'] = (float)$response[$foreignKey]['difference']; + } + } + } + + return response()->json(array_values($response)); + } + + /** + * Expenses for no tag filtered by account. + * + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function noTag(GenericRequest $request): JsonResponse + { + $accounts = $request->getAssetAccounts(); + $start = $request->getStart(); + $end = $request->getEnd(); + $response = []; + + // collect all expenses in this period (regardless of type) by the given bills and accounts. + $collector = app(GroupCollectorInterface::class); + $collector->setTypes([TransactionType::TRANSFER])->setRange($start, $end)->setDestinationAccounts($accounts); + $collector->withoutTags(); + + $genericSet = $collector->getExtractedJournals(); + + foreach ($genericSet as $journal) { + $currencyId = (int)$journal['currency_id']; + $foreignCurrencyId = (int)$journal['foreign_currency_id']; + + if (0 !== $currencyId) { + $response[$currencyId] = $response[$currencyId] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$currencyId, + 'currency_code' => $journal['currency_code'], + ]; + $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], app('steam')->positive($journal['amount'])); + $response[$currencyId]['difference_float'] = (float)$response[$currencyId]['difference']; + } + if (0 !== $foreignCurrencyId) { + $response[$foreignCurrencyId] = $response[$foreignCurrencyId] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$foreignCurrencyId, + 'currency_code' => $journal['foreign_currency_code'], + ]; + $response[$foreignCurrencyId]['difference'] = bcadd($response[$foreignCurrencyId]['difference'], app('steam')->positive($journal['foreign_amount'])); + $response[$foreignCurrencyId]['difference_float'] = (float)$response[$foreignCurrencyId]['difference']; + } + } + + return response()->json(array_values($response)); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/LinkTypeController.php b/app/Api/V1/Controllers/LinkTypeController.php deleted file mode 100644 index 6f4942c6d3..0000000000 --- a/app/Api/V1/Controllers/LinkTypeController.php +++ /dev/null @@ -1,274 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Api\V1\Controllers; - -use FireflyIII\Api\V1\Requests\LinkTypeStoreRequest; -use FireflyIII\Api\V1\Requests\LinkTypeUpdateRequest; -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Helpers\Collector\GroupCollectorInterface; -use FireflyIII\Models\LinkType; -use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; -use FireflyIII\Repositories\User\UserRepositoryInterface; -use FireflyIII\Support\Http\Api\TransactionFilter; -use FireflyIII\Transformers\LinkTypeTransformer; -use FireflyIII\Transformers\TransactionGroupTransformer; -use FireflyIII\User; -use Illuminate\Http\JsonResponse; -use Illuminate\Http\Request; -use Illuminate\Pagination\LengthAwarePaginator; -use League\Fractal\Pagination\IlluminatePaginatorAdapter; -use League\Fractal\Resource\Collection as FractalCollection; -use League\Fractal\Resource\Item; - -/** - * Class LinkTypeController. - */ -class LinkTypeController extends Controller -{ - use TransactionFilter; - - /** @var LinkTypeRepositoryInterface The link type repository */ - private $repository; - - /** @var UserRepositoryInterface The user repository */ - private $userRepository; - - - /** - * LinkTypeController constructor. - * - * @codeCoverageIgnore - */ - public function __construct() - { - parent::__construct(); - $this->middleware( - function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); - $this->repository = app(LinkTypeRepositoryInterface::class); - $this->userRepository = app(UserRepositoryInterface::class); - $this->repository->setUser($user); - - return $next($request); - } - ); - } - - /** - * Delete the resource. - * - * @param LinkType $linkType - * - * @return JsonResponse - * @throws FireflyException - * @codeCoverageIgnore - */ - public function delete(LinkType $linkType): JsonResponse - { - if (false === $linkType->editable) { - throw new FireflyException('200020: Link type cannot be changed.'); - } - $this->repository->destroy($linkType); - - return response()->json([], 204); - } - - /** - * List all of them. - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function index(): JsonResponse - { - // create some objects: - $manager = $this->getManager(); - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - - // get list of accounts. Count it and split it. - $collection = $this->repository->get(); - $count = $collection->count(); - $linkTypes = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); - - // make paginator: - $paginator = new LengthAwarePaginator($linkTypes, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.link_types.index') . $this->buildParams()); - - /** @var LinkTypeTransformer $transformer */ - $transformer = app(LinkTypeTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($linkTypes, $transformer, 'link_types'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - - /** - * List single resource. - * - * @param LinkType $linkType - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function show(LinkType $linkType): JsonResponse - { - $manager = $this->getManager(); - /** @var LinkTypeTransformer $transformer */ - $transformer = app(LinkTypeTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($linkType, $transformer, 'link_types'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - - /** - * Store new object. - * - * @param LinkTypeStoreRequest $request - * - * @return JsonResponse - * @throws FireflyException - */ - public function store(LinkTypeStoreRequest $request): JsonResponse - { - /** @var User $admin */ - $admin = auth()->user(); - - if (!$this->userRepository->hasRole($admin, 'owner')) { - throw new FireflyException('200005: You need the "owner" role to do this.'); // @codeCoverageIgnore - } - $data = $request->getAll(); - // if currency ID is 0, find the currency by the code: - $linkType = $this->repository->store($data); - $manager = $this->getManager(); - - /** @var LinkTypeTransformer $transformer */ - $transformer = app(LinkTypeTransformer::class); - $transformer->setParameters($this->parameters); - $resource = new Item($linkType, $transformer, 'link_types'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - - /** - * Delete the resource. - * - * @param Request $request - * @param LinkType $linkType - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function transactions(Request $request, LinkType $linkType): JsonResponse - { - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - $type = $request->get('type') ?? 'default'; - $this->parameters->set('type', $type); - - $types = $this->mapTransactionTypes($this->parameters->get('type')); - $manager = $this->getManager(); - - // whatever is returned by the query, it must be part of these journals: - $journalIds = $this->repository->getJournalIds($linkType); - - /** @var User $admin */ - $admin = auth()->user(); - - // use new group collector: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector - ->setUser($admin) - // filter on journal IDs. - ->setJournalIds($journalIds) - // all info needed for the API: - ->withAPIInformation() - // set page size: - ->setLimit($pageSize) - // set page to retrieve - ->setPage($this->parameters->get('page')) - // set types of transactions to return. - ->setTypes($types); - - - if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { - $collector->setRange($this->parameters->get('start'), $this->parameters->get('end')); - } - $paginator = $collector->getPaginatedGroups(); - $paginator->setPath(route('api.v1.transactions.index') . $this->buildParams()); - $transactions = $paginator->getCollection(); - - /** @var TransactionGroupTransformer $transformer */ - $transformer = app(TransactionGroupTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($transactions, $transformer, 'transactions'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - - /** - * Update object. - * - * @param LinkTypeUpdateRequest $request - * @param LinkType $linkType - * - * @return JsonResponse - * @throws FireflyException - */ - public function update(LinkTypeUpdateRequest $request, LinkType $linkType): JsonResponse - { - if (false === $linkType->editable) { - throw new FireflyException('200020: Link type cannot be changed.'); - } - - /** @var User $admin */ - $admin = auth()->user(); - - if (!$this->userRepository->hasRole($admin, 'owner')) { - throw new FireflyException('200005: You need the "owner" role to do this.'); // @codeCoverageIgnore - } - - $data = $request->getAll(); - $this->repository->update($linkType, $data); - $manager = $this->getManager(); - /** @var LinkTypeTransformer $transformer */ - $transformer = app(LinkTypeTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($linkType, $transformer, 'link_types'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } -} diff --git a/app/Api/V1/Controllers/Models/Account/DestroyController.php b/app/Api/V1/Controllers/Models/Account/DestroyController.php new file mode 100644 index 0000000000..308d8b9669 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Account/DestroyController.php @@ -0,0 +1,72 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Controllers\Models\Account; + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\Account; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use Illuminate\Http\JsonResponse; + +/** + * Class DestroyController + */ +class DestroyController extends Controller +{ + public const RESOURCE_KEY = 'accounts'; + + private AccountRepositoryInterface $repository; + + /** + * AccountController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->repository = app(AccountRepositoryInterface::class); + $this->repository->setUser(auth()->user()); + + return $next($request); + } + ); + } + + /** + * Remove the specified resource from storage. + * + * @param Account $account + * + * @codeCoverageIgnore + * @return JsonResponse + */ + public function destroy(Account $account): JsonResponse + { + $this->repository->destroy($account, null); + + return response()->json([], 204); + } +} diff --git a/app/Api/V1/Controllers/AccountController.php b/app/Api/V1/Controllers/Models/Account/ListController.php similarity index 56% rename from app/Api/V1/Controllers/AccountController.php rename to app/Api/V1/Controllers/Models/Account/ListController.php index e22a69f0e3..43d6343713 100644 --- a/app/Api/V1/Controllers/AccountController.php +++ b/app/Api/V1/Controllers/Models/Account/ListController.php @@ -21,16 +21,13 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Controllers; +namespace FireflyIII\Api\V1\Controllers\Models\Account; -use FireflyIII\Api\V1\Requests\AccountStoreRequest; -use FireflyIII\Api\V1\Requests\AccountUpdateRequest; +use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Account; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Support\Http\Api\AccountFilter; use FireflyIII\Support\Http\Api\TransactionFilter; -use FireflyIII\Transformers\AccountTransformer; use FireflyIII\Transformers\AttachmentTransformer; use FireflyIII\Transformers\PiggyBankTransformer; use FireflyIII\Transformers\TransactionGroupTransformer; @@ -41,20 +38,18 @@ use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; -use League\Fractal\Resource\Item; /** - * Class AccountController. + * Class ListController */ -class AccountController extends Controller +class ListController extends Controller { - use AccountFilter, TransactionFilter; + use TransactionFilter; public const RESOURCE_KEY = 'accounts'; private AccountRepositoryInterface $repository; - /** * AccountController constructor. * @@ -65,11 +60,8 @@ class AccountController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); - // @var AccountRepositoryInterface repository $this->repository = app(AccountRepositoryInterface::class); - $this->repository->setUser($user); + $this->repository->setUser(auth()->user()); return $next($request); } @@ -85,7 +77,7 @@ class AccountController extends Controller public function attachments(Account $account): JsonResponse { $manager = $this->getManager(); - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $collection = $this->repository->getAttachments($account); $count = $collection->count(); @@ -105,60 +97,6 @@ class AccountController extends Controller return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } - /** - * Remove the specified resource from storage. - * - * @param Account $account - * - * @codeCoverageIgnore - * @return JsonResponse - */ - public function delete(Account $account): JsonResponse - { - $this->repository->destroy($account, null); - - return response()->json([], 204); - } - - /** - * Display a listing of the resource. - * - * @param Request $request - * - * @codeCoverageIgnore - * @return JsonResponse - */ - public function index(Request $request): JsonResponse - { - $manager = $this->getManager(); - $type = $request->get('type') ?? 'all'; - $this->parameters->set('type', $type); - - // types to get, page size: - $types = $this->mapAccountTypes($this->parameters->get('type')); - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - - // get list of accounts. Count it and split it. - $collection = $this->repository->getAccountsByType($types); - $count = $collection->count(); - $accounts = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); - - // make paginator: - $paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.accounts.index') . $this->buildParams()); - - - /** @var AccountTransformer $transformer */ - $transformer = app(AccountTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($accounts, $transformer, self::RESOURCE_KEY); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** * List all piggies. * @@ -173,7 +111,7 @@ class AccountController extends Controller $manager = $this->getManager(); // types to get, page size: - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; // get list of budgets. Count it and split it. $collection = $this->repository->getPiggyBanks($account); @@ -195,46 +133,6 @@ class AccountController extends Controller } - /** - * Show single instance. - * - * @param Account $account - * - * @return JsonResponse - */ - public function show(Account $account): JsonResponse - { - $manager = $this->getManager(); - - /** @var AccountTransformer $transformer */ - $transformer = app(AccountTransformer::class); - $transformer->setParameters($this->parameters); - $resource = new Item($account, $transformer, self::RESOURCE_KEY); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Store a new instance. - * - * @param AccountStoreRequest $request - * - * @return JsonResponse - */ - public function store(AccountStoreRequest $request): JsonResponse - { - $data = $request->getAllAccountData(); - $account = $this->repository->store($data); - $manager = $this->getManager(); - - /** @var AccountTransformer $transformer */ - $transformer = app(AccountTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($account, $transformer, self::RESOURCE_KEY); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } /** * Show all transaction groups related to the account. @@ -248,7 +146,7 @@ class AccountController extends Controller */ public function transactions(Request $request, Account $account): JsonResponse { - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $type = $request->get('type') ?? 'default'; $this->parameters->set('type', $type); @@ -285,27 +183,4 @@ class AccountController extends Controller return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } - - /** - * Update account. - * - * @param AccountUpdateRequest $request - * @param Account $account - * - * @return JsonResponse - */ - public function update(AccountUpdateRequest $request, Account $account): JsonResponse - { - $data = $request->getUpdateData(); - $data['type'] = config('firefly.shortNamesByFullName.' . $account->accountType->type); - $this->repository->update($account, $data); - $manager = $this->getManager(); - - /** @var AccountTransformer $transformer */ - $transformer = app(AccountTransformer::class); - $transformer->setParameters($this->parameters); - $resource = new Item($account, $transformer, self::RESOURCE_KEY); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } } diff --git a/app/Api/V1/Controllers/Models/Account/ShowController.php b/app/Api/V1/Controllers/Models/Account/ShowController.php new file mode 100644 index 0000000000..aa5dd3ccdc --- /dev/null +++ b/app/Api/V1/Controllers/Models/Account/ShowController.php @@ -0,0 +1,126 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Account; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\Account; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Support\Http\Api\AccountFilter; +use FireflyIII\Transformers\AccountTransformer; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; + +/** + * Class ShowController + */ +class ShowController extends Controller +{ + use AccountFilter; + + public const RESOURCE_KEY = 'accounts'; + + private AccountRepositoryInterface $repository; + + /** + * AccountController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->repository = app(AccountRepositoryInterface::class); + $this->repository->setUser(auth()->user()); + + return $next($request); + } + ); + } + + /** + * Display a listing of the resource. + * + * @param Request $request + * + * @codeCoverageIgnore + * @return JsonResponse + */ + public function index(Request $request): JsonResponse + { + $manager = $this->getManager(); + $type = $request->get('type') ?? 'all'; + $this->parameters->set('type', $type); + + // types to get, page size: + $types = $this->mapAccountTypes($this->parameters->get('type')); + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of accounts. Count it and split it. + $this->repository->resetAccountOrder(); + $collection = $this->repository->getAccountsByType($types); + $count = $collection->count(); + $accounts = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.accounts.index') . $this->buildParams()); + + /** @var AccountTransformer $transformer */ + $transformer = app(AccountTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($accounts, $transformer, self::RESOURCE_KEY); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + /** + * Show single instance. + * + * @param Account $account + * + * @return JsonResponse + */ + public function show(Account $account): JsonResponse + { + // get list of accounts. Count it and split it. + $this->repository->resetAccountOrder(); + $account->refresh(); + $manager = $this->getManager(); + + /** @var AccountTransformer $transformer */ + $transformer = app(AccountTransformer::class); + $transformer->setParameters($this->parameters); + $resource = new Item($account, $transformer, self::RESOURCE_KEY); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Account/StoreController.php b/app/Api/V1/Controllers/Models/Account/StoreController.php new file mode 100644 index 0000000000..a0d8d464ef --- /dev/null +++ b/app/Api/V1/Controllers/Models/Account/StoreController.php @@ -0,0 +1,84 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Controllers\Models\Account; + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\Account\StoreRequest; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Transformers\AccountTransformer; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class StoreController + */ +class StoreController extends Controller +{ + public const RESOURCE_KEY = 'accounts'; + + private AccountRepositoryInterface $repository; + + + /** + * AccountController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->repository = app(AccountRepositoryInterface::class); + $this->repository->setUser(auth()->user()); + + return $next($request); + } + ); + } + + /** + * Store a new instance. + * + * @param StoreRequest $request + * + * @return JsonResponse + */ + public function store(StoreRequest $request): JsonResponse + { + $data = $request->getAllAccountData(); + $this->repository->resetAccountOrder(); + $account = $this->repository->store($data); + $manager = $this->getManager(); + + /** @var AccountTransformer $transformer */ + $transformer = app(AccountTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($account, $transformer, self::RESOURCE_KEY); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + +} diff --git a/app/Api/V1/Controllers/Models/Account/UpdateController.php b/app/Api/V1/Controllers/Models/Account/UpdateController.php new file mode 100644 index 0000000000..665c10e7ca --- /dev/null +++ b/app/Api/V1/Controllers/Models/Account/UpdateController.php @@ -0,0 +1,87 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Controllers\Models\Account; + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\Account\UpdateRequest; +use FireflyIII\Models\Account; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Transformers\AccountTransformer; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; +use Log; + +/** + * Class UpdateController + */ +class UpdateController extends Controller +{ + public const RESOURCE_KEY = 'accounts'; + + private AccountRepositoryInterface $repository; + + + /** + * AccountController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->repository = app(AccountRepositoryInterface::class); + $this->repository->setUser(auth()->user()); + + return $next($request); + } + ); + } + + /** + * Update account. + * + * @param UpdateRequest $request + * @param Account $account + * + * @return JsonResponse + */ + public function update(UpdateRequest $request, Account $account): JsonResponse + { + Log::debug(sprintf('Now in %s', __METHOD__)); + $data = $request->getUpdateData(); + $data['type'] = config('firefly.shortNamesByFullName.' . $account->accountType->type); + $account = $this->repository->update($account, $data); + $manager = $this->getManager(); + $account->refresh(); + + /** @var AccountTransformer $transformer */ + $transformer = app(AccountTransformer::class); + $transformer->setParameters($this->parameters); + $resource = new Item($account, $transformer, self::RESOURCE_KEY); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} diff --git a/app/Api/V1/Controllers/Models/Attachment/DestroyController.php b/app/Api/V1/Controllers/Models/Attachment/DestroyController.php new file mode 100644 index 0000000000..977556ffcf --- /dev/null +++ b/app/Api/V1/Controllers/Models/Attachment/DestroyController.php @@ -0,0 +1,77 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Attachment; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Middleware\ApiDemoUser; +use FireflyIII\Models\Attachment; +use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; + +/** + * Class DestroyController + */ +class DestroyController extends Controller +{ + private AttachmentRepositoryInterface $repository; + + + /** + * DestroyController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware(ApiDemoUser::class)->except(['delete', 'download', 'show', 'index']); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->repository = app(AttachmentRepositoryInterface::class); + $this->repository->setUser($user); + + + return $next($request); + } + ); + } + + /** + * Remove the specified resource from storage. + * + * @codeCoverageIgnore + * + * @param Attachment $attachment + * + * @return JsonResponse + */ + public function destroy(Attachment $attachment): JsonResponse + { + $this->repository->destroy($attachment); + + return response()->json([], 204); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/AttachmentController.php b/app/Api/V1/Controllers/Models/Attachment/ShowController.php similarity index 62% rename from app/Api/V1/Controllers/AttachmentController.php rename to app/Api/V1/Controllers/Models/Attachment/ShowController.php index aba389d7b3..cc49aa53ba 100644 --- a/app/Api/V1/Controllers/AttachmentController.php +++ b/app/Api/V1/Controllers/Models/Attachment/ShowController.php @@ -1,7 +1,7 @@ . */ -declare(strict_types=1); +namespace FireflyIII\Api\V1\Controllers\Models\Attachment; -namespace FireflyIII\Api\V1\Controllers; +use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Middleware\ApiDemoUser; -use FireflyIII\Api\V1\Requests\AttachmentStoreRequest; -use FireflyIII\Api\V1\Requests\AttachmentUpdateRequest; use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Helpers\Attachments\AttachmentHelperInterface; use FireflyIII\Models\Attachment; use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface; use FireflyIII\Transformers\AttachmentTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; -use Illuminate\Http\Request; use Illuminate\Http\Response as LaravelResponse; use Illuminate\Pagination\LengthAwarePaginator; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Item; -use Log; -use function strlen; /** - * Class AttachmentController. + * Class ShowController */ -class AttachmentController extends Controller +class ShowController extends Controller { - /** @var AttachmentRepositoryInterface The attachment repository */ - private $repository; + private AttachmentRepositoryInterface $repository; /** - * AccountController constructor. + * ShowController constructor. * * @codeCoverageIgnore */ @@ -73,22 +66,6 @@ class AttachmentController extends Controller ); } - /** - * Remove the specified resource from storage. - * - * @codeCoverageIgnore - * - * @param Attachment $attachment - * - * @return JsonResponse - */ - public function delete(Attachment $attachment): JsonResponse - { - $this->repository->destroy($attachment); - - return response()->json([], 204); - } - /** * Download an attachment. * @@ -144,7 +121,7 @@ class AttachmentController extends Controller // types to get, page size: $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - // get list of accounts. Count it and split it. + // get list of attachments. Count it and split it. $collection = $this->repository->get(); $count = $collection->count(); $attachments = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); @@ -163,6 +140,8 @@ class AttachmentController extends Controller return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } + + /** * Display the specified resource. * @@ -181,76 +160,4 @@ class AttachmentController extends Controller return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } - - /** - * Store a newly created resource in storage. - * - * @param AttachmentStoreRequest $request - * - * @return JsonResponse - * @throws FireflyException - */ - public function store(AttachmentStoreRequest $request): JsonResponse - { - $data = $request->getAll(); - $attachment = $this->repository->store($data); - $manager = $this->getManager(); - - /** @var AttachmentTransformer $transformer */ - $transformer = app(AttachmentTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($attachment, $transformer, 'attachments'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Update the specified resource in storage. - * - * @param AttachmentUpdateRequest $request - * @param Attachment $attachment - * - * @return JsonResponse - */ - public function update(AttachmentUpdateRequest $request, Attachment $attachment): JsonResponse - { - $data = $request->getAll(); - $this->repository->update($attachment, $data); - $manager = $this->getManager(); - - /** @var AttachmentTransformer $transformer */ - $transformer = app(AttachmentTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($attachment, $transformer, 'attachments'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Upload an attachment. - * - * @codeCoverageIgnore - * - * @param Request $request - * @param Attachment $attachment - * - * @return JsonResponse - */ - public function upload(Request $request, Attachment $attachment): JsonResponse - { - /** @var AttachmentHelperInterface $helper */ - $helper = app(AttachmentHelperInterface::class); - $body = $request->getContent(); - if ('' === $body) { - Log::error('Body of attachment is empty.'); - - return response()->json([], 422); - } - $helper->saveAttachmentFromApi($attachment, $body); - - return response()->json([], 204); - } - -} +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Attachment/StoreController.php b/app/Api/V1/Controllers/Models/Attachment/StoreController.php new file mode 100644 index 0000000000..63218934cb --- /dev/null +++ b/app/Api/V1/Controllers/Models/Attachment/StoreController.php @@ -0,0 +1,119 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Attachment; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Middleware\ApiDemoUser; +use FireflyIII\Api\V1\Requests\Models\Attachment\StoreRequest; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Helpers\Attachments\AttachmentHelperInterface; +use FireflyIII\Models\Attachment; +use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface; +use FireflyIII\Transformers\AttachmentTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; +use League\Fractal\Resource\Item; +use Log; + +/** + * Class StoreController + */ +class StoreController extends Controller +{ + private AttachmentRepositoryInterface $repository; + + + /** + * StoreController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware(ApiDemoUser::class)->except(['delete', 'download', 'show', 'index']); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->repository = app(AttachmentRepositoryInterface::class); + $this->repository->setUser($user); + + + return $next($request); + } + ); + } + + + + /** + * Store a newly created resource in storage. + * + * @param StoreRequest $request + * + * @return JsonResponse + * @throws FireflyException + */ + public function store(StoreRequest $request): JsonResponse + { + Log::debug(sprintf('Now in %s', __METHOD__)); + $data = $request->getAll(); + $attachment = $this->repository->store($data); + $manager = $this->getManager(); + + /** @var AttachmentTransformer $transformer */ + $transformer = app(AttachmentTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($attachment, $transformer, 'attachments'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + /** + * Upload an attachment. + * + * @codeCoverageIgnore + * + * @param Request $request + * @param Attachment $attachment + * + * @return JsonResponse + */ + public function upload(Request $request, Attachment $attachment): JsonResponse + { + /** @var AttachmentHelperInterface $helper */ + $helper = app(AttachmentHelperInterface::class); + $body = $request->getContent(); + if ('' === $body) { + Log::error('Body of attachment is empty.'); + + return response()->json([], 422); + } + $helper->saveAttachmentFromApi($attachment, $body); + + return response()->json([], 204); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Attachment/UpdateController.php b/app/Api/V1/Controllers/Models/Attachment/UpdateController.php new file mode 100644 index 0000000000..209a2c6b65 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Attachment/UpdateController.php @@ -0,0 +1,89 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Attachment; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Middleware\ApiDemoUser; +use FireflyIII\Api\V1\Requests\Models\Attachment\UpdateRequest; +use FireflyIII\Models\Attachment; +use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface; +use FireflyIII\Transformers\AttachmentTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class UpdateController + */ +class UpdateController extends Controller +{ + private AttachmentRepositoryInterface $repository; + + + /** + * UpdateController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware(ApiDemoUser::class)->except(['delete', 'download', 'show', 'index']); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->repository = app(AttachmentRepositoryInterface::class); + $this->repository->setUser($user); + + + return $next($request); + } + ); + } + + + + /** + * Update the specified resource in storage. + * + * @param UpdateRequest $request + * @param Attachment $attachment + * + * @return JsonResponse + */ + public function update(UpdateRequest $request, Attachment $attachment): JsonResponse + { + $data = $request->getAll(); + $this->repository->update($attachment, $data); + $manager = $this->getManager(); + + /** @var AttachmentTransformer $transformer */ + $transformer = app(AttachmentTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($attachment, $transformer, 'attachments'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/AvailableBudget/DestroyController.php b/app/Api/V1/Controllers/Models/AvailableBudget/DestroyController.php new file mode 100644 index 0000000000..79f264d4cf --- /dev/null +++ b/app/Api/V1/Controllers/Models/AvailableBudget/DestroyController.php @@ -0,0 +1,72 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\AvailableBudget; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\AvailableBudget; +use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; + +/** + * Class DestroyController + */ +class DestroyController extends Controller +{ + private AvailableBudgetRepositoryInterface $abRepository; + + /** + * AvailableBudgetController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->abRepository = app(AvailableBudgetRepositoryInterface::class); + $this->abRepository->setUser($user); + + return $next($request); + } + ); + } + /** + * Remove the specified resource from storage. + * + * @param AvailableBudget $availableBudget + * + * @codeCoverageIgnore + * + * @return JsonResponse + */ + public function destroy(AvailableBudget $availableBudget): JsonResponse + { + $this->abRepository->destroyAvailableBudget($availableBudget); + + return response()->json([], 204); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/AvailableBudgetController.php b/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php similarity index 53% rename from app/Api/V1/Controllers/AvailableBudgetController.php rename to app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php index 4a1d8e8955..459e6816ca 100644 --- a/app/Api/V1/Controllers/AvailableBudgetController.php +++ b/app/Api/V1/Controllers/Models/AvailableBudget/ShowController.php @@ -1,7 +1,7 @@ . */ -declare(strict_types=1); +namespace FireflyIII\Api\V1\Controllers\Models\AvailableBudget; -namespace FireflyIII\Api\V1\Controllers; -use FireflyIII\Api\V1\Requests\AvailableBudgetRequest; -use FireflyIII\Factory\TransactionCurrencyFactory; +use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Models\AvailableBudget; -use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface; use FireflyIII\Transformers\AvailableBudgetTransformer; use FireflyIII\User; @@ -37,9 +34,9 @@ use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Item; /** - * Class AvailableBudgetController. + * Class ShowController */ -class AvailableBudgetController extends Controller +class ShowController extends Controller { private AvailableBudgetRepositoryInterface $abRepository; @@ -63,22 +60,6 @@ class AvailableBudgetController extends Controller ); } - /** - * Remove the specified resource from storage. - * - * @param AvailableBudget $availableBudget - * - * @codeCoverageIgnore - * - * @return JsonResponse - */ - public function delete(AvailableBudget $availableBudget): JsonResponse - { - $this->abRepository->destroyAvailableBudget($availableBudget); - - return response()->json([], 204); - } - /** * Display a listing of the resource. * @@ -135,77 +116,4 @@ class AvailableBudgetController extends Controller return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } - /** - * Store a newly created resource in storage. - * - * @param AvailableBudgetRequest $request - * - * @return JsonResponse - */ - public function store(AvailableBudgetRequest $request): JsonResponse - { - $data = $request->getAll(); - $data['start']->startOfDay(); - $data['end']->endOfDay(); - - /** @var TransactionCurrencyFactory $factory */ - $factory = app(TransactionCurrencyFactory::class); - $currency = $factory->find($data['currency_id'], $data['currency_code']); - - if (null === $currency) { - $currency = app('amount')->getDefaultCurrency(); - } - $data['currency'] = $currency; - $availableBudget = $this->abRepository->store($data); - $manager = $this->getManager(); - - /** @var AvailableBudgetTransformer $transformer */ - $transformer = app(AvailableBudgetTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($availableBudget, $transformer, 'available_budgets'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - - /** - * Update the specified resource in storage. - * - * @param AvailableBudgetRequest $request - * @param AvailableBudget $availableBudget - * - * @return JsonResponse - */ - public function update(AvailableBudgetRequest $request, AvailableBudget $availableBudget): JsonResponse - { - $data = $request->getAll(); - - /** @var TransactionCurrencyFactory $factory */ - $factory = app(TransactionCurrencyFactory::class); - /** @var TransactionCurrency $currency */ - $currency = $factory->find($data['currency_id'] ?? null, $data['currency_code'] ?? null); - - if (null === $currency) { - // use default currency: - $currency = app('amount')->getDefaultCurrency(); - } - $currency->enabled = true; - $currency->save(); - unset($data['currency_code']); - $data['currency_id'] = $currency->id; - - - $this->abRepository->updateAvailableBudget($availableBudget, $data); - $manager = $this->getManager(); - - /** @var AvailableBudgetTransformer $transformer */ - $transformer = app(AvailableBudgetTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($availableBudget, $transformer, 'available_budgets'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } -} +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/AvailableBudget/StoreController.php b/app/Api/V1/Controllers/Models/AvailableBudget/StoreController.php new file mode 100644 index 0000000000..72f533a722 --- /dev/null +++ b/app/Api/V1/Controllers/Models/AvailableBudget/StoreController.php @@ -0,0 +1,97 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\AvailableBudget; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\AvailableBudget\Request; +use FireflyIII\Factory\TransactionCurrencyFactory; +use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface; +use FireflyIII\Transformers\AvailableBudgetTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class StoreController + */ +class StoreController extends Controller +{ + private AvailableBudgetRepositoryInterface $abRepository; + + /** + * AvailableBudgetController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->abRepository = app(AvailableBudgetRepositoryInterface::class); + $this->abRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Store a newly created resource in storage. + * + * @param Request $request + * + * @return JsonResponse + */ + public function store(Request $request): JsonResponse + { + $data = $request->getAll(); + $data['start']->startOfDay(); + $data['end']->endOfDay(); + + // currency is not mandatory: + if (array_key_exists('currency_id', $data) || array_key_exists('currency_code', $data)) { + $factory = app(TransactionCurrencyFactory::class); + $currency = $factory->find($data['currency_id'] ?? null, $data['currency_code'] ?? null); + $data['currency_id'] = $currency->id; + unset($data['currency_code']); + } + if (!array_key_exists('currency_id', $data)) { + $currency = app('amount')->getDefaultCurrencyByUser(auth()->user()); + $data['currency_id'] = $currency->id; + } + + $availableBudget = $this->abRepository->store($data); + $manager = $this->getManager(); + + /** @var AvailableBudgetTransformer $transformer */ + $transformer = app(AvailableBudgetTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($availableBudget, $transformer, 'available_budgets'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/AvailableBudget/UpdateController.php b/app/Api/V1/Controllers/Models/AvailableBudget/UpdateController.php new file mode 100644 index 0000000000..e9c8bb53ad --- /dev/null +++ b/app/Api/V1/Controllers/Models/AvailableBudget/UpdateController.php @@ -0,0 +1,97 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\AvailableBudget; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\AvailableBudget\Request; +use FireflyIII\Factory\TransactionCurrencyFactory; +use FireflyIII\Models\AvailableBudget; +use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface; +use FireflyIII\Transformers\AvailableBudgetTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class UpdateController + */ +class UpdateController extends Controller +{ + private AvailableBudgetRepositoryInterface $abRepository; + + /** + * AvailableBudgetController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->abRepository = app(AvailableBudgetRepositoryInterface::class); + $this->abRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Update the specified resource in storage. + * + * @param Request $request + * @param AvailableBudget $availableBudget + * + * @return JsonResponse + */ + public function update(Request $request, AvailableBudget $availableBudget): JsonResponse + { + $data = $request->getAll(); + + // find and validate currency ID + if(array_key_exists('currency_id', $data) || array_key_exists('currency_code', $data)) { + $factory = app(TransactionCurrencyFactory::class); + $currency = $factory->find($data['currency_id'] ?? null, $data['currency_code'] ?? null) ?? app('amount')->getDefaultCurrency(); + $currency->enabled = true; + $currency->save(); + unset($data['currency_code']); + $data['currency_id'] = $currency->id; + } + + $this->abRepository->updateAvailableBudget($availableBudget, $data); + $manager = $this->getManager(); + + /** @var AvailableBudgetTransformer $transformer */ + $transformer = app(AvailableBudgetTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($availableBudget, $transformer, 'available_budgets'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Bill/DestroyController.php b/app/Api/V1/Controllers/Models/Bill/DestroyController.php new file mode 100644 index 0000000000..6e110e1249 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Bill/DestroyController.php @@ -0,0 +1,76 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Bill; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\Bill; +use FireflyIII\Repositories\Bill\BillRepositoryInterface; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; + +/** + * Class DestroyController + */ +class DestroyController extends Controller +{ + private BillRepositoryInterface $repository; + + /** + * BillController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $admin */ + $admin = auth()->user(); + + /** @var BillRepositoryInterface repository */ + $this->repository = app(BillRepositoryInterface::class); + $this->repository->setUser($admin); + + return $next($request); + } + ); + } + + + /** + * Remove the specified resource from storage. + * + * @param Bill $bill + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function destroy(Bill $bill): JsonResponse + { + $this->repository->destroy($bill); + + return response()->json([], 204); + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/BillController.php b/app/Api/V1/Controllers/Models/Bill/ListController.php similarity index 64% rename from app/Api/V1/Controllers/BillController.php rename to app/Api/V1/Controllers/Models/Bill/ListController.php index 9207b0bdf2..d5d91b45d4 100644 --- a/app/Api/V1/Controllers/BillController.php +++ b/app/Api/V1/Controllers/Models/Bill/ListController.php @@ -1,8 +1,7 @@ . */ -declare(strict_types=1); +namespace FireflyIII\Api\V1\Controllers\Models\Bill; -namespace FireflyIII\Api\V1\Controllers; -use FireflyIII\Api\V1\Requests\BillUpdateRequest; -use FireflyIII\Api\V1\Requests\BillStoreRequest; -use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Bill; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Transformers\AttachmentTransformer; -use FireflyIII\Transformers\BillTransformer; use FireflyIII\Transformers\RuleTransformer; use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\User; @@ -41,12 +36,11 @@ use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; -use League\Fractal\Resource\Item; /** - * Class BillController. + * Class ListController */ -class BillController extends Controller +class ListController extends Controller { use TransactionFilter; @@ -106,45 +100,7 @@ class BillController extends Controller return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } - /** - * Remove the specified resource from storage. - * - * @param Bill $bill - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function delete(Bill $bill): JsonResponse - { - $this->repository->destroy($bill); - return response()->json([], 204); - } - - /** - * Display a listing of the resource. - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function index(): JsonResponse - { - $bills = $this->repository->getBills(); - $manager = $this->getManager(); - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - $count = $bills->count(); - $bills = $bills->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); - $paginator = new LengthAwarePaginator($bills, $count, $pageSize, $this->parameters->get('page')); - - /** @var BillTransformer $transformer */ - $transformer = app(BillTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($bills, $transformer, 'bills'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } /** * List all of them. @@ -182,47 +138,7 @@ class BillController extends Controller } - /** - * Show the specified bill. - * - * @param Bill $bill - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function show(Bill $bill): JsonResponse - { - $manager = $this->getManager(); - /** @var BillTransformer $transformer */ - $transformer = app(BillTransformer::class); - $transformer->setParameters($this->parameters); - $resource = new Item($bill, $transformer, 'bills'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Store a bill. - * - * @param BillStoreRequest $request - * - * @return JsonResponse - * @throws FireflyException - */ - public function store(BillStoreRequest $request): JsonResponse - { - $bill = $this->repository->store($request->getAll()); - $manager = $this->getManager(); - - /** @var BillTransformer $transformer */ - $transformer = app(BillTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($bill, $transformer, 'bills'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } /** * Show all transactions. @@ -282,27 +198,4 @@ class BillController extends Controller return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } - /** - * Update a bill. - * - * @param BillUpdateRequest $request - * @param Bill $bill - * - * @return JsonResponse - */ - public function update(BillUpdateRequest $request, Bill $bill): JsonResponse - { - $data = $request->getAll(); - $bill = $this->repository->update($bill, $data); - $manager = $this->getManager(); - - /** @var BillTransformer $transformer */ - $transformer = app(BillTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($bill, $transformer, 'bills'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } -} +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Bill/ShowController.php b/app/Api/V1/Controllers/Models/Bill/ShowController.php new file mode 100644 index 0000000000..385066d96b --- /dev/null +++ b/app/Api/V1/Controllers/Models/Bill/ShowController.php @@ -0,0 +1,111 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Bill; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\Bill; +use FireflyIII\Repositories\Bill\BillRepositoryInterface; +use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Transformers\BillTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; + +/** + * Class ShowController + */ +class ShowController extends Controller +{ + private BillRepositoryInterface $repository; + + /** + * BillController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $admin */ + $admin = auth()->user(); + + /** @var BillRepositoryInterface repository */ + $this->repository = app(BillRepositoryInterface::class); + $this->repository->setUser($admin); + + return $next($request); + } + ); + } + + /** + * Display a listing of the resource. + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function index(): JsonResponse + { + $this->repository->correctOrder(); + $bills = $this->repository->getBills(); + $manager = $this->getManager(); + $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $count = $bills->count(); + $bills = $bills->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + $paginator = new LengthAwarePaginator($bills, $count, $pageSize, $this->parameters->get('page')); + + /** @var BillTransformer $transformer */ + $transformer = app(BillTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($bills, $transformer, 'bills'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + /** + * Show the specified bill. + * + * @param Bill $bill + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function show(Bill $bill): JsonResponse + { + $manager = $this->getManager(); + /** @var BillTransformer $transformer */ + $transformer = app(BillTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($bill, $transformer, 'bills'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Bill/StoreController.php b/app/Api/V1/Controllers/Models/Bill/StoreController.php new file mode 100644 index 0000000000..7cb6c2847f --- /dev/null +++ b/app/Api/V1/Controllers/Models/Bill/StoreController.php @@ -0,0 +1,89 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Bill; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\Bill\StoreRequest; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Repositories\Bill\BillRepositoryInterface; +use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Transformers\BillTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class StoreController + */ +class StoreController extends Controller +{ + use TransactionFilter; + + private BillRepositoryInterface $repository; + + + /** + * BillController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $admin */ + $admin = auth()->user(); + + /** @var BillRepositoryInterface repository */ + $this->repository = app(BillRepositoryInterface::class); + $this->repository->setUser($admin); + + return $next($request); + } + ); + } + + /** + * Store a bill. + * + * @param StoreRequest $request + * + * @return JsonResponse + * @throws FireflyException + */ + public function store(StoreRequest $request): JsonResponse + { + $data = $request->getAll(); + $bill = $this->repository->store($data); + $manager = $this->getManager(); + + /** @var BillTransformer $transformer */ + $transformer = app(BillTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($bill, $transformer, 'bills'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Bill/UpdateController.php b/app/Api/V1/Controllers/Models/Bill/UpdateController.php new file mode 100644 index 0000000000..6a899b0bb3 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Bill/UpdateController.php @@ -0,0 +1,88 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Bill; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\Bill\UpdateRequest; +use FireflyIII\Models\Bill; +use FireflyIII\Repositories\Bill\BillRepositoryInterface; +use FireflyIII\Transformers\BillTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class UpdateController + */ +class UpdateController extends Controller +{ + private BillRepositoryInterface $repository; + + + /** + * BillController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $admin */ + $admin = auth()->user(); + + /** @var BillRepositoryInterface repository */ + $this->repository = app(BillRepositoryInterface::class); + $this->repository->setUser($admin); + + return $next($request); + } + ); + } + + /** + * Update a bill. + * + * @param UpdateRequest $request + * @param Bill $bill + * + * @return JsonResponse + */ + public function update(UpdateRequest $request, Bill $bill): JsonResponse + { + $data = $request->getAll(); + $bill = $this->repository->update($bill, $data); + $manager = $this->getManager(); + + /** @var BillTransformer $transformer */ + $transformer = app(BillTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($bill, $transformer, 'bills'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Budget/DestroyController.php b/app/Api/V1/Controllers/Models/Budget/DestroyController.php new file mode 100644 index 0000000000..9afe473fd5 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Budget/DestroyController.php @@ -0,0 +1,69 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Budget; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\Budget; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use Illuminate\Http\JsonResponse; + +/** + * Class DestroyController + */ +class DestroyController extends Controller +{ + private BudgetRepositoryInterface $repository; + + /** + * DestroyController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->repository = app(BudgetRepositoryInterface::class); + $this->repository->setUser(auth()->user()); + + return $next($request); + } + ); + } + + /** + * Remove the specified resource from storage. + * + * @param Budget $budget + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function destroy(Budget $budget): JsonResponse + { + $this->repository->destroy($budget); + + return response()->json([], 204); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/BudgetController.php b/app/Api/V1/Controllers/Models/Budget/ListController.php similarity index 57% rename from app/Api/V1/Controllers/BudgetController.php rename to app/Api/V1/Controllers/Models/Budget/ListController.php index ed570f20e5..065b7597f5 100644 --- a/app/Api/V1/Controllers/BudgetController.php +++ b/app/Api/V1/Controllers/Models/Budget/ListController.php @@ -1,7 +1,7 @@ . */ -declare(strict_types=1); +namespace FireflyIII\Api\V1\Controllers\Models\Budget; -namespace FireflyIII\Api\V1\Controllers; -use FireflyIII\Api\V1\Requests\BudgetStoreRequest; -use FireflyIII\Api\V1\Requests\BudgetUpdateRequest; -use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Budget; use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; @@ -33,7 +30,6 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Transformers\AttachmentTransformer; use FireflyIII\Transformers\BudgetLimitTransformer; -use FireflyIII\Transformers\BudgetTransformer; use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; @@ -41,24 +37,19 @@ use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; -use League\Fractal\Resource\Item; -/** - * Class BudgetController. +/*** + * Class ListController */ -class BudgetController extends Controller +class ListController extends Controller { use TransactionFilter; - /** @var BudgetLimitRepositoryInterface */ - private $blRepository; - - /** @var BudgetRepositoryInterface The budget repository */ - private $repository; - + private BudgetLimitRepositoryInterface $blRepository; + private BudgetRepositoryInterface $repository; /** - * BudgetController constructor. + * ListController constructor. * * @codeCoverageIgnore */ @@ -67,49 +58,16 @@ class BudgetController extends Controller parent::__construct(); $this->middleware( function ($request, $next) { - /** @var User $admin */ - $admin = auth()->user(); - $this->repository = app(BudgetRepositoryInterface::class); $this->blRepository = app(BudgetLimitRepositoryInterface::class); - $this->repository->setUser($admin); - $this->blRepository->setUser($admin); + $this->repository->setUser(auth()->user()); + $this->blRepository->setUser(auth()->user()); return $next($request); } ); } - /** - * Display a listing of the resource. - * - * @param Budget $budget - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function budgetLimits(Budget $budget): JsonResponse - { - $manager = $this->getManager(); - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - $this->parameters->set('budget_id', $budget->id); - $collection = $this->blRepository->getBudgetLimits($budget, $this->parameters->get('start'), $this->parameters->get('end')); - $count = $collection->count(); - $budgetLimits = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); - $paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.budgets.budget_limits', [$budget->id]) . $this->buildParams()); - - /** @var BudgetLimitTransformer $transformer */ - $transformer = app(BudgetLimitTransformer::class); - $transformer->setParameters($this->parameters); - - - $resource = new FractalCollection($budgetLimits, $transformer, 'budget_limits'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - /** * @param Budget $budget * @@ -119,7 +77,7 @@ class BudgetController extends Controller public function attachments(Budget $budget): JsonResponse { $manager = $this->getManager(); - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $collection = $this->repository->getAttachments($budget); $count = $collection->count(); @@ -139,93 +97,32 @@ class BudgetController extends Controller return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } - /** - * Remove the specified resource from storage. - * - * @param Budget $budget - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function delete(Budget $budget): JsonResponse - { - $this->repository->destroy($budget); - - return response()->json([], 204); - } - /** * Display a listing of the resource. * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function index(): JsonResponse - { - $manager = $this->getManager(); - - // types to get, page size: - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - - // get list of budgets. Count it and split it. - $collection = $this->repository->getBudgets(); - $count = $collection->count(); - $budgets = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); - - // make paginator: - $paginator = new LengthAwarePaginator($budgets, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.budgets.index') . $this->buildParams()); - - /** @var BudgetTransformer $transformer */ - $transformer = app(BudgetTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($budgets, $transformer, 'budgets'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Show a budget. - * * @param Budget $budget * * @return JsonResponse * @codeCoverageIgnore */ - public function show(Budget $budget): JsonResponse + public function budgetLimits(Budget $budget): JsonResponse { - $manager = $this->getManager(); + $manager = $this->getManager(); + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $this->parameters->set('budget_id', $budget->id); + $collection = $this->blRepository->getBudgetLimits($budget, $this->parameters->get('start'), $this->parameters->get('end')); + $count = $collection->count(); + $budgetLimits = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + $paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.budgets.budget_limits', [$budget->id]) . $this->buildParams()); - /** @var BudgetTransformer $transformer */ - $transformer = app(BudgetTransformer::class); + /** @var BudgetLimitTransformer $transformer */ + $transformer = app(BudgetLimitTransformer::class); $transformer->setParameters($this->parameters); - $resource = new Item($budget, $transformer, 'budgets'); - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Store a budget. - * - * @param BudgetStoreRequest $request - * - * @return JsonResponse - * @throws FireflyException - * - */ - public function store(BudgetStoreRequest $request): JsonResponse - { - $budget = $this->repository->store($request->getAll()); - $manager = $this->getManager(); - - /** @var BudgetTransformer $transformer */ - $transformer = app(BudgetTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($budget, $transformer, 'budgets'); + $resource = new FractalCollection($budgetLimits, $transformer, 'budget_limits'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } @@ -242,7 +139,7 @@ class BudgetController extends Controller */ public function transactions(Request $request, Budget $budget): JsonResponse { - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; // user can overrule page size with limit parameter. $limit = $this->parameters->get('limit'); @@ -294,28 +191,4 @@ class BudgetController extends Controller return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } - /** - * Update a budget. - * - * @param BudgetUpdateRequest $request - * @param Budget $budget - * - * @return JsonResponse - */ - public function update(BudgetUpdateRequest $request, Budget $budget): JsonResponse - { - $data = $request->getAll(); - $budget = $this->repository->update($budget, $data); - $manager = $this->getManager(); - - /** @var BudgetTransformer $transformer */ - $transformer = app(BudgetTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($budget, $transformer, 'budgets'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - -} +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Budget/ShowController.php b/app/Api/V1/Controllers/Models/Budget/ShowController.php new file mode 100644 index 0000000000..424e7fee6e --- /dev/null +++ b/app/Api/V1/Controllers/Models/Budget/ShowController.php @@ -0,0 +1,117 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Budget; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\Budget; +use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Transformers\BudgetTransformer; +use Illuminate\Http\JsonResponse; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; + +/** + * Class ShowController + */ +class ShowController extends Controller +{ + private BudgetLimitRepositoryInterface $blRepository; + private BudgetRepositoryInterface $repository; + + /** + * ListController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->repository = app(BudgetRepositoryInterface::class); + $this->blRepository = app(BudgetLimitRepositoryInterface::class); + $this->repository->setUser(auth()->user()); + $this->blRepository->setUser(auth()->user()); + + return $next($request); + } + ); + } + + /** + * Display a listing of the resource. + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function index(): JsonResponse + { + $manager = $this->getManager(); + + // types to get, page size: + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of budgets. Count it and split it. + $collection = $this->repository->getBudgets(); + $count = $collection->count(); + $budgets = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($budgets, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.budgets.index') . $this->buildParams()); + + /** @var BudgetTransformer $transformer */ + $transformer = app(BudgetTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($budgets, $transformer, 'budgets'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + /** + * Show a budget. + * + * @param Budget $budget + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function show(Budget $budget): JsonResponse + { + $manager = $this->getManager(); + + /** @var BudgetTransformer $transformer */ + $transformer = app(BudgetTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($budget, $transformer, 'budgets'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Budget/StoreController.php b/app/Api/V1/Controllers/Models/Budget/StoreController.php new file mode 100644 index 0000000000..9fd4299a77 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Budget/StoreController.php @@ -0,0 +1,82 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Budget; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\Budget\StoreRequest; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Transformers\BudgetTransformer; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class StoreController + */ +class StoreController extends Controller +{ + private BudgetRepositoryInterface $repository; + + + /** + * StoreController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->repository = app(BudgetRepositoryInterface::class); + $this->repository->setUser(auth()->user()); + + return $next($request); + } + ); + } + + /** + * Store a budget. + * + * @param StoreRequest $request + * + * @return JsonResponse + * @throws FireflyException + * + */ + public function store(StoreRequest $request): JsonResponse + { + $budget = $this->repository->store($request->getAll()); + $budget->refresh(); + $manager = $this->getManager(); + + /** @var BudgetTransformer $transformer */ + $transformer = app(BudgetTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($budget, $transformer, 'budgets'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Budget/UpdateController.php b/app/Api/V1/Controllers/Models/Budget/UpdateController.php new file mode 100644 index 0000000000..9afbf2aab4 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Budget/UpdateController.php @@ -0,0 +1,81 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Budget; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\Budget\UpdateRequest; +use FireflyIII\Models\Budget; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Transformers\BudgetTransformer; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class UpdateController + */ +class UpdateController extends Controller +{ + private BudgetRepositoryInterface $repository; + + /** + * UpdateController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->repository = app(BudgetRepositoryInterface::class); + $this->repository->setUser(auth()->user()); + + return $next($request); + } + ); + } + + /** + * Update a budget. + * + * @param UpdateRequest $request + * @param Budget $budget + * + * @return JsonResponse + */ + public function update(UpdateRequest $request, Budget $budget): JsonResponse + { + $data = $request->getAll(); + $budget = $this->repository->update($budget, $data); + $manager = $this->getManager(); + + /** @var BudgetTransformer $transformer */ + $transformer = app(BudgetTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($budget, $transformer, 'budgets'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/DestroyController.php b/app/Api/V1/Controllers/Models/BudgetLimit/DestroyController.php new file mode 100644 index 0000000000..df7de2dd16 --- /dev/null +++ b/app/Api/V1/Controllers/Models/BudgetLimit/DestroyController.php @@ -0,0 +1,79 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\BudgetLimit; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Budget; +use FireflyIII\Models\BudgetLimit; +use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; + +/** + * Class DestroyController + */ +class DestroyController extends Controller +{ + private BudgetLimitRepositoryInterface $blRepository; + + + /** + * BudgetLimitController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->blRepository = app(BudgetLimitRepositoryInterface::class); + $this->blRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Remove the specified resource from storage. + * + * @param Budget $budget + * @param BudgetLimit $budgetLimit + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function destroy(Budget $budget, BudgetLimit $budgetLimit): JsonResponse + { + if ($budget->id !== $budgetLimit->budget_id) { + throw new FireflyException('20028: The budget limit does not belong to the budget.'); + } + $this->blRepository->destroyBudgetLimit($budgetLimit); + + return response()->json([], 204); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php b/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php new file mode 100644 index 0000000000..0b3e3faa5e --- /dev/null +++ b/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php @@ -0,0 +1,122 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\BudgetLimit; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; +use FireflyIII\Models\Budget; +use FireflyIII\Models\BudgetLimit; +use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; +use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Transformers\TransactionGroupTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; + +/** + * Class ListController + */ +class ListController extends Controller +{ + use TransactionFilter; + + private BudgetLimitRepositoryInterface $blRepository; + + + /** + * BudgetLimitController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->blRepository = app(BudgetLimitRepositoryInterface::class); + $this->blRepository->setUser($user); + + return $next($request); + } + ); + } + + + /** + * Show all transactions. + * + * @param Request $request + * @param Budget $budget + * @param BudgetLimit $budgetLimit + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function transactions(Request $request, Budget $budget, BudgetLimit $budgetLimit): JsonResponse + { + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $type = $request->get('type') ?? 'default'; + $this->parameters->set('type', $type); + + $types = $this->mapTransactionTypes($this->parameters->get('type')); + $manager = $this->getManager(); + + /** @var User $admin */ + $admin = auth()->user(); + + // use new group collector: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector + ->setUser($admin) + // filter on budget. + ->setBudget($budget) + // all info needed for the API: + ->withAPIInformation() + // set page size: + ->setLimit($pageSize) + // set page to retrieve + ->setPage($this->parameters->get('page')) + // set types of transactions to return. + ->setTypes($types); + + $collector->setRange($budgetLimit->start_date, $budgetLimit->end_date); + $collector->setTypes($types); + $paginator = $collector->getPaginatedGroups(); + $paginator->setPath(route('api.v1.budgets.limits.transactions', [$budget->id, $budgetLimit->id]) . $this->buildParams()); + $transactions = $paginator->getCollection(); + + /** @var TransactionGroupTransformer $transformer */ + $transformer = app(TransactionGroupTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($transactions, $transformer, 'transactions'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php b/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php new file mode 100644 index 0000000000..4f7120b52d --- /dev/null +++ b/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php @@ -0,0 +1,153 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\BudgetLimit; + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Data\DateRequest; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Budget; +use FireflyIII\Models\BudgetLimit; +use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Transformers\BudgetLimitTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; + +/** + * Class ShowController + */ +class ShowController extends Controller +{ + private BudgetLimitRepositoryInterface $blRepository; + private BudgetRepositoryInterface $repository; + + /** + * BudgetLimitController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->repository = app(BudgetRepositoryInterface::class); + $this->blRepository = app(BudgetLimitRepositoryInterface::class); + $this->repository->setUser($user); + $this->blRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Display a listing of the budget limits for this budget.. + * + * @param Request $request + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function index(Request $request, Budget $budget): JsonResponse + { + $manager = $this->getManager(); + $manager->parseIncludes('budget'); + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $collection = $this->blRepository->getBudgetLimits($budget, $this->parameters->get('start'), $this->parameters->get('end')); + $count = $collection->count(); + $budgetLimits = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + $paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.budgets.limits.index', [$budget->id]) . $this->buildParams()); + + /** @var BudgetLimitTransformer $transformer */ + $transformer = app(BudgetLimitTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($budgetLimits, $transformer, 'budget_limits'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + /** + * @param Request $request + * @param Budget $budget + * @param BudgetLimit $budgetLimit + * + * @return JsonResponse + */ + public function show(Request $request, Budget $budget, BudgetLimit $budgetLimit): JsonResponse + { + if ((int)$budget->id !== (int)$budgetLimit->budget_id) { + throw new FireflyException('20028: The budget limit does not belong to the budget.'); + } + // continue! + $manager = $this->getManager(); + + /** @var BudgetLimitTransformer $transformer */ + $transformer = app(BudgetLimitTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($budgetLimit, $transformer, 'budget_limits'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + + /** + * Display a listing of the budget limits for this budget.. + * + * @param DateRequest $request + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function indexAll(DateRequest $request): JsonResponse + { + $manager = $this->getManager(); + $manager->parseIncludes('budget'); + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $collection = $this->blRepository->getAllBudgetLimits($this->parameters->get('start'), $this->parameters->get('end')); + $count = $collection->count(); + $budgetLimits = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + $paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.budget-limits.index') . $this->buildParams()); + + /** @var BudgetLimitTransformer $transformer */ + $transformer = app(BudgetLimitTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($budgetLimits, $transformer, 'budget_limits'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/StoreController.php b/app/Api/V1/Controllers/Models/BudgetLimit/StoreController.php new file mode 100644 index 0000000000..c623b0909e --- /dev/null +++ b/app/Api/V1/Controllers/Models/BudgetLimit/StoreController.php @@ -0,0 +1,92 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\BudgetLimit; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\BudgetLimit\StoreRequest; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Budget; +use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; +use FireflyIII\Transformers\BudgetLimitTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class StoreController + */ +class StoreController extends Controller +{ + private BudgetLimitRepositoryInterface $blRepository; + + + /** + * BudgetLimitController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->blRepository = app(BudgetLimitRepositoryInterface::class); + $this->blRepository->setUser($user); + + return $next($request); + } + ); + } + + + /** + * Store a newly created resource in storage. + * + * @param StoreRequest $request + * + * @return JsonResponse + * @throws FireflyException + * + */ + public function store(StoreRequest $request, Budget $budget): JsonResponse + { + $data = $request->getAll(); + $data['start_date'] = $data['start']; + $data['end_date'] = $data['end']; + $data['budget_id'] = $budget->id; + + $budgetLimit = $this->blRepository->store($data); + $manager = $this->getManager(); + + + /** @var BudgetLimitTransformer $transformer */ + $transformer = app(BudgetLimitTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($budgetLimit, $transformer, 'budget_limits'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php b/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php new file mode 100644 index 0000000000..bad2d4418b --- /dev/null +++ b/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php @@ -0,0 +1,94 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\BudgetLimit; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\BudgetLimit\UpdateRequest; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Budget; +use FireflyIII\Models\BudgetLimit; +use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Transformers\BudgetLimitTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class UpdateController + */ +class UpdateController extends Controller +{ + private BudgetLimitRepositoryInterface $blRepository; + + + /** + * BudgetLimitController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->blRepository = app(BudgetLimitRepositoryInterface::class); + $this->blRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Update the specified resource in storage. + * + * @param UpdateRequest $request + * @param Budget $budget + * @param BudgetLimit $budgetLimit + * + * @return JsonResponse + */ + public function update(UpdateRequest $request, Budget $budget, BudgetLimit $budgetLimit): JsonResponse + { + + if ((int)$budget->id !== (int)$budgetLimit->budget_id) { + throw new FireflyException('20028: The budget limit does not belong to the budget.'); + } + $data = $request->getAll(); + $data['budget_id'] = $budget->id; + $budgetLimit = $this->blRepository->update($budgetLimit, $data); + $manager = $this->getManager(); + + /** @var BudgetLimitTransformer $transformer */ + $transformer = app(BudgetLimitTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($budgetLimit, $transformer, 'budget_limits'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Category/DestroyController.php b/app/Api/V1/Controllers/Models/Category/DestroyController.php new file mode 100644 index 0000000000..b0ad5d5e0a --- /dev/null +++ b/app/Api/V1/Controllers/Models/Category/DestroyController.php @@ -0,0 +1,78 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Category; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\Category; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; + +/** + * Class DestroyController + */ +class DestroyController extends Controller +{ + private CategoryRepositoryInterface $repository; + + + /** + * CategoryController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $admin */ + $admin = auth()->user(); + + /** @var CategoryRepositoryInterface repository */ + $this->repository = app(CategoryRepositoryInterface::class); + $this->repository->setUser($admin); + + return $next($request); + } + ); + } + + /** + * Remove the specified resource from storage. + * + * @param Category $category + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function destroy(Category $category): JsonResponse + { + $this->repository->destroy($category); + + return response()->json([], 204); + } + + + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/CategoryController.php b/app/Api/V1/Controllers/Models/Category/ListController.php similarity index 54% rename from app/Api/V1/Controllers/CategoryController.php rename to app/Api/V1/Controllers/Models/Category/ListController.php index 1fa628cbcf..5f6559b588 100644 --- a/app/Api/V1/Controllers/CategoryController.php +++ b/app/Api/V1/Controllers/Models/Category/ListController.php @@ -1,7 +1,7 @@ . */ -declare(strict_types=1); +namespace FireflyIII\Api\V1\Controllers\Models\Category; -namespace FireflyIII\Api\V1\Controllers; -use FireflyIII\Api\V1\Requests\CategoryStoreRequest; -use FireflyIII\Api\V1\Requests\CategoryUpdateRequest; -use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Category; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Transformers\AttachmentTransformer; -use FireflyIII\Transformers\CategoryTransformer; use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; @@ -39,16 +35,15 @@ use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; -use League\Fractal\Resource\Item; /** - * Class CategoryController. + * Class ListController */ -class CategoryController extends Controller +class ListController extends Controller { use TransactionFilter; - private CategoryRepositoryInterface $repository; + private CategoryRepositoryInterface $repository; /** * CategoryController constructor. @@ -72,6 +67,7 @@ class CategoryController extends Controller ); } + /** * @param Category $category * @@ -81,7 +77,7 @@ class CategoryController extends Controller public function attachments(Category $category): JsonResponse { $manager = $this->getManager(); - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $collection = $this->repository->getAttachments($category); $count = $collection->count(); @@ -101,97 +97,6 @@ class CategoryController extends Controller return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } - /** - * Remove the specified resource from storage. - * - * @param Category $category - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function delete(Category $category): JsonResponse - { - $this->repository->destroy($category); - - return response()->json([], 204); - } - - /** - * Display a listing of the resource. - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function index(): JsonResponse - { - $manager = $this->getManager(); - - // types to get, page size: - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - - // get list of budgets. Count it and split it. - $collection = $this->repository->getCategories(); - $count = $collection->count(); - $categories = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); - - // make paginator: - $paginator = new LengthAwarePaginator($categories, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.categories.index') . $this->buildParams()); - - /** @var CategoryTransformer $transformer */ - $transformer = app(CategoryTransformer::class); - $transformer->setParameters($this->parameters); - - - $resource = new FractalCollection($categories, $transformer, 'categories'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - - /** - * Show the category. - * - * @param Category $category - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function show(Category $category): JsonResponse - { - $manager = $this->getManager(); - - /** @var CategoryTransformer $transformer */ - $transformer = app(CategoryTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($category, $transformer, 'categories'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Store new category. - * - * @param CategoryStoreRequest $request - * - * @return JsonResponse - * @throws FireflyException - */ - public function store(CategoryStoreRequest $request): JsonResponse - { - $category = $this->repository->store($request->getAll()); - $manager = $this->getManager(); - - /** @var CategoryTransformer $transformer */ - $transformer = app(CategoryTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($category, $transformer, 'categories'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } /** * Show all transactions. @@ -205,7 +110,7 @@ class CategoryController extends Controller */ public function transactions(Request $request, Category $category): JsonResponse { - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; $type = $request->get('type') ?? 'default'; $this->parameters->set('type', $type); @@ -249,28 +154,4 @@ class CategoryController extends Controller return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } - /** - * Update the category. - * - * @param CategoryUpdateRequest $request - * @param Category $category - * - * @return JsonResponse - */ - public function update(CategoryUpdateRequest $request, Category $category): JsonResponse - { - $data = $request->getAll(); - $category = $this->repository->update($category, $data); - $manager = $this->getManager(); - - /** @var CategoryTransformer $transformer */ - $transformer = app(CategoryTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($category, $transformer, 'categories'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - -} +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Category/ShowController.php b/app/Api/V1/Controllers/Models/Category/ShowController.php new file mode 100644 index 0000000000..e7b5e267b6 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Category/ShowController.php @@ -0,0 +1,119 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Category; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\Category; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Transformers\CategoryTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; + +/** + * Class ShowController + */ +class ShowController extends Controller +{ + private CategoryRepositoryInterface $repository; + + /** + * CategoryController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $admin */ + $admin = auth()->user(); + + /** @var CategoryRepositoryInterface repository */ + $this->repository = app(CategoryRepositoryInterface::class); + $this->repository->setUser($admin); + + return $next($request); + } + ); + } + + + /** + * Display a listing of the resource. + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function index(): JsonResponse + { + $manager = $this->getManager(); + + // types to get, page size: + $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of budgets. Count it and split it. + $collection = $this->repository->getCategories(); + $count = $collection->count(); + $categories = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($categories, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.categories.index') . $this->buildParams()); + + /** @var CategoryTransformer $transformer */ + $transformer = app(CategoryTransformer::class); + $transformer->setParameters($this->parameters); + + + $resource = new FractalCollection($categories, $transformer, 'categories'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + /** + * Show the category. + * + * @param Category $category + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function show(Category $category): JsonResponse + { + $manager = $this->getManager(); + + /** @var CategoryTransformer $transformer */ + $transformer = app(CategoryTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($category, $transformer, 'categories'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Category/StoreController.php b/app/Api/V1/Controllers/Models/Category/StoreController.php new file mode 100644 index 0000000000..3829646f39 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Category/StoreController.php @@ -0,0 +1,86 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Category; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\Category\StoreRequest; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Transformers\CategoryTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class StoreController + */ +class StoreController extends Controller +{ + private CategoryRepositoryInterface $repository; + + + /** + * CategoryController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $admin */ + $admin = auth()->user(); + + /** @var CategoryRepositoryInterface repository */ + $this->repository = app(CategoryRepositoryInterface::class); + $this->repository->setUser($admin); + + return $next($request); + } + ); + } + + /** + * Store new category. + * + * @param StoreRequest $request + * + * @return JsonResponse + * @throws FireflyException + */ + public function store(StoreRequest $request): JsonResponse + { + $category = $this->repository->store($request->getAll()); + $manager = $this->getManager(); + + /** @var CategoryTransformer $transformer */ + $transformer = app(CategoryTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($category, $transformer, 'categories'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Category/UpdateController.php b/app/Api/V1/Controllers/Models/Category/UpdateController.php new file mode 100644 index 0000000000..565def622b --- /dev/null +++ b/app/Api/V1/Controllers/Models/Category/UpdateController.php @@ -0,0 +1,86 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Category; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\Category\UpdateRequest; +use FireflyIII\Models\Category; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Transformers\CategoryTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class UpdateController + */ +class UpdateController extends Controller +{ + private CategoryRepositoryInterface $repository; + + /** + * CategoryController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $admin */ + $admin = auth()->user(); + + /** @var CategoryRepositoryInterface repository */ + $this->repository = app(CategoryRepositoryInterface::class); + $this->repository->setUser($admin); + + return $next($request); + } + ); + } + + /** + * Update the category. + * + * @param UpdateRequest $request + * @param Category $category + * + * @return JsonResponse + */ + public function update(UpdateRequest $request, Category $category): JsonResponse + { + $data = $request->getAll(); + $category = $this->repository->update($category, $data); + $manager = $this->getManager(); + + /** @var CategoryTransformer $transformer */ + $transformer = app(CategoryTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($category, $transformer, 'categories'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/ObjectGroup/DestroyController.php b/app/Api/V1/Controllers/Models/ObjectGroup/DestroyController.php new file mode 100644 index 0000000000..180c4127e3 --- /dev/null +++ b/app/Api/V1/Controllers/Models/ObjectGroup/DestroyController.php @@ -0,0 +1,73 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\ObjectGroup; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\ObjectGroup; +use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; + +/** + * Class DestroyController + */ +class DestroyController extends Controller +{ + private ObjectGroupRepositoryInterface $repository; + + + /** + * ObjectGroupController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->repository = app(ObjectGroupRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Remove the specified resource from storage. + * + * @param ObjectGroup $objectGroup + * + * @codeCoverageIgnore + * @return JsonResponse + */ + public function destroy(ObjectGroup $objectGroup): JsonResponse + { + $this->repository->destroy($objectGroup); + + return response()->json([], 204); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/ObjectGroup/ListController.php b/app/Api/V1/Controllers/Models/ObjectGroup/ListController.php new file mode 100644 index 0000000000..258e641ce7 --- /dev/null +++ b/app/Api/V1/Controllers/Models/ObjectGroup/ListController.php @@ -0,0 +1,132 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\ObjectGroup; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\ObjectGroup; +use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface; +use FireflyIII\Transformers\BillTransformer; +use FireflyIII\Transformers\PiggyBankTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; + +/** + * Class ListController + */ +class ListController extends Controller +{ + private ObjectGroupRepositoryInterface $repository; + + + /** + * ObjectGroupController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->repository = app(ObjectGroupRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + + /** + * List all piggies under the object group. + * + * @param ObjectGroup $objectGroup + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function piggyBanks(ObjectGroup $objectGroup): JsonResponse + { + // create some objects: + $manager = $this->getManager(); + + // types to get, page size: + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of piggy banks. Count it and split it. + $collection = $this->repository->getPiggyBanks($objectGroup); + $count = $collection->count(); + $piggyBanks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.object-groups.piggy_banks', [$objectGroup->id]) . $this->buildParams()); + + /** @var PiggyBankTransformer $transformer */ + $transformer = app(PiggyBankTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($piggyBanks, $transformer, 'piggy_banks'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } + + /** + * List all bills + * + * @param ObjectGroup $objectGroup + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function bills(ObjectGroup $objectGroup): JsonResponse + { + $manager = $this->getManager(); + + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + // get list of piggy banks. Count it and split it. + $collection = $this->repository->getBills($objectGroup); + $count = $collection->count(); + $bills = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($bills, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.currencies.bills', [$objectGroup->id]) . $this->buildParams()); + + /** @var BillTransformer $transformer */ + $transformer = app(BillTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($bills, $transformer, 'bills'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/ObjectGroupController.php b/app/Api/V1/Controllers/Models/ObjectGroup/ShowController.php similarity index 53% rename from app/Api/V1/Controllers/ObjectGroupController.php rename to app/Api/V1/Controllers/Models/ObjectGroup/ShowController.php index ae6ad31dfc..267c18d8fc 100644 --- a/app/Api/V1/Controllers/ObjectGroupController.php +++ b/app/Api/V1/Controllers/Models/ObjectGroup/ShowController.php @@ -1,7 +1,7 @@ . */ -declare(strict_types=1); +namespace FireflyIII\Api\V1\Controllers\Models\ObjectGroup; -namespace FireflyIII\Api\V1\Controllers; -use FireflyIII\Api\V1\Requests\ObjectGroupUpdateRequest; -use FireflyIII\Models\Account; +use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Models\ObjectGroup; use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface; use FireflyIII\Transformers\ObjectGroupTransformer; -use FireflyIII\Transformers\PiggyBankTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -38,9 +35,9 @@ use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Item; /** - * Class GroupController. + * Class ShowController */ -class ObjectGroupController extends Controller +class ShowController extends Controller { private ObjectGroupRepositoryInterface $repository; @@ -65,20 +62,6 @@ class ObjectGroupController extends Controller ); } - /** - * Remove the specified resource from storage. - * - * @param ObjectGroup $objectGroup - * - * @codeCoverageIgnore - * @return JsonResponse - */ - public function delete(ObjectGroup $objectGroup): JsonResponse - { - $this->repository->destroy($objectGroup); - - return response()->json([], 204); - } /** * Display a listing of the resource. @@ -93,9 +76,9 @@ class ObjectGroupController extends Controller $manager = $this->getManager(); // types to get, page size: - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - // get list of accounts. Count it and split it. + $this->repository->resetOrder(); $collection = $this->repository->get(); $count = $collection->count(); $objectGroups = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); @@ -115,42 +98,6 @@ class ObjectGroupController extends Controller } - /** - * List all piggies under the object group. - * - * @param ObjectGroup $objectGroup - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function piggyBanks(ObjectGroup $objectGroup): JsonResponse - { - // create some objects: - $manager = $this->getManager(); - - // types to get, page size: - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - - // get list of piggy banks. Count it and split it. - $collection = $this->repository->getPiggyBanks($objectGroup); - $count = $collection->count(); - $piggyBanks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); - - // make paginator: - $paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.object-groups.piggy_banks', [$objectGroup->id]) . $this->buildParams()); - - /** @var PiggyBankTransformer $transformer */ - $transformer = app(PiggyBankTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($piggyBanks, $transformer, 'piggy_banks'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - /** * Show single instance. * @@ -161,6 +108,8 @@ class ObjectGroupController extends Controller public function show(ObjectGroup $objectGroup): JsonResponse { $manager = $this->getManager(); + $this->repository->resetOrder(); + $objectGroup->refresh(); /** @var ObjectGroupTransformer $transformer */ $transformer = app(ObjectGroupTransformer::class); @@ -170,26 +119,5 @@ class ObjectGroupController extends Controller return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } - /** - * Update object. - * - * @param ObjectGroupUpdateRequest $request - * @param Account $account - * - * @return JsonResponse - */ - public function update(ObjectGroupUpdateRequest $request, ObjectGroup $objectGroup): JsonResponse - { - $data = $request->getUpdateData(); - $this->repository->update($objectGroup, $data); - $this->repository->sort(); - $manager = $this->getManager(); - /** @var ObjectGroupTransformer $transformer */ - $transformer = app(ObjectGroupTransformer::class); - $transformer->setParameters($this->parameters); - $resource = new Item($objectGroup, $transformer, 'object_groups'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } -} +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/ObjectGroup/UpdateController.php b/app/Api/V1/Controllers/Models/ObjectGroup/UpdateController.php new file mode 100644 index 0000000000..87842525d8 --- /dev/null +++ b/app/Api/V1/Controllers/Models/ObjectGroup/UpdateController.php @@ -0,0 +1,83 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\ObjectGroup; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\ObjectGroup\UpdateRequest; +use FireflyIII\Models\ObjectGroup; +use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface; +use FireflyIII\Transformers\ObjectGroupTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class UpdateController + */ +class UpdateController extends Controller +{ + private ObjectGroupRepositoryInterface $repository; + + + /** + * ObjectGroupController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->repository = app(ObjectGroupRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + + /** + * @param UpdateRequest $request + * @param ObjectGroup $objectGroup + * + * @return JsonResponse + */ + public function update(UpdateRequest $request, ObjectGroup $objectGroup): JsonResponse + { + $data = $request->getUpdateData(); + $this->repository->update($objectGroup, $data); + $this->repository->resetOrder(); + $manager = $this->getManager(); + + /** @var ObjectGroupTransformer $transformer */ + $transformer = app(ObjectGroupTransformer::class); + $transformer->setParameters($this->parameters); + $resource = new Item($objectGroup, $transformer, 'object_groups'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/PiggyBank/DestroyController.php b/app/Api/V1/Controllers/Models/PiggyBank/DestroyController.php new file mode 100644 index 0000000000..99fdc7c03e --- /dev/null +++ b/app/Api/V1/Controllers/Models/PiggyBank/DestroyController.php @@ -0,0 +1,70 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\PiggyBank; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\PiggyBank; +use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; +use Illuminate\Http\JsonResponse; + +/** + * Class DestroyController + */ +class DestroyController extends Controller +{ + private PiggyBankRepositoryInterface $repository; + + /** + * Constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->repository = app(PiggyBankRepositoryInterface::class); + $this->repository->setUser(auth()->user()); + + + return $next($request); + } + ); + } + + /** + * Delete the resource. + * + * @param PiggyBank $piggyBank + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function destroy(PiggyBank $piggyBank): JsonResponse + { + $this->repository->destroy($piggyBank); + + return response()->json([], 204); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/PiggyBank/ListController.php b/app/Api/V1/Controllers/Models/PiggyBank/ListController.php new file mode 100644 index 0000000000..d4b65206f0 --- /dev/null +++ b/app/Api/V1/Controllers/Models/PiggyBank/ListController.php @@ -0,0 +1,122 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\PiggyBank; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\PiggyBank; +use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; +use FireflyIII\Transformers\AttachmentTransformer; +use FireflyIII\Transformers\PiggyBankEventTransformer; +use Illuminate\Http\JsonResponse; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; + +/** + * Class ListController + */ +class ListController extends Controller +{ + private PiggyBankRepositoryInterface $repository; + + /** + * Constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->repository = app(PiggyBankRepositoryInterface::class); + $this->repository->setUser(auth()->user()); + + + return $next($request); + } + ); + } + + /** + * @param PiggyBank $piggyBank + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function attachments(PiggyBank $piggyBank): JsonResponse + { + $manager = $this->getManager(); + $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $collection = $this->repository->getAttachments($piggyBank); + + $count = $collection->count(); + $attachments = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.piggy_banks.attachments', [$piggyBank->id]) . $this->buildParams()); + + /** @var AttachmentTransformer $transformer */ + $transformer = app(AttachmentTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($attachments, $transformer, 'attachments'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + /** + * List single resource. + * + * @param PiggyBank $piggyBank + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function piggyBankEvents(PiggyBank $piggyBank): JsonResponse + { + // types to get, page size: + $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $manager = $this->getManager(); + + $collection = $this->repository->getEvents($piggyBank); + $count = $collection->count(); + $events = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($events, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.piggy_banks.events', [$piggyBank->id]) . $this->buildParams()); + + /** @var PiggyBankEventTransformer $transformer */ + $transformer = app(PiggyBankEventTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($events, $transformer, 'piggy_bank_events'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php b/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php new file mode 100644 index 0000000000..f9d5ddd584 --- /dev/null +++ b/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php @@ -0,0 +1,114 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\PiggyBank; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\PiggyBank; +use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; +use FireflyIII\Transformers\PiggyBankTransformer; +use Illuminate\Http\JsonResponse; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; + +/** + * Class ShowController + */ +class ShowController extends Controller +{ + private PiggyBankRepositoryInterface $repository; + + /** + * Constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->repository = app(PiggyBankRepositoryInterface::class); + $this->repository->setUser(auth()->user()); + + + return $next($request); + } + ); + } + + /** + * List all of them. + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function index(): JsonResponse + { + $manager = $this->getManager(); + // types to get, page size: + $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of budgets. Count it and split it. + $collection = $this->repository->getPiggyBanks(); + $count = $collection->count(); + $piggyBanks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.piggy_banks.index') . $this->buildParams()); + + /** @var PiggyBankTransformer $transformer */ + $transformer = app(PiggyBankTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($piggyBanks, $transformer, 'piggy_banks'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } + + /** + * List single resource. + * + * @param PiggyBank $piggyBank + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function show(PiggyBank $piggyBank): JsonResponse + { + $manager = $this->getManager(); + + /** @var PiggyBankTransformer $transformer */ + $transformer = app(PiggyBankTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($piggyBank, $transformer, 'piggy_banks'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/PiggyBank/StoreController.php b/app/Api/V1/Controllers/Models/PiggyBank/StoreController.php new file mode 100644 index 0000000000..f0517961c4 --- /dev/null +++ b/app/Api/V1/Controllers/Models/PiggyBank/StoreController.php @@ -0,0 +1,80 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\PiggyBank; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\PiggyBank\StoreRequest; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; +use FireflyIII\Transformers\PiggyBankTransformer; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class StoreController + */ +class StoreController extends Controller +{ + private PiggyBankRepositoryInterface $repository; + + /** + * Constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->repository = app(PiggyBankRepositoryInterface::class); + $this->repository->setUser(auth()->user()); + + + return $next($request); + } + ); + } + + /** + * Store new object. + * + * @param StoreRequest $request + * + * @return JsonResponse + * @throws FireflyException + */ + public function store(StoreRequest $request): JsonResponse + { + $piggyBank = $this->repository->store($request->getAll()); + $manager = $this->getManager(); + + /** @var PiggyBankTransformer $transformer */ + $transformer = app(PiggyBankTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($piggyBank, $transformer, 'piggy_banks'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/PiggyBank/UpdateController.php b/app/Api/V1/Controllers/Models/PiggyBank/UpdateController.php new file mode 100644 index 0000000000..718fdaa206 --- /dev/null +++ b/app/Api/V1/Controllers/Models/PiggyBank/UpdateController.php @@ -0,0 +1,86 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\PiggyBank; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\PiggyBank\UpdateRequest; +use FireflyIII\Models\PiggyBank; +use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; +use FireflyIII\Transformers\PiggyBankTransformer; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class UpdateController + */ +class UpdateController extends Controller +{ + private PiggyBankRepositoryInterface $repository; + + /** + * Constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->repository = app(PiggyBankRepositoryInterface::class); + $this->repository->setUser(auth()->user()); + + + return $next($request); + } + ); + } + + /** + * Update piggy bank. + * + * @param UpdateRequest $request + * @param PiggyBank $piggyBank + * + * @return JsonResponse + */ + public function update(UpdateRequest $request, PiggyBank $piggyBank): JsonResponse + { + $data = $request->getAll(); + $piggyBank = $this->repository->update($piggyBank, $data); + + if (array_key_exists('current_amount', $data) && '' !== $data['current_amount']) { + $this->repository->setCurrentAmount($piggyBank, $data['current_amount']); + } + + $manager = $this->getManager(); + /** @var PiggyBankTransformer $transformer */ + $transformer = app(PiggyBankTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($piggyBank, $transformer, 'piggy_banks'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Recurrence/DestroyController.php b/app/Api/V1/Controllers/Models/Recurrence/DestroyController.php new file mode 100644 index 0000000000..573c996762 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Recurrence/DestroyController.php @@ -0,0 +1,75 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Recurrence; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\Recurrence; +use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; + +/** + * Class DestroyController + */ +class DestroyController extends Controller +{ + private RecurringRepositoryInterface $repository; + + /** + * RecurrenceController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + /** @var RecurringRepositoryInterface repository */ + $this->repository = app(RecurringRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + + /** + * Delete the resource. + * + * @param Recurrence $recurrence + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function destroy(Recurrence $recurrence): JsonResponse + { + $this->repository->destroy($recurrence); + + return response()->json([], 204); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Recurrence/ListController.php b/app/Api/V1/Controllers/Models/Recurrence/ListController.php new file mode 100644 index 0000000000..b84b2ae63e --- /dev/null +++ b/app/Api/V1/Controllers/Models/Recurrence/ListController.php @@ -0,0 +1,123 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Recurrence; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; +use FireflyIII\Models\Recurrence; +use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; +use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Transformers\TransactionGroupTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; + +/** + * Class ListController + */ +class ListController extends Controller +{ + use TransactionFilter; + + private RecurringRepositoryInterface $repository; + + /** + * RecurrenceController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + /** @var RecurringRepositoryInterface repository */ + $this->repository = app(RecurringRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Show transactions for this recurrence. + * + * @param Request $request + * @param Recurrence $recurrence + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function transactions(Request $request, Recurrence $recurrence): JsonResponse + { + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $type = $request->get('type') ?? 'default'; + $this->parameters->set('type', $type); + + $types = $this->mapTransactionTypes($this->parameters->get('type')); + $manager = $this->getManager(); + // whatever is returned by the query, it must be part of these journals: + $journalIds = $this->repository->getJournalIds($recurrence); + + /** @var User $admin */ + $admin = auth()->user(); + + // use new group collector: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector + ->setUser($admin) + // filter on journal IDs. + ->setJournalIds($journalIds) + // all info needed for the API: + ->withAPIInformation() + // set page size: + ->setLimit($pageSize) + // set page to retrieve + ->setPage($this->parameters->get('page')) + // set types of transactions to return. + ->setTypes($types); + + if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { + $collector->setRange($this->parameters->get('start'), $this->parameters->get('end')); + } + $paginator = $collector->getPaginatedGroups(); + $paginator->setPath(route('api.v1.transactions.index') . $this->buildParams()); + $transactions = $paginator->getCollection(); + + /** @var TransactionGroupTransformer $transformer */ + $transformer = app(TransactionGroupTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($transactions, $transformer, 'transactions'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Recurrence/ShowController.php b/app/Api/V1/Controllers/Models/Recurrence/ShowController.php new file mode 100644 index 0000000000..f776df6626 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Recurrence/ShowController.php @@ -0,0 +1,120 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Recurrence; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\Recurrence; +use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; +use FireflyIII\Transformers\RecurrenceTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; + +/** + * Class ShowController + */ +class ShowController extends Controller +{ + private RecurringRepositoryInterface $repository; + + /** + * RecurrenceController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + /** @var RecurringRepositoryInterface repository */ + $this->repository = app(RecurringRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + /** + * List all of them. + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function index(): JsonResponse + { + $manager = $this->getManager(); + + // types to get, page size: + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of budgets. Count it and split it. + $collection = $this->repository->getAll(); + $count = $collection->count(); + $piggyBanks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.recurrences.index') . $this->buildParams()); + + /** @var RecurrenceTransformer $transformer */ + $transformer = app(RecurrenceTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($piggyBanks, $transformer, 'recurrences'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } + + /** + * List single resource. + * + * @param Recurrence $recurrence + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function show(Recurrence $recurrence): JsonResponse + { + $manager = $this->getManager(); + + /** @var RecurrenceTransformer $transformer */ + $transformer = app(RecurrenceTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($recurrence, $transformer, 'recurrences'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Recurrence/StoreController.php b/app/Api/V1/Controllers/Models/Recurrence/StoreController.php new file mode 100644 index 0000000000..af84688bf8 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Recurrence/StoreController.php @@ -0,0 +1,85 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Recurrence; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\Recurrence\StoreRequest; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; +use FireflyIII\Transformers\RecurrenceTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class StoreController + */ +class StoreController extends Controller +{ + private RecurringRepositoryInterface $repository; + + /** + * RecurrenceController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + /** @var RecurringRepositoryInterface repository */ + $this->repository = app(RecurringRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Store new object. + * + * @param StoreRequest $request + * + * @return JsonResponse + * @throws FireflyException + */ + public function store(StoreRequest $request): JsonResponse + { + $data = $request->getAll(); + $recurrence = $this->repository->store($data); + $manager = $this->getManager(); + + /** @var RecurrenceTransformer $transformer */ + $transformer = app(RecurrenceTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($recurrence, $transformer, 'recurrences'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Recurrence/TriggerController.php b/app/Api/V1/Controllers/Models/Recurrence/TriggerController.php new file mode 100644 index 0000000000..7735076232 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Recurrence/TriggerController.php @@ -0,0 +1,86 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Recurrence; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; +use FireflyIII\Support\Cronjobs\RecurringCronjob; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Log; + +/** + * Class TriggerController + */ +class TriggerController extends Controller +{ + private RecurringRepositoryInterface $repository; + + /** + * RecurrenceController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + /** @var RecurringRepositoryInterface repository */ + $this->repository = app(RecurringRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + + /** + * @return JsonResponse + * @throws FireflyException + * @codeCoverageIgnore + * + * TODO currently unused + unreachable. + */ + public function trigger(): JsonResponse + { + /** @var RecurringCronjob $recurring */ + $recurring = app(RecurringCronjob::class); + try { + $result = $recurring->fire(); + } catch (FireflyException $e) { + Log::error($e->getMessage()); + throw new FireflyException('200022: Error in cron job.', 0, $e); + } + if (false === $result) { + return response()->json([], 204); + } + + return response()->json(); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Recurrence/UpdateController.php b/app/Api/V1/Controllers/Models/Recurrence/UpdateController.php new file mode 100644 index 0000000000..5c988790f4 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Recurrence/UpdateController.php @@ -0,0 +1,88 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Recurrence; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\Recurrence\UpdateRequest; +use FireflyIII\Models\Recurrence; +use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; +use FireflyIII\Transformers\RecurrenceTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class UpdateController + */ +class UpdateController extends Controller +{ + private RecurringRepositoryInterface $repository; + + /** + * RecurrenceController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + /** @var RecurringRepositoryInterface repository */ + $this->repository = app(RecurringRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + + + /** + * Update single recurrence. + * + * @param UpdateRequest $request + * @param Recurrence $recurrence + * + * @return JsonResponse + */ + public function update(UpdateRequest $request, Recurrence $recurrence): JsonResponse + { + $data = $request->getAll(); + $recurrence = $this->repository->update($recurrence, $data); + $manager = $this->getManager(); + + /** @var RecurrenceTransformer $transformer */ + $transformer = app(RecurrenceTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($recurrence, $transformer, 'recurrences'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Rule/DestroyController.php b/app/Api/V1/Controllers/Models/Rule/DestroyController.php new file mode 100644 index 0000000000..237f44621a --- /dev/null +++ b/app/Api/V1/Controllers/Models/Rule/DestroyController.php @@ -0,0 +1,74 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Rule; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\Rule; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Rule\RuleRepositoryInterface; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; + +/** + * Class DestroyController + */ +class DestroyController extends Controller +{ + private RuleRepositoryInterface $ruleRepository; + + /** + * RuleController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->ruleRepository = app(RuleRepositoryInterface::class); + $this->ruleRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Delete the resource. + * + * @param Rule $rule + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function destroy(Rule $rule): JsonResponse + { + $this->ruleRepository->destroy($rule); + + return response()->json([], 204); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Rule/ListController.php b/app/Api/V1/Controllers/Models/Rule/ListController.php new file mode 100644 index 0000000000..86a9a99a44 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Rule/ListController.php @@ -0,0 +1,62 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Rule; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Rule\RuleRepositoryInterface; +use FireflyIII\User; + +/** + * Class ListController + */ +class ListController extends Controller +{ + private AccountRepositoryInterface $accountRepository; + private RuleRepositoryInterface $ruleRepository; + + + /** + * RuleController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->ruleRepository = app(RuleRepositoryInterface::class); + $this->ruleRepository->setUser($user); + + $this->accountRepository = app(AccountRepositoryInterface::class); + $this->accountRepository->setUser($user); + + return $next($request); + } + ); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Rule/ShowController.php b/app/Api/V1/Controllers/Models/Rule/ShowController.php new file mode 100644 index 0000000000..6a46329d62 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Rule/ShowController.php @@ -0,0 +1,121 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Rule; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\Rule; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Rule\RuleRepositoryInterface; +use FireflyIII\Transformers\RuleTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; + +/** + * Class ShowController + */ +class ShowController extends Controller +{ + private RuleRepositoryInterface $ruleRepository; + + /** + * RuleController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->ruleRepository = app(RuleRepositoryInterface::class); + $this->ruleRepository->setUser($user); + + return $next($request); + } + ); + } + + + /** + * List all of them. + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function index(): JsonResponse + { + $manager = $this->getManager(); + + // types to get, page size: + $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of budgets. Count it and split it. + $collection = $this->ruleRepository->getAll(); + $count = $collection->count(); + $rules = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.rules.index') . $this->buildParams()); + + /** @var RuleTransformer $transformer */ + $transformer = app(RuleTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($rules, $transformer, 'rules'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } + + + + /** + * List single resource. + * + * @param Rule $rule + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function show(Rule $rule): JsonResponse + { + $manager = $this->getManager(); + /** @var RuleTransformer $transformer */ + $transformer = app(RuleTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($rule, $transformer, 'rules'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Rule/StoreController.php b/app/Api/V1/Controllers/Models/Rule/StoreController.php new file mode 100644 index 0000000000..b3f54c542f --- /dev/null +++ b/app/Api/V1/Controllers/Models/Rule/StoreController.php @@ -0,0 +1,80 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Rule; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\Rule\StoreRequest; +use FireflyIII\Repositories\Rule\RuleRepositoryInterface; +use FireflyIII\Transformers\RuleTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class StoreController + */ +class StoreController extends Controller +{ + private RuleRepositoryInterface $ruleRepository; + + /** + * RuleController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->ruleRepository = app(RuleRepositoryInterface::class); + $this->ruleRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Store new object. + * + * @param StoreRequest $request + * + * @return JsonResponse + */ + public function store(StoreRequest $request): JsonResponse + { + $rule = $this->ruleRepository->store($request->getAll()); + $manager = $this->getManager(); + /** @var RuleTransformer $transformer */ + $transformer = app(RuleTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($rule, $transformer, 'rules'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Rule/TriggerController.php b/app/Api/V1/Controllers/Models/Rule/TriggerController.php new file mode 100644 index 0000000000..32d5c76b8d --- /dev/null +++ b/app/Api/V1/Controllers/Models/Rule/TriggerController.php @@ -0,0 +1,152 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Rule; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\Rule\TestRequest; +use FireflyIII\Api\V1\Requests\Models\Rule\TriggerRequest; +use FireflyIII\Models\Rule; +use FireflyIII\Repositories\Rule\RuleRepositoryInterface; +use FireflyIII\TransactionRules\Engine\RuleEngineInterface; +use FireflyIII\Transformers\TransactionGroupTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Pagination\LengthAwarePaginator; +use Illuminate\Support\Collection; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; + +/** + * Class TriggerController + */ +class TriggerController extends Controller +{ + private RuleRepositoryInterface $ruleRepository; + + + /** + * RuleController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->ruleRepository = app(RuleRepositoryInterface::class); + $this->ruleRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * @param TestRequest $request + * @param Rule $rule + * + * @return JsonResponse + */ + public function testRule(TestRequest $request, Rule $rule): JsonResponse + { + $parameters = $request->getTestParameters(); + + /** @var RuleEngineInterface $ruleEngine */ + $ruleEngine = app(RuleEngineInterface::class); + $ruleEngine->setRules(new Collection([$rule])); + + // overrule the rule(s) if necessary. + if (array_key_exists('start', $parameters) && null !== $parameters['start']) { + // add a range: + $ruleEngine->addOperator(['type' => 'date_after', 'value' => $parameters['start']->format('Y-m-d')]); + } + + if (array_key_exists('end', $parameters) && null !== $parameters['end']) { + // add a range: + $ruleEngine->addOperator(['type' => 'date_before', 'value' => $parameters['end']->format('Y-m-d')]); + } + if (array_key_exists('accounts', $parameters) && '' !== $parameters['accounts']) { + $ruleEngine->addOperator(['type' => 'account_id', 'value' => implode(',', $parameters['accounts'])]); + } + + // file the rule(s) + $transactions = $ruleEngine->find(); + $count = $transactions->count(); + + $paginator = new LengthAwarePaginator($transactions, $count, 31337, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.rules.test', [$rule->id]) . $this->buildParams()); + + // resulting list is presented as JSON thing. + $manager = $this->getManager(); + /** @var TransactionGroupTransformer $transformer */ + $transformer = app(TransactionGroupTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($transactions, $transformer, 'transactions'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + + /** + * Execute the given rule group on a set of existing transactions. + * + * @param TriggerRequest $request + * @param Rule $rule + * + * @return JsonResponse + */ + public function triggerRule(TriggerRequest $request, Rule $rule): JsonResponse + { + // Get parameters specified by the user + $parameters = $request->getTriggerParameters(); + + /** @var RuleEngineInterface $ruleEngine */ + $ruleEngine = app(RuleEngineInterface::class); + $ruleEngine->setRules(new Collection([$rule])); + + // overrule the rule(s) if necessary. + if (array_key_exists('start', $parameters) && null !== $parameters['start']) { + // add a range: + $ruleEngine->addOperator(['type' => 'date_after', 'value' => $parameters['start']->format('Y-m-d')]); + } + + if (array_key_exists('end', $parameters) && null !== $parameters['end']) { + // add a range: + $ruleEngine->addOperator(['type' => 'date_before', 'value' => $parameters['end']->format('Y-m-d')]); + } + if (array_key_exists('accounts', $parameters) && '' !== $parameters['accounts']) { + $ruleEngine->addOperator(['type' => 'account_id', 'value' => implode(',', $parameters['accounts'])]); + } + + // file the rule(s) + $ruleEngine->fire(); + + return response()->json([], 204); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Rule/UpdateController.php b/app/Api/V1/Controllers/Models/Rule/UpdateController.php new file mode 100644 index 0000000000..84d91db599 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Rule/UpdateController.php @@ -0,0 +1,86 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Rule; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\Rule\UpdateRequest; +use FireflyIII\Models\Rule; +use FireflyIII\Repositories\Rule\RuleRepositoryInterface; +use FireflyIII\Transformers\RuleTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class UpdateController + */ +class UpdateController extends Controller +{ + private RuleRepositoryInterface $ruleRepository; + + + /** + * RuleController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->ruleRepository = app(RuleRepositoryInterface::class); + $this->ruleRepository->setUser($user); + + return $next($request); + } + ); + } + + + /** + * Update a rule. + * + * @param UpdateRequest $request + * @param Rule $rule + * + * @return JsonResponse + */ + public function update(UpdateRequest $request, Rule $rule): JsonResponse + { + $data = $request->getAll(); + $rule = $this->ruleRepository->update($rule, $data); + $manager = $this->getManager(); + + /** @var RuleTransformer $transformer */ + $transformer = app(RuleTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($rule, $transformer, 'rules'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/RuleGroup/DestroyController.php b/app/Api/V1/Controllers/Models/RuleGroup/DestroyController.php new file mode 100644 index 0000000000..64d09952a5 --- /dev/null +++ b/app/Api/V1/Controllers/Models/RuleGroup/DestroyController.php @@ -0,0 +1,73 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\RuleGroup; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\RuleGroup; +use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; + +/** + * Class DestroyController + */ +class DestroyController extends Controller +{ + private RuleGroupRepositoryInterface $ruleGroupRepository; + + /** + * RuleGroupController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class); + $this->ruleGroupRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Delete the resource. + * + * @param RuleGroup $ruleGroup + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function destroy(RuleGroup $ruleGroup): JsonResponse + { + $this->ruleGroupRepository->destroy($ruleGroup, null); + + return response()->json([], 204); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/RuleGroup/ListController.php b/app/Api/V1/Controllers/Models/RuleGroup/ListController.php new file mode 100644 index 0000000000..2d8c5c712d --- /dev/null +++ b/app/Api/V1/Controllers/Models/RuleGroup/ListController.php @@ -0,0 +1,96 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\RuleGroup; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\RuleGroup; +use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; +use FireflyIII\Transformers\RuleTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; + +/** + * Class ListController + */ +class ListController extends Controller +{ + private RuleGroupRepositoryInterface $ruleGroupRepository; + + + /** + * RuleGroupController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class); + $this->ruleGroupRepository->setUser($user); + + return $next($request); + } + ); + } + + + /** + * @param RuleGroup $group + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function rules(RuleGroup $group): JsonResponse + { + $manager = $this->getManager(); + // types to get, page size: + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of budgets. Count it and split it. + $collection = $this->ruleGroupRepository->getRules($group); + $count = $collection->count(); + $rules = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.rule_groups.rules', [$group->id]) . $this->buildParams()); + + /** @var RuleTransformer $transformer */ + $transformer = app(RuleTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($rules, $transformer, 'rules'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/RuleGroup/ShowController.php b/app/Api/V1/Controllers/Models/RuleGroup/ShowController.php new file mode 100644 index 0000000000..40ae267b43 --- /dev/null +++ b/app/Api/V1/Controllers/Models/RuleGroup/ShowController.php @@ -0,0 +1,119 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\RuleGroup; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\RuleGroup; +use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; +use FireflyIII\Transformers\RuleGroupTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; + +/** + * Class ShowController + */ +class ShowController extends Controller +{ + private RuleGroupRepositoryInterface $ruleGroupRepository; + + + /** + * RuleGroupController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class); + $this->ruleGroupRepository->setUser($user); + + return $next($request); + } + ); + } + + + /** + * List all of them. + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function index(): JsonResponse + { + $manager = $this->getManager(); + // types to get, page size: + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of rule groups. Count it and split it. + $collection = $this->ruleGroupRepository->get(); + $count = $collection->count(); + $ruleGroups = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($ruleGroups, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.rule_groups.index') . $this->buildParams()); + + /** @var RuleGroupTransformer $transformer */ + $transformer = app(RuleGroupTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($ruleGroups, $transformer, 'rule_groups'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + + /** + * List single resource. + * + * @param RuleGroup $ruleGroup + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function show(RuleGroup $ruleGroup): JsonResponse + { + $manager = $this->getManager(); + /** @var RuleGroupTransformer $transformer */ + $transformer = app(RuleGroupTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($ruleGroup, $transformer, 'rule_groups'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/RuleGroup/StoreController.php b/app/Api/V1/Controllers/Models/RuleGroup/StoreController.php new file mode 100644 index 0000000000..61c5f3ea77 --- /dev/null +++ b/app/Api/V1/Controllers/Models/RuleGroup/StoreController.php @@ -0,0 +1,90 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\RuleGroup; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\RuleGroup\StoreRequest; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; +use FireflyIII\Transformers\RuleGroupTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class StoreController + */ +class StoreController extends Controller +{ + + private AccountRepositoryInterface $accountRepository; + private RuleGroupRepositoryInterface $ruleGroupRepository; + + + /** + * RuleGroupController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class); + $this->ruleGroupRepository->setUser($user); + + $this->accountRepository = app(AccountRepositoryInterface::class); + $this->accountRepository->setUser($user); + + return $next($request); + } + ); + } + + + /** + * Store new object. + * + * @param StoreRequest $request + * + * @return JsonResponse + */ + public function store(StoreRequest $request): JsonResponse + { + $ruleGroup = $this->ruleGroupRepository->store($request->getAll()); + $manager = $this->getManager(); + + /** @var RuleGroupTransformer $transformer */ + $transformer = app(RuleGroupTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($ruleGroup, $transformer, 'rule_groups'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/RuleGroup/TriggerController.php b/app/Api/V1/Controllers/Models/RuleGroup/TriggerController.php new file mode 100644 index 0000000000..7813528869 --- /dev/null +++ b/app/Api/V1/Controllers/Models/RuleGroup/TriggerController.php @@ -0,0 +1,169 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\RuleGroup; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\RuleGroup\TestRequest; +use FireflyIII\Api\V1\Requests\Models\RuleGroup\TriggerRequest; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\RuleGroup; +use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; +use FireflyIII\TransactionRules\Engine\RuleEngineInterface; +use FireflyIII\Transformers\TransactionGroupTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Pagination\LengthAwarePaginator; +use Illuminate\Support\Collection; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; + +/** + * Class TriggerController + */ +class TriggerController extends Controller +{ + private RuleGroupRepositoryInterface $ruleGroupRepository; + + + /** + * RuleGroupController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class); + $this->ruleGroupRepository->setUser($user); + + return $next($request); + } + ); + } + + + /** + * @param TestRequest $request + * @param RuleGroup $group + * + * @return JsonResponse + * @throws FireflyException + * + */ + public function testGroup(TestRequest $request, RuleGroup $group): JsonResponse + { + /** @var Collection $rules */ + $rules = $this->ruleGroupRepository->getActiveRules($group); + if (0 === $rules->count()) { + throw new FireflyException('200023: No rules in this rule group.'); + } + $parameters = $request->getTestParameters(); + + /** @var RuleEngineInterface $ruleEngine */ + $ruleEngine = app(RuleEngineInterface::class); + $ruleEngine->setRules($rules); + + // overrule the rule(s) if necessary. + if (array_key_exists('start', $parameters) && null !== $parameters['start']) { + // add a range: + $ruleEngine->addOperator(['type' => 'date_after', 'value' => $parameters['start']->format('Y-m-d')]); + } + + if (array_key_exists('end', $parameters) && null !== $parameters['end']) { + // add a range: + $ruleEngine->addOperator(['type' => 'date_before', 'value' => $parameters['end']->format('Y-m-d')]); + } + if (array_key_exists('accounts', $parameters) && '' !== $parameters['accounts']) { + $ruleEngine->addOperator(['type' => 'account_id', 'value' => implode(',', $parameters['accounts'])]); + } + + // file the rule(s) + $transactions = $ruleEngine->find(); + $count = $transactions->count(); + + $paginator = new LengthAwarePaginator($transactions, $count, 31337, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.rule_groups.test', [$group->id]) . $this->buildParams()); + + // resulting list is presented as JSON thing. + $manager = $this->getManager(); + /** @var TransactionGroupTransformer $transformer */ + $transformer = app(TransactionGroupTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($transactions, $transformer, 'transactions'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + + /** + * Execute the given rule group on a set of existing transactions. + * + * @param TriggerRequest $request + * @param RuleGroup $group + * + * @return JsonResponse + * @throws Exception + */ + public function triggerGroup(TriggerRequest $request, RuleGroup $group): JsonResponse + { + /** @var Collection $rules */ + $rules = $this->ruleGroupRepository->getActiveRules($group); + if (0 === $rules->count()) { + throw new FireflyException('200023: No rules in this rule group.'); + } + + // Get parameters specified by the user + $parameters = $request->getTriggerParameters(); + + /** @var RuleEngineInterface $ruleEngine */ + $ruleEngine = app(RuleEngineInterface::class); + $ruleEngine->setRules($rules); + + // overrule the rule(s) if necessary. + if (array_key_exists('start', $parameters) && null !== $parameters['start']) { + // add a range: + $ruleEngine->addOperator(['type' => 'date_after', 'value' => $parameters['start']->format('Y-m-d')]); + } + + if (array_key_exists('end', $parameters) && null !== $parameters['end']) { + // add a range: + $ruleEngine->addOperator(['type' => 'date_before', 'value' => $parameters['end']->format('Y-m-d')]); + } + if (array_key_exists('accounts', $parameters) && '' !== $parameters['accounts']) { + $ruleEngine->addOperator(['type' => 'account_id', 'value' => implode(',', $parameters['accounts'])]); + } + + // file the rule(s) + $ruleEngine->fire(); + + return response()->json([], 204); + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/RuleGroup/UpdateController.php b/app/Api/V1/Controllers/Models/RuleGroup/UpdateController.php new file mode 100644 index 0000000000..d99a8ed8b4 --- /dev/null +++ b/app/Api/V1/Controllers/Models/RuleGroup/UpdateController.php @@ -0,0 +1,86 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\RuleGroup; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\RuleGroup\UpdateRequest; +use FireflyIII\Models\RuleGroup; +use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; +use FireflyIII\Transformers\RuleGroupTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class UpdateController + */ +class UpdateController extends Controller +{ + + private RuleGroupRepositoryInterface $ruleGroupRepository; + + + /** + * RuleGroupController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class); + $this->ruleGroupRepository->setUser($user); + + return $next($request); + } + ); + } + + + /** + * Update a rule group. + * + * @param UpdateRequest $request + * @param RuleGroup $ruleGroup + * + * @return JsonResponse + */ + public function update(UpdateRequest $request, RuleGroup $ruleGroup): JsonResponse + { + $ruleGroup = $this->ruleGroupRepository->update($ruleGroup, $request->getAll()); + $manager = $this->getManager(); + + /** @var RuleGroupTransformer $transformer */ + $transformer = app(RuleGroupTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($ruleGroup, $transformer, 'rule_groups'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Tag/DestroyController.php b/app/Api/V1/Controllers/Models/Tag/DestroyController.php new file mode 100644 index 0000000000..93d273be1b --- /dev/null +++ b/app/Api/V1/Controllers/Models/Tag/DestroyController.php @@ -0,0 +1,77 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Tag; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\Tag; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; +use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; + +/** + * Class DestroyController + */ +class DestroyController extends Controller +{ + private TagRepositoryInterface $repository; + + + /** + * TagController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->repository = app(TagRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + + + /** + * Delete the resource. + * + * @param Tag $tag + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function destroy(Tag $tag): JsonResponse + { + $this->repository->destroy($tag); + + return response()->json([], 204); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/TagController.php b/app/Api/V1/Controllers/Models/Tag/ListController.php similarity index 56% rename from app/Api/V1/Controllers/TagController.php rename to app/Api/V1/Controllers/Models/Tag/ListController.php index bb370d6f47..57f3ada42d 100644 --- a/app/Api/V1/Controllers/TagController.php +++ b/app/Api/V1/Controllers/Models/Tag/ListController.php @@ -1,7 +1,7 @@ . */ -declare(strict_types=1); +namespace FireflyIII\Api\V1\Controllers\Models\Tag; -namespace FireflyIII\Api\V1\Controllers; -use FireflyIII\Api\V1\Requests\TagStoreRequest; -use FireflyIII\Api\V1\Requests\TagUpdateRequest; +use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Tag; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Transformers\AttachmentTransformer; -use FireflyIII\Transformers\TagTransformer; use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; @@ -38,17 +35,14 @@ use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; -use League\Fractal\Resource\Item; /** - * Class TagController + * Class ListController */ -class TagController extends Controller +class ListController extends Controller { use TransactionFilter; - - /** @var TagRepositoryInterface The tag repository */ - private $repository; + private TagRepositoryInterface $repository; /** @@ -72,51 +66,6 @@ class TagController extends Controller ); } - /** - * Delete the resource. - * - * @param Tag $tag - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function delete(Tag $tag): JsonResponse - { - $this->repository->destroy($tag); - - return response()->json([], 204); - } - - /** - * List all of them. - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function index(): JsonResponse - { - $manager = $this->getManager(); - // types to get, page size: - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - - // get list of budgets. Count it and split it. - $collection = $this->repository->get(); - $count = $collection->count(); - $rules = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); - - // make paginator: - $paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.tags.index') . $this->buildParams()); - - /** @var TagTransformer $transformer */ - $transformer = app(TagTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($rules, $transformer, 'tags'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } /** @@ -148,46 +97,7 @@ class TagController extends Controller return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } - /** - * List single resource. - * - * @param Tag $tag - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function show(Tag $tag): JsonResponse - { - $manager = $this->getManager(); - /** @var TagTransformer $transformer */ - $transformer = app(TagTransformer::class); - $transformer->setParameters($this->parameters); - $resource = new Item($tag, $transformer, 'tags'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - - /** - * Store new object. - * - * @param TagStoreRequest $request - * - * @return JsonResponse - */ - public function store(TagStoreRequest $request): JsonResponse - { - $rule = $this->repository->store($request->getAll()); - $manager = $this->getManager(); - /** @var TagTransformer $transformer */ - $transformer = app(TagTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($rule, $transformer, 'tags'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } /** * Show all transactions. @@ -229,7 +139,7 @@ class TagController extends Controller $collector->setRange($this->parameters->get('start'), $this->parameters->get('end')); } $paginator = $collector->getPaginatedGroups(); - $paginator->setPath(route('api.v1.transactions.index') . $this->buildParams()); + $paginator->setPath(route('api.v1.tags.transactions', [$tag->id]) . $this->buildParams()); $transactions = $paginator->getCollection(); /** @var TransactionGroupTransformer $transformer */ @@ -241,26 +151,4 @@ class TagController extends Controller return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } - - /** - * Update a rule. - * - * @param TagUpdateRequest $request - * @param Tag $tag - * - * @return JsonResponse - */ - public function update(TagUpdateRequest $request, Tag $tag): JsonResponse - { - $rule = $this->repository->update($tag, $request->getAll()); - $manager = $this->getManager(); - /** @var TagTransformer $transformer */ - $transformer = app(TagTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($rule, $transformer, 'tags'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } -} +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Tag/ShowController.php b/app/Api/V1/Controllers/Models/Tag/ShowController.php new file mode 100644 index 0000000000..edf104fb85 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Tag/ShowController.php @@ -0,0 +1,119 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Tag; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\Tag; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; +use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Transformers\TagTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; + +/** + * Class ShowController + */ +class ShowController extends Controller +{ + private TagRepositoryInterface $repository; + + + /** + * TagController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->repository = app(TagRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + /** + * List all of them. + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function index(): JsonResponse + { + $manager = $this->getManager(); + // types to get, page size: + $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of budgets. Count it and split it. + $collection = $this->repository->get(); + $count = $collection->count(); + $rules = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.tags.index') . $this->buildParams()); + + /** @var TagTransformer $transformer */ + $transformer = app(TagTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($rules, $transformer, 'tags'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + + + /** + * List single resource. + * + * @param Tag $tag + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function show(Tag $tag): JsonResponse + { + $manager = $this->getManager(); + /** @var TagTransformer $transformer */ + $transformer = app(TagTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($tag, $transformer, 'tags'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Tag/StoreController.php b/app/Api/V1/Controllers/Models/Tag/StoreController.php new file mode 100644 index 0000000000..11b264a5a2 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Tag/StoreController.php @@ -0,0 +1,85 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Tag; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\Tag\StoreRequest; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; +use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Transformers\TagTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class StoreController + */ +class StoreController extends Controller +{ + private TagRepositoryInterface $repository; + + + /** + * TagController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->repository = app(TagRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + + + /** + * Store new object. + * + * @param StoreRequest $request + * + * @return JsonResponse + */ + public function store(StoreRequest $request): JsonResponse + { + $rule = $this->repository->store($request->getAll()); + $manager = $this->getManager(); + /** @var TagTransformer $transformer */ + $transformer = app(TagTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($rule, $transformer, 'tags'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} + diff --git a/app/Api/V1/Controllers/Models/Tag/UpdateController.php b/app/Api/V1/Controllers/Models/Tag/UpdateController.php new file mode 100644 index 0000000000..87811a2ad1 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Tag/UpdateController.php @@ -0,0 +1,88 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Tag; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\Tag\UpdateRequest; +use FireflyIII\Models\Tag; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; +use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Transformers\TagTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; + +/** + * Class UpdateController + */ +class UpdateController extends Controller +{ + private TagRepositoryInterface $repository; + + + /** + * TagController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->repository = app(TagRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + + + /** + * Update a rule. + * + * @param UpdateRequest $request + * @param Tag $tag + * + * @return JsonResponse + */ + public function update(UpdateRequest $request, Tag $tag): JsonResponse + { + $rule = $this->repository->update($tag, $request->getAll()); + $manager = $this->getManager(); + /** @var TagTransformer $transformer */ + $transformer = app(TagTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($rule, $transformer, 'tags'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Transaction/DestroyController.php b/app/Api/V1/Controllers/Models/Transaction/DestroyController.php new file mode 100644 index 0000000000..f4eacf40f7 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Transaction/DestroyController.php @@ -0,0 +1,96 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Transaction; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Events\DestroyedTransactionGroup; +use FireflyIII\Models\TransactionGroup; +use FireflyIII\Models\TransactionJournal; +use FireflyIII\Repositories\Journal\JournalRepositoryInterface; +use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; + +/** + * Class DestroyController + */ +class DestroyController extends Controller +{ + private JournalRepositoryInterface $repository; + + + /** + * TransactionController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $admin */ + $admin = auth()->user(); + + $this->repository = app(JournalRepositoryInterface::class); + $this->repository->setUser($admin); + + return $next($request); + } + ); + } + + + /** + * Remove the specified resource from storage. + * + * @param TransactionGroup $transactionGroup + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function destroy(TransactionGroup $transactionGroup): JsonResponse + { + $this->repository->destroyGroup($transactionGroup); + // trigger just after destruction + event(new DestroyedTransactionGroup($transactionGroup)); + + return response()->json([], 204); + } + + + /** + * Remove the specified resource from storage. + * + * @param TransactionJournal $transactionJournal + * + * @codeCoverageIgnore + * @return JsonResponse + */ + public function destroyJournal(TransactionJournal $transactionJournal): JsonResponse + { + $this->repository->destroyJournal($transactionJournal); + + return response()->json([], 204); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Transaction/ListController.php b/app/Api/V1/Controllers/Models/Transaction/ListController.php new file mode 100644 index 0000000000..ce9393db4a --- /dev/null +++ b/app/Api/V1/Controllers/Models/Transaction/ListController.php @@ -0,0 +1,171 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Transaction; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Models\TransactionGroup; +use FireflyIII\Models\TransactionJournal; +use FireflyIII\Repositories\Journal\JournalAPIRepositoryInterface; +use FireflyIII\Repositories\Journal\JournalRepositoryInterface; +use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Transformers\AttachmentTransformer; +use FireflyIII\Transformers\PiggyBankEventTransformer; +use FireflyIII\Transformers\TransactionLinkTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Pagination\LengthAwarePaginator; +use Illuminate\Support\Collection; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; + +/** + * Class ListController + */ +class ListController extends Controller +{ + private JournalAPIRepositoryInterface $journalAPIRepository; + + /** + * TransactionController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $admin */ + $admin = auth()->user(); + + $this->journalAPIRepository = app(JournalAPIRepositoryInterface::class); + $this->journalAPIRepository->setUser($admin); + + return $next($request); + } + ); + } + + + /** + * @param TransactionGroup $transactionGroup + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function attachments(TransactionGroup $transactionGroup): JsonResponse + { + $manager = $this->getManager(); + $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $collection = new Collection; + foreach ($transactionGroup->transactionJournals as $transactionJournal) { + $collection = $this->journalAPIRepository->getAttachments($transactionJournal)->merge($collection); + } + + $count = $collection->count(); + $attachments = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.transactions.attachments', [$transactionGroup->id]) . $this->buildParams()); + + /** @var AttachmentTransformer $transformer */ + $transformer = app(AttachmentTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($attachments, $transformer, 'attachments'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + + /** + * @param TransactionGroup $transactionGroup + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function piggyBankEvents(TransactionGroup $transactionGroup): JsonResponse + { + $manager = $this->getManager(); + $collection = new Collection; + $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + foreach ($transactionGroup->transactionJournals as $transactionJournal) { + $collection = $this->journalAPIRepository->getPiggyBankEvents($transactionJournal)->merge($collection); + } + $count = $collection->count(); + $events = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + + // make paginator: + $paginator = new LengthAwarePaginator($events, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.transactions.piggy_bank_events', [$transactionGroup->id]) . $this->buildParams()); + + /** @var PiggyBankEventTransformer $transformer */ + $transformer = app(PiggyBankEventTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($events, $transformer, 'piggy_bank_events'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + + +// /** @var PiggyBankEventTransformer $transformer */ +// $transformer = app(PiggyBankEventTransformer::class); +// $transformer->setParameters($this->parameters); +// +// $resource = new FractalCollection($events, $transformer, 'piggy_bank_events'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + + /** + * @param TransactionJournal $transactionJournal + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function transactionLinks(TransactionJournal $transactionJournal): JsonResponse + { + $manager = $this->getManager(); + $collection = $this->journalAPIRepository->getJournalLinks($transactionJournal); + $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $count = $collection->count(); + $journalLinks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($journalLinks, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.transaction-journals.transaction_links', [$transactionJournal->id]) . $this->buildParams()); + + /** @var TransactionLinkTransformer $transformer */ + $transformer = app(TransactionLinkTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($journalLinks, $transformer, 'transaction_links'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Transaction/ShowController.php b/app/Api/V1/Controllers/Models/Transaction/ShowController.php new file mode 100644 index 0000000000..b1fa5a81ac --- /dev/null +++ b/app/Api/V1/Controllers/Models/Transaction/ShowController.php @@ -0,0 +1,146 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Transaction; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; +use FireflyIII\Models\TransactionGroup; +use FireflyIII\Models\TransactionJournal; +use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Transformers\TransactionGroupTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; + +/** + * Class ShowController + */ +class ShowController extends Controller +{ + use TransactionFilter; + + /** + * Show all transactions. + * + * @param Request $request + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function index(Request $request): JsonResponse + { + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $type = $request->get('type') ?? 'default'; + $this->parameters->set('type', $type); + + $types = $this->mapTransactionTypes($this->parameters->get('type')); + $manager = $this->getManager(); + /** @var User $admin */ + $admin = auth()->user(); + + // use new group collector: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector + ->setUser($admin) + // all info needed for the API: + ->withAPIInformation() + // set page size: + ->setLimit($pageSize) + // set page to retrieve + ->setPage($this->parameters->get('page')) + // set types of transactions to return. + ->setTypes($types); + + + if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { + $collector->setRange($this->parameters->get('start'), $this->parameters->get('end')); + } + $paginator = $collector->getPaginatedGroups(); + $paginator->setPath(route('api.v1.transactions.index') . $this->buildParams()); + $transactions = $paginator->getCollection(); + + /** @var TransactionGroupTransformer $transformer */ + $transformer = app(TransactionGroupTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($transactions, $transformer, 'transactions'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + + /** + * Show a single transaction. + * + * @param TransactionGroup $transactionGroup + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function show(TransactionGroup $transactionGroup): JsonResponse + { + $manager = $this->getManager(); + /** @var User $admin */ + $admin = auth()->user(); + // use new group collector: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector + ->setUser($admin) + // filter on transaction group. + ->setTransactionGroup($transactionGroup) + // all info needed for the API: + ->withAPIInformation(); + + $selectedGroup = $collector->getGroups()->first(); + if (null === $selectedGroup) { + throw new NotFoundHttpException(); + } + /** @var TransactionGroupTransformer $transformer */ + $transformer = app(TransactionGroupTransformer::class); + $transformer->setParameters($this->parameters); + $resource = new Item($selectedGroup, $transformer, 'transactions'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + /** + * Show a single transaction, by transaction journal. + * + * @param TransactionJournal $transactionJournal + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function showJournal(TransactionJournal $transactionJournal): JsonResponse + { + return $this->show($transactionJournal->transactionGroup); + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Transaction/StoreController.php b/app/Api/V1/Controllers/Models/Transaction/StoreController.php new file mode 100644 index 0000000000..fb31cea302 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Transaction/StoreController.php @@ -0,0 +1,133 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Transaction; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\Transaction\StoreRequest; +use FireflyIII\Events\StoredTransactionGroup; +use FireflyIII\Exceptions\DuplicateTransactionException; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; +use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface; +use FireflyIII\Rules\IsDuplicateTransaction; +use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Transformers\TransactionGroupTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Validation\ValidationException; +use League\Fractal\Resource\Item; +use Log; +use Validator; + +/** + * Class StoreController + */ +class StoreController extends Controller +{ + use TransactionFilter; + + private TransactionGroupRepositoryInterface $groupRepository; + + + /** + * TransactionController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $admin */ + $admin = auth()->user(); + + $this->groupRepository = app(TransactionGroupRepositoryInterface::class); + $this->groupRepository->setUser($admin); + + return $next($request); + } + ); + } + + + /** + * Store a new transaction. + * + * @param StoreRequest $request + * + * @return JsonResponse + * @throws FireflyException|ValidationException + */ + public function store(StoreRequest $request): JsonResponse + { + Log::debug('Now in API StoreController::store()'); + $data = $request->getAll(); + $data['user'] = auth()->user()->id; + + Log::channel('audit') + ->info('Store new transaction over API.', $data); + + try { + $transactionGroup = $this->groupRepository->store($data); + } catch (DuplicateTransactionException $e) { + Log::warning('Caught a duplicate transaction. Return error message.'); + $validator = Validator::make( + ['transactions' => [['description' => $e->getMessage()]]], ['transactions.0.description' => new IsDuplicateTransaction] + ); + throw new ValidationException($validator); + } catch (FireflyException $e) { + Log::warning('Caught an exception. Return error message.'); + Log::error($e->getMessage()); + $message = sprintf('Internal exception: %s', $e->getMessage()); + $validator = Validator::make(['transactions' => [['description' => $message]]], ['transactions.0.description' => new IsDuplicateTransaction]); + throw new ValidationException($validator); + } + app('preferences')->mark(); + event(new StoredTransactionGroup($transactionGroup, $data['apply_rules'] ?? true)); + + $manager = $this->getManager(); + /** @var User $admin */ + $admin = auth()->user(); + // use new group collector: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector + ->setUser($admin) + // filter on transaction group. + ->setTransactionGroup($transactionGroup) + // all info needed for the API: + ->withAPIInformation(); + + $selectedGroup = $collector->getGroups()->first(); + if (null === $selectedGroup) { + throw new FireflyException('Cannot find transaction. Possibly, a rule deleted this transaction after its creation.'); + } + /** @var TransactionGroupTransformer $transformer */ + $transformer = app(TransactionGroupTransformer::class); + $transformer->setParameters($this->parameters); + $resource = new Item($selectedGroup, $transformer, 'transactions'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/Transaction/UpdateController.php b/app/Api/V1/Controllers/Models/Transaction/UpdateController.php new file mode 100644 index 0000000000..515ef16a72 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Transaction/UpdateController.php @@ -0,0 +1,109 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Models\Transaction; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\Transaction\UpdateRequest; +use FireflyIII\Events\UpdatedTransactionGroup; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; +use FireflyIII\Models\TransactionGroup; +use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface; +use FireflyIII\Transformers\TransactionGroupTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use League\Fractal\Resource\Item; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Log; + +/** + * Class UpdateController + */ +class UpdateController extends Controller +{ + private TransactionGroupRepositoryInterface $groupRepository; + + /** + * TransactionController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $admin */ + $admin = auth()->user(); + + $this->groupRepository = app(TransactionGroupRepositoryInterface::class); + $this->groupRepository->setUser($admin); + + return $next($request); + } + ); + } + + + /** + * Update a transaction. + * + * @param UpdateRequest $request + * @param TransactionGroup $transactionGroup + * + * @return JsonResponse + */ + public function update(UpdateRequest $request, TransactionGroup $transactionGroup): JsonResponse + { + Log::debug('Now in update routine for transaction group!'); + $data = $request->getAll(); + $transactionGroup = $this->groupRepository->update($transactionGroup, $data); + $manager = $this->getManager(); + + app('preferences')->mark(); + event(new UpdatedTransactionGroup($transactionGroup, $data['apply_rules'] ?? true)); + + /** @var User $admin */ + $admin = auth()->user(); + // use new group collector: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector + ->setUser($admin) + // filter on transaction group. + ->setTransactionGroup($transactionGroup) + // all info needed for the API: + ->withAPIInformation(); + + $selectedGroup = $collector->getGroups()->first(); + if (null === $selectedGroup) { + throw new NotFoundHttpException(); // @codeCoverageIgnore + } + /** @var TransactionGroupTransformer $transformer */ + $transformer = app(TransactionGroupTransformer::class); + $transformer->setParameters($this->parameters); + $resource = new Item($selectedGroup, $transformer, 'transactions'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/DestroyController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/DestroyController.php new file mode 100644 index 0000000000..b668094ee3 --- /dev/null +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/DestroyController.php @@ -0,0 +1,75 @@ +middleware( + function ($request, $next) { + /** @var User $admin */ + $admin = auth()->user(); + + /** @var CurrencyRepositoryInterface repository */ + $this->repository = app(CurrencyRepositoryInterface::class); + $this->userRepository = app(UserRepositoryInterface::class); + $this->repository->setUser($admin); + + return $next($request); + } + ); + } + + /** + * Remove the specified resource from storage. + * + * @param TransactionCurrency $currency + * + * @return JsonResponse + * @throws FireflyException + * @codeCoverageIgnore + */ + public function destroy(TransactionCurrency $currency): JsonResponse + { + /** @var User $admin */ + $admin = auth()->user(); + + if (!$this->userRepository->hasRole($admin, 'owner')) { + // access denied: + throw new FireflyException('200005: You need the "owner" role to do this.'); // @codeCoverageIgnore + } + if ($this->repository->currencyInUse($currency)) { + throw new FireflyException('200006: Currency in use.'); // @codeCoverageIgnore + } + if ($this->repository->isFallbackCurrency($currency)) { + throw new FireflyException('200026: Currency is fallback.'); // @codeCoverageIgnore + } + + $this->repository->destroy($currency); + + return response()->json([], 204); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/CurrencyController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php similarity index 57% rename from app/Api/V1/Controllers/CurrencyController.php rename to app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php index c82a14380d..4e99811b26 100644 --- a/app/Api/V1/Controllers/CurrencyController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php @@ -1,31 +1,10 @@ . - */ -declare(strict_types=1); -namespace FireflyIII\Api\V1\Controllers; +namespace FireflyIII\Api\V1\Controllers\Models\TransactionCurrency; -use FireflyIII\Api\V1\Requests\CurrencyUpdateRequest; -use FireflyIII\Api\V1\Requests\CurrencyStoreRequest; -use FireflyIII\Exceptions\FireflyException; + +use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Account; use FireflyIII\Models\Bill; @@ -48,8 +27,6 @@ use FireflyIII\Transformers\AccountTransformer; use FireflyIII\Transformers\AvailableBudgetTransformer; use FireflyIII\Transformers\BillTransformer; use FireflyIII\Transformers\BudgetLimitTransformer; -use FireflyIII\Transformers\CurrencyExchangeRateTransformer; -use FireflyIII\Transformers\CurrencyTransformer; use FireflyIII\Transformers\RecurrenceTransformer; use FireflyIII\Transformers\RuleTransformer; use FireflyIII\Transformers\TransactionGroupTransformer; @@ -59,19 +36,17 @@ use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; -use League\Fractal\Resource\Item; /** - * Class CurrencyController. + * Class ListController */ -class CurrencyController extends Controller +class ListController extends Controller { use AccountFilter, TransactionFilter; private CurrencyRepositoryInterface $repository; private UserRepositoryInterface $userRepository; - /** * CurrencyRepository constructor. * @@ -256,183 +231,6 @@ class CurrencyController extends Controller return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } - /** - * Show a list of known exchange rates - * - * @param TransactionCurrency $currency - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function cer(TransactionCurrency $currency): JsonResponse - { - // create some objects: - $manager = $this->getManager(); - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - $collection = $this->repository->getExchangeRates($currency); - - - $count = $collection->count(); - $exchangeRates = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); - $paginator = new LengthAwarePaginator($exchangeRates, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.currencies.cer', [$currency->code]) . $this->buildParams()); - - /** @var CurrencyExchangeRateTransformer $transformer */ - $transformer = app(CurrencyExchangeRateTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($exchangeRates, $transformer, 'currency_exchange_rates'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Remove the specified resource from storage. - * - * @param TransactionCurrency $currency - * - * @return JsonResponse - * @throws FireflyException - * @codeCoverageIgnore - */ - public function delete(TransactionCurrency $currency): JsonResponse - { - /** @var User $admin */ - $admin = auth()->user(); - - if (!$this->userRepository->hasRole($admin, 'owner')) { - // access denied: - throw new FireflyException('200005: You need the "owner" role to do this.'); // @codeCoverageIgnore - } - if ($this->repository->currencyInUse($currency)) { - throw new FireflyException('200006: Currency in use.'); // @codeCoverageIgnore - } - if ($this->repository->isFallbackCurrency($currency)) { - throw new FireflyException('200026: Currency is fallback.'); // @codeCoverageIgnore - } - - $this->repository->destroy($currency); - - return response()->json([], 204); - } - - /** - * Disable a currency. - * - * @param TransactionCurrency $currency - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function disable(TransactionCurrency $currency): JsonResponse - { - // must be unused. - if ($this->repository->currencyInUse($currency)) { - return response()->json([], 409); - } - $this->repository->disable($currency); - $manager = $this->getManager(); - - $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); - $this->parameters->set('defaultCurrency', $defaultCurrency); - - /** @var CurrencyTransformer $transformer */ - $transformer = app(CurrencyTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($currency, $transformer, 'currencies'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - - /** - * Enable a currency. - * - * @param TransactionCurrency $currency - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function enable(TransactionCurrency $currency): JsonResponse - { - $this->repository->enable($currency); - $manager = $this->getManager(); - - $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); - $this->parameters->set('defaultCurrency', $defaultCurrency); - - /** @var CurrencyTransformer $transformer */ - $transformer = app(CurrencyTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($currency, $transformer, 'currencies'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - - /** - * Display a listing of the resource. - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function index(): JsonResponse - { - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - $collection = $this->repository->getAll(); - $count = $collection->count(); - // slice them: - $currencies = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); - $paginator = new LengthAwarePaginator($currencies, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.currencies.index') . $this->buildParams()); - - - $manager = $this->getManager(); - $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); - $this->parameters->set('defaultCurrency', $defaultCurrency); - - /** @var CurrencyTransformer $transformer */ - $transformer = app(CurrencyTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($currencies, $transformer, 'currencies'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Make the currency a default currency. - * - * @param TransactionCurrency $currency - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function makeDefault(TransactionCurrency $currency): JsonResponse - { - $this->repository->enable($currency); - - app('preferences')->set('currencyPreference', $currency->code); - app('preferences')->mark(); - - $manager = $this->getManager(); - - $this->parameters->set('defaultCurrency', $currency); - - /** @var CurrencyTransformer $transformer */ - $transformer = app(CurrencyTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($currency, $transformer, 'currencies'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - /** * List all recurring transactions. * @@ -534,78 +332,6 @@ class CurrencyController extends Controller } - /** - * Show a currency. - * - * @param TransactionCurrency $currency - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function show(TransactionCurrency $currency): JsonResponse - { - $manager = $this->getManager(); - $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); - $this->parameters->set('defaultCurrency', $defaultCurrency); - - /** @var CurrencyTransformer $transformer */ - $transformer = app(CurrencyTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($currency, $transformer, 'currencies'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Show a currency. - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function showDefault(): JsonResponse - { - $manager = $this->getManager(); - $currency = app('amount')->getDefaultCurrencyByUser(auth()->user()); - $this->parameters->set('defaultCurrency', $currency); - - /** @var CurrencyTransformer $transformer */ - $transformer = app(CurrencyTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($currency, $transformer, 'currencies'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Store new currency. - * - * @param CurrencyStoreRequest $request - * - * @return JsonResponse - * @throws FireflyException - */ - public function store(CurrencyStoreRequest $request): JsonResponse - { - $currency = $this->repository->store($request->getAll()); - if (true === $request->boolean('default')) { - app('preferences')->set('currencyPreference', $currency->code); - app('preferences')->mark(); - } - $manager = $this->getManager(); - $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); - $this->parameters->set('defaultCurrency', $defaultCurrency); - - /** @var CurrencyTransformer $transformer */ - $transformer = app(CurrencyTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($currency, $transformer, 'currencies'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - /** * Show all transactions. * @@ -661,37 +387,4 @@ class CurrencyController extends Controller return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } - - /** - * Update a currency. - * - * @param CurrencyUpdateRequest $request - * @param TransactionCurrency $currency - * - * @return JsonResponse - */ - public function update(CurrencyUpdateRequest $request, TransactionCurrency $currency): JsonResponse - { - $data = $request->getAll(); - $currency = $this->repository->update($currency, $data); - - if (true === $request->boolean('default')) { - app('preferences')->set('currencyPreference', $currency->code); - app('preferences')->mark(); - } - - $manager = $this->getManager(); - - $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); - $this->parameters->set('defaultCurrency', $defaultCurrency); - - /** @var CurrencyTransformer $transformer */ - $transformer = app(CurrencyTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($currency, $transformer, 'currencies'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } -} +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php new file mode 100644 index 0000000000..f5cfe2fe3b --- /dev/null +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/ShowController.php @@ -0,0 +1,125 @@ +middleware( + function ($request, $next) { + /** @var User $admin */ + $admin = auth()->user(); + + /** @var CurrencyRepositoryInterface repository */ + $this->repository = app(CurrencyRepositoryInterface::class); + $this->repository->setUser($admin); + + return $next($request); + } + ); + } + + /** + * Display a listing of the resource. + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function index(): JsonResponse + { + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $collection = $this->repository->getAll(); + $count = $collection->count(); + // slice them: + $currencies = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + $paginator = new LengthAwarePaginator($currencies, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.currencies.index') . $this->buildParams()); + + + $manager = $this->getManager(); + $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); + $this->parameters->set('defaultCurrency', $defaultCurrency); + + /** @var CurrencyTransformer $transformer */ + $transformer = app(CurrencyTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($currencies, $transformer, 'currencies'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + /** + * Show a currency. + * + * @param TransactionCurrency $currency + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function show(TransactionCurrency $currency): JsonResponse + { + $manager = $this->getManager(); + $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); + $this->parameters->set('defaultCurrency', $defaultCurrency); + + /** @var CurrencyTransformer $transformer */ + $transformer = app(CurrencyTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($currency, $transformer, 'currencies'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + /** + * Show a currency. + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function showDefault(): JsonResponse + { + $manager = $this->getManager(); + $currency = app('amount')->getDefaultCurrencyByUser(auth()->user()); + $this->parameters->set('defaultCurrency', $currency); + + /** @var CurrencyTransformer $transformer */ + $transformer = app(CurrencyTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($currency, $transformer, 'currencies'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php new file mode 100644 index 0000000000..6af9f38832 --- /dev/null +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/StoreController.php @@ -0,0 +1,82 @@ +middleware( + function ($request, $next) { + /** @var User $admin */ + $admin = auth()->user(); + + /** @var CurrencyRepositoryInterface repository */ + $this->repository = app(CurrencyRepositoryInterface::class); + $this->userRepository = app(UserRepositoryInterface::class); + $this->repository->setUser($admin); + + return $next($request); + } + ); + } + + + + + /** + * Store new currency. + * + * @param StoreRequest $request + * + * @return JsonResponse + * @throws FireflyException + */ + public function store(StoreRequest $request): JsonResponse + { + $currency = $this->repository->store($request->getAll()); + if (true === $request->boolean('default')) { + app('preferences')->set('currencyPreference', $currency->code); + app('preferences')->mark(); + } + $manager = $this->getManager(); + $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); + $this->parameters->set('defaultCurrency', $defaultCurrency); + + /** @var CurrencyTransformer $transformer */ + $transformer = app(CurrencyTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($currency, $transformer, 'currencies'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php new file mode 100644 index 0000000000..c844b88c58 --- /dev/null +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/UpdateController.php @@ -0,0 +1,169 @@ +middleware( + function ($request, $next) { + /** @var User $admin */ + $admin = auth()->user(); + + /** @var CurrencyRepositoryInterface repository */ + $this->repository = app(CurrencyRepositoryInterface::class); + $this->userRepository = app(UserRepositoryInterface::class); + $this->repository->setUser($admin); + + return $next($request); + } + ); + } + + /** + * Disable a currency. + * + * @param TransactionCurrency $currency + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function disable(TransactionCurrency $currency): JsonResponse + { + // must be unused. + if ($this->repository->currencyInUse($currency)) { + return response()->json([], 409); + } + $this->repository->disable($currency); + $manager = $this->getManager(); + + $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); + $this->parameters->set('defaultCurrency', $defaultCurrency); + + /** @var CurrencyTransformer $transformer */ + $transformer = app(CurrencyTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($currency, $transformer, 'currencies'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } + + /** + * Enable a currency. + * + * @param TransactionCurrency $currency + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function enable(TransactionCurrency $currency): JsonResponse + { + $this->repository->enable($currency); + $manager = $this->getManager(); + + $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); + $this->parameters->set('defaultCurrency', $defaultCurrency); + + /** @var CurrencyTransformer $transformer */ + $transformer = app(CurrencyTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($currency, $transformer, 'currencies'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } + + /** + * Make the currency a default currency. + * + * @param TransactionCurrency $currency + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function makeDefault(TransactionCurrency $currency): JsonResponse + { + $this->repository->enable($currency); + + app('preferences')->set('currencyPreference', $currency->code); + app('preferences')->mark(); + + $manager = $this->getManager(); + + $this->parameters->set('defaultCurrency', $currency); + + /** @var CurrencyTransformer $transformer */ + $transformer = app(CurrencyTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($currency, $transformer, 'currencies'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } + + /** + * Update a currency. + * + * @param UpdateRequest $request + * @param TransactionCurrency $currency + * + * @return JsonResponse + */ + public function update(UpdateRequest $request, TransactionCurrency $currency): JsonResponse + { + $data = $request->getAll(); + $currency = $this->repository->update($currency, $data); + + if (true === $request->boolean('default')) { + app('preferences')->set('currencyPreference', $currency->code); + app('preferences')->mark(); + } + + $manager = $this->getManager(); + + $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); + $this->parameters->set('defaultCurrency', $defaultCurrency); + + /** @var CurrencyTransformer $transformer */ + $transformer = app(CurrencyTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($currency, $transformer, 'currencies'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/TransactionLink/DestroyController.php b/app/Api/V1/Controllers/Models/TransactionLink/DestroyController.php new file mode 100644 index 0000000000..4c8f56315d --- /dev/null +++ b/app/Api/V1/Controllers/Models/TransactionLink/DestroyController.php @@ -0,0 +1,56 @@ +middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->repository = app(LinkTypeRepositoryInterface::class); + + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Delete the resource. + * + * @param TransactionJournalLink $link + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function destroy(TransactionJournalLink $link): JsonResponse + { + $this->repository->destroyLink($link); + + return response()->json([], 204); + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/TransactionLink/ShowController.php b/app/Api/V1/Controllers/Models/TransactionLink/ShowController.php new file mode 100644 index 0000000000..925127adbb --- /dev/null +++ b/app/Api/V1/Controllers/Models/TransactionLink/ShowController.php @@ -0,0 +1,108 @@ +middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->repository = app(LinkTypeRepositoryInterface::class); + + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + /** + * List all of the transaction links there are. + * + * @param Request $request + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function index(Request $request): JsonResponse + { + // create some objects: + $manager = $this->getManager(); + // read type from URI + $name = $request->get('name'); + + // types to get, page size: + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $linkType = $this->repository->findByName($name); + + // get list of transaction links. Count it and split it. + $collection = $this->repository->getJournalLinks($linkType); + $count = $collection->count(); + $journalLinks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($journalLinks, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.transaction_links.index') . $this->buildParams()); + + /** @var TransactionLinkTransformer $transformer */ + $transformer = app(TransactionLinkTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($journalLinks, $transformer, 'transaction_links'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } + + + /** + * List single resource. + * + * @param TransactionJournalLink $journalLink + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function show(TransactionJournalLink $journalLink): JsonResponse + { + $manager = $this->getManager(); + + /** @var TransactionLinkTransformer $transformer */ + $transformer = app(TransactionLinkTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($journalLink, $transformer, 'transaction_links'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } + + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/TransactionLink/StoreController.php b/app/Api/V1/Controllers/Models/TransactionLink/StoreController.php new file mode 100644 index 0000000000..856dd39e34 --- /dev/null +++ b/app/Api/V1/Controllers/Models/TransactionLink/StoreController.php @@ -0,0 +1,79 @@ +middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->repository = app(LinkTypeRepositoryInterface::class); + $this->journalRepository = app(JournalRepositoryInterface::class); + + $this->repository->setUser($user); + $this->journalRepository->setUser($user); + + return $next($request); + } + ); + } + + + /** + * Store new object. + * + * @param StoreRequest $request + * + * @return JsonResponse + * @throws FireflyException + */ + public function store(StoreRequest $request): JsonResponse + { + $manager = $this->getManager(); + $data = $request->getAll(); + $inward = $this->journalRepository->findNull($data['inward_id'] ?? 0); + $outward = $this->journalRepository->findNull($data['outward_id'] ?? 0); + if (null === $inward || null === $outward) { + throw new FireflyException('200024: Source or destination does not exist.'); + } + $data['direction'] = 'inward'; + + $journalLink = $this->repository->storeLink($data, $inward, $outward); + + /** @var TransactionLinkTransformer $transformer */ + $transformer = app(TransactionLinkTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($journalLink, $transformer, 'transaction_links'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/TransactionLink/UpdateController.php b/app/Api/V1/Controllers/Models/TransactionLink/UpdateController.php new file mode 100644 index 0000000000..a1c3caca0b --- /dev/null +++ b/app/Api/V1/Controllers/Models/TransactionLink/UpdateController.php @@ -0,0 +1,72 @@ +middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->repository = app(LinkTypeRepositoryInterface::class); + $this->journalRepository = app(JournalRepositoryInterface::class); + + $this->repository->setUser($user); + $this->journalRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Update object. + * + * @param UpdateRequest $request + * @param TransactionJournalLink $journalLink + * + * @return JsonResponse + * @throws FireflyException + */ + public function update(UpdateRequest $request, TransactionJournalLink $journalLink): JsonResponse + { + $manager = $this->getManager(); + $data = $request->getAll(); + $journalLink = $this->repository->updateLink($journalLink, $data); + + /** @var TransactionLinkTransformer $transformer */ + $transformer = app(TransactionLinkTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($journalLink, $transformer, 'transaction_links'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/TransactionLinkType/DestroyController.php b/app/Api/V1/Controllers/Models/TransactionLinkType/DestroyController.php new file mode 100644 index 0000000000..171dc384ae --- /dev/null +++ b/app/Api/V1/Controllers/Models/TransactionLinkType/DestroyController.php @@ -0,0 +1,67 @@ +middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->repository = app(LinkTypeRepositoryInterface::class); + $this->userRepository = app(UserRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + + /** + * Delete the resource. + * + * @param LinkType $linkType + * + * @return JsonResponse + * @throws FireflyException + * @codeCoverageIgnore + */ + public function destroy(LinkType $linkType): JsonResponse + { + if (false === $linkType->editable) { + throw new FireflyException('200020: Link type cannot be changed.'); + } + $this->repository->destroy($linkType); + + return response()->json([], 204); + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php b/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php new file mode 100644 index 0000000000..0a4c077bb3 --- /dev/null +++ b/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php @@ -0,0 +1,111 @@ +middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->repository = app(LinkTypeRepositoryInterface::class); + $this->userRepository = app(UserRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + + + /** + * Delete the resource. + * + * @param Request $request + * @param LinkType $linkType + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function transactions(Request $request, LinkType $linkType): JsonResponse + { + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $type = $request->get('type') ?? 'default'; + $this->parameters->set('type', $type); + + $types = $this->mapTransactionTypes($this->parameters->get('type')); + $manager = $this->getManager(); + + // whatever is returned by the query, it must be part of these journals: + $journalIds = $this->repository->getJournalIds($linkType); + + /** @var User $admin */ + $admin = auth()->user(); + + // use new group collector: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector + ->setUser($admin) + // filter on journal IDs. + ->setJournalIds($journalIds) + // all info needed for the API: + ->withAPIInformation() + // set page size: + ->setLimit($pageSize) + // set page to retrieve + ->setPage($this->parameters->get('page')) + // set types of transactions to return. + ->setTypes($types); + + + if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { + $collector->setRange($this->parameters->get('start'), $this->parameters->get('end')); + } + $paginator = $collector->getPaginatedGroups(); + $paginator->setPath(route('api.v1.transactions.index') . $this->buildParams()); + $transactions = $paginator->getCollection(); + + /** @var TransactionGroupTransformer $transformer */ + $transformer = app(TransactionGroupTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($transactions, $transformer, 'transactions'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/TransactionLinkType/ShowController.php b/app/Api/V1/Controllers/Models/TransactionLinkType/ShowController.php new file mode 100644 index 0000000000..9d5b6fb45f --- /dev/null +++ b/app/Api/V1/Controllers/Models/TransactionLinkType/ShowController.php @@ -0,0 +1,105 @@ +middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->repository = app(LinkTypeRepositoryInterface::class); + $this->userRepository = app(UserRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + /** + * List all of them. + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function index(): JsonResponse + { + // create some objects: + $manager = $this->getManager(); + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of accounts. Count it and split it. + $collection = $this->repository->get(); + $count = $collection->count(); + $linkTypes = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($linkTypes, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.link_types.index') . $this->buildParams()); + + /** @var LinkTypeTransformer $transformer */ + $transformer = app(LinkTypeTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($linkTypes, $transformer, 'link_types'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } + + + + /** + * List single resource. + * + * @param LinkType $linkType + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function show(LinkType $linkType): JsonResponse + { + $manager = $this->getManager(); + /** @var LinkTypeTransformer $transformer */ + $transformer = app(LinkTypeTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($linkType, $transformer, 'link_types'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/TransactionLinkType/StoreController.php b/app/Api/V1/Controllers/Models/TransactionLinkType/StoreController.php new file mode 100644 index 0000000000..1154d9c451 --- /dev/null +++ b/app/Api/V1/Controllers/Models/TransactionLinkType/StoreController.php @@ -0,0 +1,80 @@ +middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->repository = app(LinkTypeRepositoryInterface::class); + $this->userRepository = app(UserRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + + + /** + * Store new object. + * + * @param StoreRequest $request + * + * @return JsonResponse + * @throws FireflyException + */ + public function store(StoreRequest $request): JsonResponse + { + /** @var User $admin */ + $admin = auth()->user(); + + if (!$this->userRepository->hasRole($admin, 'owner')) { + throw new FireflyException('200005: You need the "owner" role to do this.'); // @codeCoverageIgnore + } + $data = $request->getAll(); + // if currency ID is 0, find the currency by the code: + $linkType = $this->repository->store($data); + $manager = $this->getManager(); + + /** @var LinkTypeTransformer $transformer */ + $transformer = app(LinkTypeTransformer::class); + $transformer->setParameters($this->parameters); + $resource = new Item($linkType, $transformer, 'link_types'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Models/TransactionLinkType/UpdateController.php b/app/Api/V1/Controllers/Models/TransactionLinkType/UpdateController.php new file mode 100644 index 0000000000..9c06b2a672 --- /dev/null +++ b/app/Api/V1/Controllers/Models/TransactionLinkType/UpdateController.php @@ -0,0 +1,86 @@ +middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + $this->repository = app(LinkTypeRepositoryInterface::class); + $this->userRepository = app(UserRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + + + /** + * Update object. + * + * @param UpdateRequest $request + * @param LinkType $linkType + * + * @return JsonResponse + * @throws FireflyException + */ + public function update(UpdateRequest $request, LinkType $linkType): JsonResponse + { + if (false === $linkType->editable) { + throw new FireflyException('200020: Link type cannot be changed.'); + } + + /** @var User $admin */ + $admin = auth()->user(); + + if (!$this->userRepository->hasRole($admin, 'owner')) { + throw new FireflyException('200005: You need the "owner" role to do this.'); // @codeCoverageIgnore + } + + $data = $request->getAll(); + $this->repository->update($linkType, $data); + $manager = $this->getManager(); + /** @var LinkTypeTransformer $transformer */ + $transformer = app(LinkTypeTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($linkType, $transformer, 'link_types'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/PiggyBankController.php b/app/Api/V1/Controllers/PiggyBankController.php deleted file mode 100644 index b61dc029f4..0000000000 --- a/app/Api/V1/Controllers/PiggyBankController.php +++ /dev/null @@ -1,251 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Api\V1\Controllers; - -use FireflyIII\Api\V1\Requests\PiggyBankUpdateRequest; -use FireflyIII\Api\V1\Requests\PiggyBankStoreRequest; -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Models\PiggyBank; -use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; -use FireflyIII\Transformers\AttachmentTransformer; -use FireflyIII\Transformers\PiggyBankEventTransformer; -use FireflyIII\Transformers\PiggyBankTransformer; -use FireflyIII\User; -use Illuminate\Http\JsonResponse; -use Illuminate\Pagination\LengthAwarePaginator; -use League\Fractal\Pagination\IlluminatePaginatorAdapter; -use League\Fractal\Resource\Collection as FractalCollection; -use League\Fractal\Resource\Item; - -/** - * Class PiggyBankController. - */ -class PiggyBankController extends Controller -{ - private PiggyBankRepositoryInterface $repository; - - /** - * PiggyBankController constructor. - * - * @codeCoverageIgnore - */ - public function __construct() - { - parent::__construct(); - $this->middleware( - function ($request, $next) { - /** @var User $admin */ - $admin = auth()->user(); - - $this->repository = app(PiggyBankRepositoryInterface::class); - $this->repository->setUser($admin); - - - return $next($request); - } - ); - } - - /** - * Delete the resource. - * - * @param PiggyBank $piggyBank - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function delete(PiggyBank $piggyBank): JsonResponse - { - $this->repository->destroy($piggyBank); - - return response()->json([], 204); - } - - - /** - * @param PiggyBank $piggyBank - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function attachments(PiggyBank $piggyBank): JsonResponse - { - $manager = $this->getManager(); - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - $collection = $this->repository->getAttachments($piggyBank); - - $count = $collection->count(); - $attachments = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); - - // make paginator: - $paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.piggy_banks.attachments', [$piggyBank->id]) . $this->buildParams()); - - /** @var AttachmentTransformer $transformer */ - $transformer = app(AttachmentTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($attachments, $transformer, 'attachments'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * List all of them. - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function index(): JsonResponse - { - $manager = $this->getManager(); - // types to get, page size: - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - - // get list of budgets. Count it and split it. - $collection = $this->repository->getPiggyBanks(); - $count = $collection->count(); - $piggyBanks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); - - // make paginator: - $paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.piggy_banks.index') . $this->buildParams()); - - /** @var PiggyBankTransformer $transformer */ - $transformer = app(PiggyBankTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($piggyBanks, $transformer, 'piggy_banks'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - - /** - * List single resource. - * - * @param PiggyBank $piggyBank - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function piggyBankEvents(PiggyBank $piggyBank): JsonResponse - { - // types to get, page size: - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - $manager = $this->getManager(); - - $collection = $this->repository->getEvents($piggyBank); - $count = $collection->count(); - $events = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); - - // make paginator: - $paginator = new LengthAwarePaginator($events, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.piggy_banks.events', [$piggyBank->id]) . $this->buildParams()); - - /** @var PiggyBankEventTransformer $transformer */ - $transformer = app(PiggyBankEventTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($events, $transformer, 'piggy_bank_events'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - - /** - * List single resource. - * - * @param PiggyBank $piggyBank - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function show(PiggyBank $piggyBank): JsonResponse - { - $manager = $this->getManager(); - - /** @var PiggyBankTransformer $transformer */ - $transformer = app(PiggyBankTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($piggyBank, $transformer, 'piggy_banks'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - - /** - * Store new object. - * - * @param PiggyBankStoreRequest $request - * - * @return JsonResponse - * @throws FireflyException - */ - public function store(PiggyBankStoreRequest $request): JsonResponse - { - $piggyBank = $this->repository->store($request->getAll()); - $manager = $this->getManager(); - - /** @var PiggyBankTransformer $transformer */ - $transformer = app(PiggyBankTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($piggyBank, $transformer, 'piggy_banks'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Update piggy bank. - * - * @param PiggyBankUpdateRequest $request - * @param PiggyBank $piggyBank - * - * @return JsonResponse - */ - public function update(PiggyBankUpdateRequest $request, PiggyBank $piggyBank): JsonResponse - { - $data = $request->getAll(); - $piggyBank = $this->repository->update($piggyBank, $data); - - if ('' !== $data['current_amount']) { - $this->repository->setCurrentAmount($piggyBank, $data['current_amount']); - } - - $manager = $this->getManager(); - /** @var PiggyBankTransformer $transformer */ - $transformer = app(PiggyBankTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($piggyBank, $transformer, 'piggy_banks'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } -} diff --git a/app/Api/V1/Controllers/PreferenceController.php b/app/Api/V1/Controllers/PreferenceController.php deleted file mode 100644 index 754c8cbeb7..0000000000 --- a/app/Api/V1/Controllers/PreferenceController.php +++ /dev/null @@ -1,167 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Api\V1\Controllers; - -use FireflyIII\Api\V1\Requests\PreferenceRequest; -use FireflyIII\Models\AccountType; -use FireflyIII\Models\Preference; -use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Transformers\PreferenceTransformer; -use FireflyIII\User; -use Illuminate\Http\JsonResponse; -use Illuminate\Support\Collection; -use League\Fractal\Resource\Collection as FractalCollection; -use League\Fractal\Resource\Item; - -/** - * Class PreferenceController - */ -class PreferenceController extends Controller -{ - /** - * LinkTypeController constructor. - * - * @codeCoverageIgnore - */ - public function __construct() - { - parent::__construct(); - $this->middleware( - static function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); - $repository = app(AccountRepositoryInterface::class); - $repository->setUser($user); - - // an important fallback is that the frontPageAccount array gets refilled automatically - // when it turns up empty. - $frontPageAccounts = app('preferences')->getForUser($user, 'frontPageAccounts', [])->data; - if (0 === count($frontPageAccounts)) { - /** @var Collection $accounts */ - $accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); - $accountIds = $accounts->pluck('id')->toArray(); - app('preferences')->setForUser($user, 'frontPageAccounts', $accountIds); - } - - return $next($request); - } - ); - } - - /** - * List all of them. - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function index(): JsonResponse - { - /** @var User $user */ - $user = auth()->user(); - $available = [ - 'language', 'customFiscalYear', 'fiscalYearStart', 'currencyPreference', - 'transaction_journal_optional_fields', 'frontPageAccounts', 'viewRange', - 'listPageSize', - ]; - - $preferences = new Collection; - foreach ($available as $name) { - $pref = app('preferences')->getForUser($user, $name); - if (null !== $pref) { - $preferences->push($pref); - } - } - - $manager = $this->getManager(); - - /** @var PreferenceTransformer $transformer */ - $transformer = app(PreferenceTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($preferences, $transformer, 'preferences'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - - /** - * Return a single preference by name. - * - * @param Preference $preference - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function show(Preference $preference): JsonResponse - { - $manager = $this->getManager(); - /** @var PreferenceTransformer $transformer */ - $transformer = app(PreferenceTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($preference, $transformer, 'preferences'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Update a preference. - * - * @param PreferenceRequest $request - * @param Preference $preference - * - * @return JsonResponse - */ - public function update(PreferenceRequest $request, Preference $preference): JsonResponse - { - - $data = $request->getAll(); - $newValue = $data['data']; - switch ($preference->name) { - default: - break; - case 'transaction_journal_optional_fields': - case 'frontPageAccounts': - $newValue = explode(',', $data['data']); - break; - case 'listPageSize': - $newValue = (int) $data['data']; - break; - case 'customFiscalYear': - $newValue = 1 === (int) $data['data']; - break; - } - $result = app('preferences')->set($preference->name, $newValue); - - $manager = $this->getManager(); - /** @var PreferenceTransformer $transformer */ - $transformer = app(PreferenceTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($result, $transformer, 'preferences'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } -} diff --git a/app/Api/V1/Controllers/Preferences/IndexController.php b/app/Api/V1/Controllers/Preferences/IndexController.php deleted file mode 100644 index b4c5551079..0000000000 --- a/app/Api/V1/Controllers/Preferences/IndexController.php +++ /dev/null @@ -1,99 +0,0 @@ -. - */ - -namespace FireflyIII\Api\V1\Controllers\Preferences; - - -use Carbon\Carbon; -use FireflyIII\Api\V1\Controllers\Controller; -use FireflyIII\Repositories\Journal\JournalRepositoryInterface; -use FireflyIII\Support\Facades\Navigation; -use Illuminate\Http\JsonResponse; - -/** - * Class IndexController - */ -class IndexController extends Controller -{ - public const DATE_FORMAT = 'Y-m-d'; - - /** - * Return users preferred date range settings, the current period - * and some previous / next periods. - * - * @return JsonResponse - */ - public function dateRanges(): JsonResponse - { - $range = app('preferences')->get('viewRange', '1M')->data; - $return = [ - 'range' => $range, - 'ranges' => [], - 'default' => null, - ]; - $today = Carbon::today(config('app.timezone')); - $start = Navigation::startOfPeriod($today, $range); - $todayStr = $today->format(self::DATE_FORMAT); - // optional date ranges. Maybe to be configured later - // current $period - $title = (string)Navigation::periodShow($start, $range); - $return['default'] = $title; - $return['ranges'][$title] = [$start->format(self::DATE_FORMAT), - Navigation::endOfPeriod($start, $range)->format(self::DATE_FORMAT)]; - // previous $period - $previousStart = Navigation::subtractPeriod($start, $range); - $title = (string)Navigation::periodShow($previousStart, $range); - $return['ranges'][$title] = [$previousStart->format(self::DATE_FORMAT), - Navigation::endOfPeriod($previousStart, $range)->format(self::DATE_FORMAT)]; - // next $period - $nextStart = Navigation::addPeriod($start, $range, 0); - $title = (string)Navigation::periodShow($nextStart, $range); - $return['ranges'][$title] = [$nextStart->format(self::DATE_FORMAT), - Navigation::endOfPeriod($nextStart, $range)->format(self::DATE_FORMAT)]; - // -- - // last seven days: - $seven = Carbon::today()->subDays(7); - $title = (string)trans('firefly.last_seven_days'); - $return['ranges'][$title] = [$seven->format(self::DATE_FORMAT), $todayStr]; - // last 30 days: - $thirty = Carbon::today()->subDays(30); - $title = (string)trans('firefly.last_thirty_days'); - $return['ranges'][$title] = [$thirty->format(self::DATE_FORMAT), $todayStr]; - // last 180 days - $long = Carbon::today()->subDays(180); - $title = (string)trans('firefly.last_180_days'); - $return['ranges'][$title] = [$long->format(self::DATE_FORMAT), $todayStr]; - // YTD - $YTD = Carbon::today()->startOfYear(); - $title = (string)trans('firefly.YTD'); - $return['ranges'][$title] = [$YTD->format(self::DATE_FORMAT), $todayStr]; - // --- - // everything - $repository = app(JournalRepositoryInterface::class); - $journal = $repository->firstNull(); - $first = null === $journal ? clone $YTD : clone $journal->date; - $title = (string)trans('firefly.everything'); - $return['ranges'][$title] = [$first->format(self::DATE_FORMAT), $todayStr]; - - return response()->json($return); - } - -} \ No newline at end of file diff --git a/app/Api/V1/Controllers/RecurrenceController.php b/app/Api/V1/Controllers/RecurrenceController.php deleted file mode 100644 index 2c85d0d8c9..0000000000 --- a/app/Api/V1/Controllers/RecurrenceController.php +++ /dev/null @@ -1,276 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Api\V1\Controllers; - -use FireflyIII\Api\V1\Requests\RecurrenceStoreRequest; -use FireflyIII\Api\V1\Requests\RecurrenceUpdateRequest; -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Helpers\Collector\GroupCollectorInterface; -use FireflyIII\Models\Recurrence; -use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; -use FireflyIII\Support\Cronjobs\RecurringCronjob; -use FireflyIII\Support\Http\Api\TransactionFilter; -use FireflyIII\Transformers\RecurrenceTransformer; -use FireflyIII\Transformers\TransactionGroupTransformer; -use FireflyIII\User; -use Illuminate\Http\JsonResponse; -use Illuminate\Http\Request; -use Illuminate\Pagination\LengthAwarePaginator; -use League\Fractal\Pagination\IlluminatePaginatorAdapter; -use League\Fractal\Resource\Collection as FractalCollection; -use League\Fractal\Resource\Item; -use Log; - -/** - * Class RecurrenceController - */ -class RecurrenceController extends Controller -{ - use TransactionFilter; - - /** @var RecurringRepositoryInterface The recurring transaction repository */ - private $repository; - - - /** - * RecurrenceController constructor. - * - * @codeCoverageIgnore - */ - public function __construct() - { - parent::__construct(); - $this->middleware( - function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); - - /** @var RecurringRepositoryInterface repository */ - $this->repository = app(RecurringRepositoryInterface::class); - $this->repository->setUser($user); - - return $next($request); - } - ); - } - - /** - * Delete the resource. - * - * @param Recurrence $recurrence - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function delete(Recurrence $recurrence): JsonResponse - { - $this->repository->destroy($recurrence); - - return response()->json([], 204); - } - - /** - * List all of them. - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function index(): JsonResponse - { - $manager = $this->getManager(); - - // types to get, page size: - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - - // get list of budgets. Count it and split it. - $collection = $this->repository->getAll(); - $count = $collection->count(); - $piggyBanks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); - - // make paginator: - $paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.recurrences.index') . $this->buildParams()); - - /** @var RecurrenceTransformer $transformer */ - $transformer = app(RecurrenceTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($piggyBanks, $transformer, 'recurrences'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - - /** - * List single resource. - * - * @param Recurrence $recurrence - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function show(Recurrence $recurrence): JsonResponse - { - $manager = $this->getManager(); - - /** @var RecurrenceTransformer $transformer */ - $transformer = app(RecurrenceTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($recurrence, $transformer, 'recurrences'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - - } - - /** - * Store new object. - * - * @param RecurrenceStoreRequest $request - * - * @return JsonResponse - * @throws FireflyException - */ - public function store(RecurrenceStoreRequest $request): JsonResponse - { - $data = $request->getAll(); - $recurrence = $this->repository->store($data); - $manager = $this->getManager(); - - /** @var RecurrenceTransformer $transformer */ - $transformer = app(RecurrenceTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($recurrence, $transformer, 'recurrences'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Show transactions for this recurrence. - * - * @param Request $request - * @param Recurrence $recurrence - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function transactions(Request $request, Recurrence $recurrence): JsonResponse - { - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - $type = $request->get('type') ?? 'default'; - $this->parameters->set('type', $type); - - $types = $this->mapTransactionTypes($this->parameters->get('type')); - $manager = $this->getManager(); - // whatever is returned by the query, it must be part of these journals: - $journalIds = $this->repository->getJournalIds($recurrence); - - /** @var User $admin */ - $admin = auth()->user(); - - // use new group collector: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector - ->setUser($admin) - // filter on journal IDs. - ->setJournalIds($journalIds) - // all info needed for the API: - ->withAPIInformation() - // set page size: - ->setLimit($pageSize) - // set page to retrieve - ->setPage($this->parameters->get('page')) - // set types of transactions to return. - ->setTypes($types); - - if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { - $collector->setRange($this->parameters->get('start'), $this->parameters->get('end')); - } - $paginator = $collector->getPaginatedGroups(); - $paginator->setPath(route('api.v1.transactions.index') . $this->buildParams()); - $transactions = $paginator->getCollection(); - - /** @var TransactionGroupTransformer $transformer */ - $transformer = app(TransactionGroupTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($transactions, $transformer, 'transactions'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * @return JsonResponse - * @throws FireflyException - * @codeCoverageIgnore - */ - public function trigger(): JsonResponse - { - /** @var RecurringCronjob $recurring */ - $recurring = app(RecurringCronjob::class); - try { - $result = $recurring->fire(); - } catch (FireflyException $e) { - Log::error($e->getMessage()); - throw new FireflyException('200022: Error in cron job.', 0, $e); - } - if (false === $result) { - return response()->json([], 204); - } - if (true === $result) { - return response()->json(); - } - - return response()->json([], 418); // @codeCoverageIgnore - } - - /** - * Update single recurrence. - * - * @param RecurrenceUpdateRequest $request - * @param Recurrence $recurrence - * - * @return JsonResponse - */ - public function update(RecurrenceUpdateRequest $request, Recurrence $recurrence): JsonResponse - { - $data = $request->getAll(); - $category = $this->repository->update($recurrence, $data); - $manager = $this->getManager(); - - /** @var RecurrenceTransformer $transformer */ - $transformer = app(RecurrenceTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($category, $transformer, 'recurrences'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } -} diff --git a/app/Api/V1/Controllers/RuleController.php b/app/Api/V1/Controllers/RuleController.php deleted file mode 100644 index 2c854455ee..0000000000 --- a/app/Api/V1/Controllers/RuleController.php +++ /dev/null @@ -1,320 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Api\V1\Controllers; - -use FireflyIII\Api\V1\Requests\RuleStoreRequest; -use FireflyIII\Api\V1\Requests\RuleTestRequest; -use FireflyIII\Api\V1\Requests\RuleTriggerRequest; -use FireflyIII\Api\V1\Requests\RuleUpdateRequest; -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Models\Rule; -use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Rule\RuleRepositoryInterface; -use FireflyIII\TransactionRules\Engine\RuleEngineInterface; -use FireflyIII\Transformers\RuleTransformer; -use FireflyIII\Transformers\TransactionGroupTransformer; -use FireflyIII\User; -use Illuminate\Http\JsonResponse; -use Illuminate\Pagination\LengthAwarePaginator; -use Illuminate\Support\Collection; -use League\Fractal\Pagination\IlluminatePaginatorAdapter; -use League\Fractal\Resource\Collection as FractalCollection; -use League\Fractal\Resource\Item; - -/** - * Class RuleController - */ -class RuleController extends Controller -{ - /** @var AccountRepositoryInterface Account repository */ - private $accountRepository; - - /** @var RuleRepositoryInterface The rule repository */ - private $ruleRepository; - - - /** - * RuleController constructor. - * - * @codeCoverageIgnore - */ - public function __construct() - { - parent::__construct(); - $this->middleware( - function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); - - $this->ruleRepository = app(RuleRepositoryInterface::class); - $this->ruleRepository->setUser($user); - - $this->accountRepository = app(AccountRepositoryInterface::class); - $this->accountRepository->setUser($user); - - return $next($request); - } - ); - } - - /** - * Delete the resource. - * - * @param Rule $rule - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function delete(Rule $rule): JsonResponse - { - $this->ruleRepository->destroy($rule); - - return response()->json([], 204); - } - - /** - * List all of them. - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function index(): JsonResponse - { - $manager = $this->getManager(); - - // types to get, page size: - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - - // get list of budgets. Count it and split it. - $collection = $this->ruleRepository->getAll(); - $count = $collection->count(); - $rules = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); - - // make paginator: - $paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.rules.index') . $this->buildParams()); - - /** @var RuleTransformer $transformer */ - $transformer = app(RuleTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($rules, $transformer, 'rules'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - - /** - * @param Rule $rule - * - * @return JsonResponse - */ - public function moveDown(Rule $rule): JsonResponse - { - $this->ruleRepository->moveDown($rule); - $rule = $this->ruleRepository->find($rule->id); - $manager = $this->getManager(); - - /** @var RuleTransformer $transformer */ - $transformer = app(RuleTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($rule, $transformer, 'rules'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - - /** - * @param Rule $rule - * - * @return JsonResponse - */ - public function moveUp(Rule $rule): JsonResponse - { - $this->ruleRepository->moveUp($rule); - $rule = $this->ruleRepository->find($rule->id); - $manager = $this->getManager(); - - /** @var RuleTransformer $transformer */ - $transformer = app(RuleTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($rule, $transformer, 'rules'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - - /** - * List single resource. - * - * @param Rule $rule - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function show(Rule $rule): JsonResponse - { - $manager = $this->getManager(); - /** @var RuleTransformer $transformer */ - $transformer = app(RuleTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($rule, $transformer, 'rules'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - - /** - * Store new object. - * - * @param RuleStoreRequest $request - * - * @return JsonResponse - */ - public function store(RuleStoreRequest $request): JsonResponse - { - $rule = $this->ruleRepository->store($request->getAll()); - $manager = $this->getManager(); - /** @var RuleTransformer $transformer */ - $transformer = app(RuleTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($rule, $transformer, 'rules'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * @param RuleTestRequest $request - * @param Rule $rule - * - * @return JsonResponse - * @throws FireflyException - */ - public function testRule(RuleTestRequest $request, Rule $rule): JsonResponse - { - $parameters = $request->getTestParameters(); - - /** @var RuleEngineInterface $ruleEngine */ - $ruleEngine = app(RuleEngineInterface::class); - $ruleEngine->setRules(new Collection([$rule])); - - - // overrule the rule(s) if necessary. - if (array_key_exists('start', $parameters) && null !== $parameters['start']) { - // add a range: - $ruleEngine->addOperator(['type' => 'date_after', 'value' => $parameters['start']->format('Y-m-d')]); - } - - if (array_key_exists('end', $parameters) && null !== $parameters['end']) { - // add a range: - $ruleEngine->addOperator(['type' => 'date_before', 'value' => $parameters['end']->format('Y-m-d')]); - } - if (array_key_exists('accounts', $parameters) && '' !== $parameters['accounts']) { - $ruleEngine->addOperator(['type' => 'account_id', 'value' => $parameters['accounts']]); - } - - // file the rule(s) - $transactions = $ruleEngine->find(); - $count = $transactions->count(); - - $paginator = new LengthAwarePaginator($transactions, $count, 31337, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.rules.test', [$rule->id]) . $this->buildParams()); - - // resulting list is presented as JSON thing. - $manager = $this->getManager(); - /** @var TransactionGroupTransformer $transformer */ - $transformer = app(TransactionGroupTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($transactions, $transformer, 'transactions'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Execute the given rule group on a set of existing transactions. - * - * @param RuleTriggerRequest $request - * @param Rule $rule - * - * @return JsonResponse - */ - public function triggerRule(RuleTriggerRequest $request, Rule $rule): JsonResponse - { - // Get parameters specified by the user - $parameters = $request->getTriggerParameters(); - - /** @var RuleEngineInterface $ruleEngine */ - $ruleEngine = app(RuleEngineInterface::class); - $ruleEngine->setRules(new Collection([$rule])); - - // overrule the rule(s) if necessary. - if (array_key_exists('start', $parameters) && null !== $parameters['start']) { - // add a range: - $ruleEngine->addOperator(['type' => 'date_after', 'value' => $parameters['start']->format('Y-m-d')]); - } - - if (array_key_exists('end', $parameters) && null !== $parameters['end']) { - // add a range: - $ruleEngine->addOperator(['type' => 'date_before', 'value' => $parameters['end']->format('Y-m-d')]); - } - if (array_key_exists('accounts', $parameters) && '' !== $parameters['accounts']) { - $ruleEngine->addOperator(['type' => 'account_id', 'value' => $parameters['accounts']]); - } - - // file the rule(s) - $ruleEngine->fire(); - - return response()->json([], 204); - } - - /** - * Update a rule. - * - * @param RuleUpdateRequest $request - * @param Rule $rule - * - * @return JsonResponse - */ - public function update(RuleUpdateRequest $request, Rule $rule): JsonResponse - { - $data = $request->getAll(); - $rule = $this->ruleRepository->update($rule, $data); - $manager = $this->getManager(); - - /** @var RuleTransformer $transformer */ - $transformer = app(RuleTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($rule, $transformer, 'rules'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } -} diff --git a/app/Api/V1/Controllers/RuleGroupController.php b/app/Api/V1/Controllers/RuleGroupController.php deleted file mode 100644 index 51e84b7740..0000000000 --- a/app/Api/V1/Controllers/RuleGroupController.php +++ /dev/null @@ -1,363 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Api\V1\Controllers; - -use Exception; -use FireflyIII\Api\V1\Requests\RuleGroupUpdateRequest; -use FireflyIII\Api\V1\Requests\RuleGroupStoreRequest; -use FireflyIII\Api\V1\Requests\RuleGroupTestRequest; -use FireflyIII\Api\V1\Requests\RuleGroupTriggerRequest; -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Models\RuleGroup; -use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; -use FireflyIII\TransactionRules\Engine\RuleEngineInterface; -use FireflyIII\Transformers\RuleGroupTransformer; -use FireflyIII\Transformers\RuleTransformer; -use FireflyIII\Transformers\TransactionGroupTransformer; -use FireflyIII\User; -use Illuminate\Http\JsonResponse; -use Illuminate\Pagination\LengthAwarePaginator; -use Illuminate\Support\Collection; -use League\Fractal\Pagination\IlluminatePaginatorAdapter; -use League\Fractal\Resource\Collection as FractalCollection; -use League\Fractal\Resource\Item; - -/** - * Class RuleGroupController - */ -class RuleGroupController extends Controller -{ - /** @var AccountRepositoryInterface Account repository */ - private $accountRepository; - - /** @var RuleGroupRepositoryInterface The rule group repository */ - private $ruleGroupRepository; - - - /** - * RuleGroupController constructor. - * - * @codeCoverageIgnore - */ - public function __construct() - { - parent::__construct(); - $this->middleware( - function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); - - $this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class); - $this->ruleGroupRepository->setUser($user); - - $this->accountRepository = app(AccountRepositoryInterface::class); - $this->accountRepository->setUser($user); - - return $next($request); - } - ); - } - - /** - * Delete the resource. - * - * @param RuleGroup $ruleGroup - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function delete(RuleGroup $ruleGroup): JsonResponse - { - $this->ruleGroupRepository->destroy($ruleGroup, null); - - return response()->json([], 204); - } - - /** - * List all of them. - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function index(): JsonResponse - { - $manager = $this->getManager(); - // types to get, page size: - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - - // get list of rule groups. Count it and split it. - $collection = $this->ruleGroupRepository->get(); - $count = $collection->count(); - $ruleGroups = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); - - // make paginator: - $paginator = new LengthAwarePaginator($ruleGroups, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.rule_groups.index') . $this->buildParams()); - - /** @var RuleGroupTransformer $transformer */ - $transformer = app(RuleGroupTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($ruleGroups, $transformer, 'rule_groups'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * @param RuleGroup $ruleGroup - * - * @return JsonResponse - */ - public function moveDown(RuleGroup $ruleGroup): JsonResponse - { - $this->ruleGroupRepository->moveDown($ruleGroup); - $ruleGroup = $this->ruleGroupRepository->find($ruleGroup->id); - $manager = $this->getManager(); - - /** @var RuleGroupTransformer $transformer */ - $transformer = app(RuleGroupTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($ruleGroup, $transformer, 'rule_groups'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * @param RuleGroup $ruleGroup - * - * @return JsonResponse - */ - public function moveUp(RuleGroup $ruleGroup): JsonResponse - { - $this->ruleGroupRepository->moveUp($ruleGroup); - $ruleGroup = $this->ruleGroupRepository->find($ruleGroup->id); - $manager = $this->getManager(); - - /** @var RuleGroupTransformer $transformer */ - $transformer = app(RuleGroupTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($ruleGroup, $transformer, 'rule_groups'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * @param RuleGroup $group - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function rules(RuleGroup $group): JsonResponse - { - $manager = $this->getManager(); - // types to get, page size: - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - - // get list of budgets. Count it and split it. - $collection = $this->ruleGroupRepository->getRules($group); - $count = $collection->count(); - $rules = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); - - // make paginator: - $paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.rule_groups.rules', [$group->id]) . $this->buildParams()); - - /** @var RuleTransformer $transformer */ - $transformer = app(RuleTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($rules, $transformer, 'rules'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - - /** - * List single resource. - * - * @param RuleGroup $ruleGroup - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function show(RuleGroup $ruleGroup): JsonResponse - { - $manager = $this->getManager(); - /** @var RuleGroupTransformer $transformer */ - $transformer = app(RuleGroupTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($ruleGroup, $transformer, 'rule_groups'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - - /** - * Store new object. - * - * @param RuleGroupStoreRequest $request - * - * @return JsonResponse - */ - public function store(RuleGroupStoreRequest $request): JsonResponse - { - $ruleGroup = $this->ruleGroupRepository->store($request->getAll()); - $manager = $this->getManager(); - - /** @var RuleGroupTransformer $transformer */ - $transformer = app(RuleGroupTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($ruleGroup, $transformer, 'rule_groups'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - - /** - * @param RuleGroupTestRequest $request - * @param RuleGroup $group - * - * @return JsonResponse - * @throws FireflyException - * - */ - public function testGroup(RuleGroupTestRequest $request, RuleGroup $group): JsonResponse - { - /** @var Collection $rules */ - $rules = $this->ruleGroupRepository->getActiveRules($group); - if (0 === $rules->count()) { - throw new FireflyException('200023: No rules in this rule group.'); - } - $parameters = $request->getTestParameters(); - - /** @var RuleEngineInterface $ruleEngine */ - $ruleEngine = app(RuleEngineInterface::class); - $ruleEngine->setRules($rules); - - // overrule the rule(s) if necessary. - if (array_key_exists('start', $parameters) && null !== $parameters['start']) { - // add a range: - $ruleEngine->addOperator(['type' => 'date_after', 'value' => $parameters['start']->format('Y-m-d')]); - } - - if (array_key_exists('end', $parameters) && null !== $parameters['end']) { - // add a range: - $ruleEngine->addOperator(['type' => 'date_before', 'value' => $parameters['end']->format('Y-m-d')]); - } - if (array_key_exists('accounts', $parameters) && '' !== $parameters['accounts']) { - $ruleEngine->addOperator(['type' => 'account_id', 'value' => $parameters['accounts']]); - } - - // file the rule(s) - $transactions = $ruleEngine->find(); - $count = $transactions->count(); - - $paginator = new LengthAwarePaginator($transactions, $count, 31337, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.rule_groups.test', [$group->id]) . $this->buildParams()); - - // resulting list is presented as JSON thing. - $manager = $this->getManager(); - /** @var TransactionGroupTransformer $transformer */ - $transformer = app(TransactionGroupTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($transactions, $transformer, 'transactions'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Execute the given rule group on a set of existing transactions. - * - * @param RuleGroupTriggerRequest $request - * @param RuleGroup $group - * - * @return JsonResponse - * @throws Exception - */ - public function triggerGroup(RuleGroupTriggerRequest $request, RuleGroup $group): JsonResponse - { - /** @var Collection $rules */ - $rules = $this->ruleGroupRepository->getActiveRules($group); - if (0 === $rules->count()) { - throw new FireflyException('200023: No rules in this rule group.'); - } - - // Get parameters specified by the user - $parameters = $request->getTriggerParameters(); - - /** @var RuleEngineInterface $ruleEngine */ - $ruleEngine = app(RuleEngineInterface::class); - $ruleEngine->setRules($rules); - - // overrule the rule(s) if necessary. - if (array_key_exists('start', $parameters) && null !== $parameters['start']) { - // add a range: - $ruleEngine->addOperator(['type' => 'date_after', 'value' => $parameters['start']->format('Y-m-d')]); - } - - if (array_key_exists('end', $parameters) && null !== $parameters['end']) { - // add a range: - $ruleEngine->addOperator(['type' => 'date_before', 'value' => $parameters['end']->format('Y-m-d')]); - } - if (array_key_exists('accounts', $parameters) && '' !== $parameters['accounts']) { - $ruleEngine->addOperator(['type' => 'account_id', 'value' => $parameters['accounts']]); - } - - // file the rule(s) - $ruleEngine->fire(); - - return response()->json([], 204); - } - - /** - * Update a rule group. - * - * @param RuleGroupUpdateRequest $request - * @param RuleGroup $ruleGroup - * - * @return JsonResponse - */ - public function update(RuleGroupUpdateRequest $request, RuleGroup $ruleGroup): JsonResponse - { - $ruleGroup = $this->ruleGroupRepository->update($ruleGroup, $request->getAll()); - $manager = $this->getManager(); - - /** @var RuleGroupTransformer $transformer */ - $transformer = app(RuleGroupTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($ruleGroup, $transformer, 'rule_groups'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } -} diff --git a/app/Api/V1/Controllers/Search/AccountController.php b/app/Api/V1/Controllers/Search/AccountController.php index d6b652b325..b1df06b854 100644 --- a/app/Api/V1/Controllers/Search/AccountController.php +++ b/app/Api/V1/Controllers/Search/AccountController.php @@ -1,8 +1,8 @@ mapAccountTypes($type); - Log::debug(sprintf('Going to search for "%s" in types', $query), $types); /** @var AccountSearch $search */ $search = app(AccountSearch::class); @@ -85,7 +86,6 @@ class AccountController extends Controller $accounts = $search->search(); - Log::debug(sprintf('Found %d accounts', $accounts->count())); /** @var AccountTransformer $transformer */ $transformer = app(AccountTransformer::class); diff --git a/app/Api/V1/Controllers/SummaryController.php b/app/Api/V1/Controllers/Summary/BasicController.php similarity index 98% rename from app/Api/V1/Controllers/SummaryController.php rename to app/Api/V1/Controllers/Summary/BasicController.php index 6f25ea6554..d18af60f8c 100644 --- a/app/Api/V1/Controllers/SummaryController.php +++ b/app/Api/V1/Controllers/Summary/BasicController.php @@ -1,8 +1,8 @@ . + */ + +namespace FireflyIII\Api\V1\Controllers\System; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\System\UpdateRequest; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Repositories\User\UserRepositoryInterface; +use FireflyIII\Support\Binder\EitherConfigKey; +use Illuminate\Http\JsonResponse; +use Log; + +/** + * Class ConfigurationController + */ +class ConfigurationController extends Controller +{ + private UserRepositoryInterface $repository; + + /** + * ConfigurationController constructor. + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->repository = app(UserRepositoryInterface::class); + + return $next($request); + } + ); + } + + /** + * @return JsonResponse + * @throws FireflyException + */ + public function index(): JsonResponse + { + try { + $dynamicData = $this->getDynamicConfiguration(); + } catch (FireflyException $e) { + Log::error($e->getMessage()); + Log::error($e->getTraceAsString()); + throw new FireflyException('200030: Could not load config variables.'); + } + $staticData = $this->getStaticConfiguration(); + $return = []; + foreach ($dynamicData as $key => $value) { + $return[] = [ + 'title' => sprintf('configuration.%s', $key), + 'value' => $value, + 'editable' => true, + ]; + } + foreach ($staticData as $key => $value) { + $return[] = [ + 'title' => $key, + 'value' => $value, + 'editable' => false, + ]; + } + + return response()->json($return); + } + + /** + * @param string $configKey + * + * @return JsonResponse + * @throws FireflyException + */ + public function show(string $configKey): JsonResponse + { + $data = []; + $dynamic = $this->getDynamicConfiguration(); + $shortKey = str_replace('configuration.', '', $configKey); + if ('configuration.' === substr($configKey, 0, 14)) { + $data = [ + 'title' => $configKey, + 'value' => $dynamic[$shortKey], + 'editable' => true, + ]; + } + if ('configuration.' !== substr($configKey, 0, 14)) { + $data = [ + 'title' => $configKey, + 'value' => config($configKey), + 'editable' => false, + ]; + } + + return response()->json(['data' => $data])->header('Content-Type', self::CONTENT_TYPE); + } + + /** + * Update the configuration. + * + * @param UpdateRequest $request + * @param string $name + * + * @return JsonResponse + */ + public function update(UpdateRequest $request, string $name): JsonResponse + { + if (!$this->repository->hasRole(auth()->user(), 'owner')) { + throw new FireflyException('200005: You need the "owner" role to do this.'); // @codeCoverageIgnore + } + $data = $request->getAll(); + $shortName = str_replace('configuration.','', $name); + + app('fireflyconfig')->set($shortName, $data['value']); + + // get updated config: + $newConfig = $this->getDynamicConfiguration(); + + + $data = [ + 'title' => $name, + 'value' => $newConfig[$shortName], + 'editable' => true, + ]; + + return response()->json(['data' => $data])->header('Content-Type', self::CONTENT_TYPE); + } + + + /** + * @return array + */ + private function getStaticConfiguration(): array + { + $list = EitherConfigKey::$static; + $return = []; + foreach ($list as $key) { + $return[$key] = config($key); + } + + return $return; + } + + + /** + * Get all config values. + * + * @return array + * @throws FireflyException + */ + private function getDynamicConfiguration(): array + { + $isDemoSite = app('fireflyconfig')->get('is_demo_site'); + $updateCheck = app('fireflyconfig')->get('permission_update_check'); + $lastCheck = app('fireflyconfig')->get('last_update_check'); + $singleUser = app('fireflyconfig')->get('single_user_mode'); + + return [ + 'is_demo_site' => null === $isDemoSite ? null : $isDemoSite->data, + 'permission_update_check' => null === $updateCheck ? null : (int)$updateCheck->data, + 'last_update_check' => null === $lastCheck ? null : (int)$lastCheck->data, + 'single_user_mode' => null === $singleUser ? null : $singleUser->data, + ]; + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/UserController.php b/app/Api/V1/Controllers/System/UserController.php similarity index 93% rename from app/Api/V1/Controllers/UserController.php rename to app/Api/V1/Controllers/System/UserController.php index c294deb019..75fca8d115 100644 --- a/app/Api/V1/Controllers/UserController.php +++ b/app/Api/V1/Controllers/System/UserController.php @@ -1,8 +1,8 @@ user(); + if($admin->id === $user->id) { + return response()->json([], 500); + } + if ($admin->id !== $user->id && $this->repository->hasRole($admin, 'owner')) { $this->repository->destroy($user); diff --git a/app/Api/V1/Controllers/TransactionController.php b/app/Api/V1/Controllers/TransactionController.php deleted file mode 100644 index f7addd8d0e..0000000000 --- a/app/Api/V1/Controllers/TransactionController.php +++ /dev/null @@ -1,394 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Api\V1\Controllers; - -use FireflyIII\Api\V1\Requests\TransactionStoreRequest; -use FireflyIII\Api\V1\Requests\TransactionUpdateRequest; -use FireflyIII\Events\DestroyedTransactionGroup; -use FireflyIII\Events\StoredTransactionGroup; -use FireflyIII\Events\UpdatedTransactionGroup; -use FireflyIII\Exceptions\DuplicateTransactionException; -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Helpers\Collector\GroupCollectorInterface; -use FireflyIII\Models\TransactionGroup; -use FireflyIII\Models\TransactionJournal; -use FireflyIII\Repositories\Journal\JournalAPIRepositoryInterface; -use FireflyIII\Repositories\Journal\JournalRepositoryInterface; -use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface; -use FireflyIII\Rules\IsDuplicateTransaction; -use FireflyIII\Support\Http\Api\TransactionFilter; -use FireflyIII\Transformers\AttachmentTransformer; -use FireflyIII\Transformers\PiggyBankEventTransformer; -use FireflyIII\Transformers\TransactionGroupTransformer; -use FireflyIII\Transformers\TransactionLinkTransformer; -use FireflyIII\User; -use Illuminate\Http\JsonResponse; -use Illuminate\Http\Request; -use Illuminate\Support\Collection; -use Illuminate\Validation\ValidationException; -use League\Fractal\Pagination\IlluminatePaginatorAdapter; -use League\Fractal\Resource\Collection as FractalCollection; -use League\Fractal\Resource\Item; -use Log; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Validator; - -/** - * Class TransactionController - */ -class TransactionController extends Controller -{ - use TransactionFilter; - - private TransactionGroupRepositoryInterface $groupRepository; - private JournalAPIRepositoryInterface $journalAPIRepository; - private JournalRepositoryInterface $repository; - - - /** - * TransactionController constructor. - * - * @codeCoverageIgnore - */ - public function __construct() - { - parent::__construct(); - $this->middleware( - function ($request, $next) { - /** @var User $admin */ - $admin = auth()->user(); - - $this->repository = app(JournalRepositoryInterface::class); - $this->groupRepository = app(TransactionGroupRepositoryInterface::class); - $this->journalAPIRepository = app(JournalAPIRepositoryInterface::class); - $this->repository->setUser($admin); - $this->groupRepository->setUser($admin); - $this->journalAPIRepository->setUser($admin); - - return $next($request); - } - ); - } - - /** - * @param TransactionGroup $transactionGroup - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function attachments(TransactionGroup $transactionGroup): JsonResponse - { - $manager = $this->getManager(); - $attachments = new Collection; - foreach ($transactionGroup->transactionJournals as $transactionJournal) { - $attachments = $this->journalAPIRepository->getAttachments($transactionJournal)->merge($attachments); - } - - /** @var AttachmentTransformer $transformer */ - $transformer = app(AttachmentTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($attachments, $transformer, 'attachments'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * @param TransactionJournal $transactionJournal - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function transactionLinks(TransactionJournal $transactionJournal): JsonResponse - { - $manager = $this->getManager(); - $journalLinks = $this->journalAPIRepository->getJournalLinks($transactionJournal); - - /** @var TransactionLinkTransformer $transformer */ - $transformer = app(TransactionLinkTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($journalLinks, $transformer, 'transaction_links'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Remove the specified resource from storage. - * - * @param TransactionGroup $transactionGroup - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function delete(TransactionGroup $transactionGroup): JsonResponse - { - $this->repository->destroyGroup($transactionGroup); - // trigger just after destruction - event(new DestroyedTransactionGroup($transactionGroup)); - - return response()->json([], 204); - } - - /** - * Remove the specified resource from storage. - * - * @param TransactionJournal $transactionJournal - * - * @codeCoverageIgnore - * @return JsonResponse - */ - public function deleteJournal(TransactionJournal $transactionJournal): JsonResponse - { - $this->repository->destroyJournal($transactionJournal); - - return response()->json([], 204); - } - - /** - * Show all transactions. - * - * @param Request $request - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function index(Request $request): JsonResponse - { - $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - $type = $request->get('type') ?? 'default'; - $this->parameters->set('type', $type); - - $types = $this->mapTransactionTypes($this->parameters->get('type')); - $manager = $this->getManager(); - /** @var User $admin */ - $admin = auth()->user(); - - // use new group collector: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector - ->setUser($admin) - // all info needed for the API: - ->withAPIInformation() - // set page size: - ->setLimit($pageSize) - // set page to retrieve - ->setPage($this->parameters->get('page')) - // set types of transactions to return. - ->setTypes($types); - - - if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { - $collector->setRange($this->parameters->get('start'), $this->parameters->get('end')); - } - $paginator = $collector->getPaginatedGroups(); - $paginator->setPath(route('api.v1.transactions.index') . $this->buildParams()); - $transactions = $paginator->getCollection(); - - /** @var TransactionGroupTransformer $transformer */ - $transformer = app(TransactionGroupTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($transactions, $transformer, 'transactions'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * @param TransactionGroup $transactionGroup - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function piggyBankEvents(TransactionGroup $transactionGroup): JsonResponse - { - $manager = $this->getManager(); - $events = new Collection; - foreach ($transactionGroup->transactionJournals as $transactionJournal) { - $events = $this->journalAPIRepository->getPiggyBankEvents($transactionJournal)->merge($events); - } - - /** @var PiggyBankEventTransformer $transformer */ - $transformer = app(PiggyBankEventTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($events, $transformer, 'piggy_bank_events'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Show a single transaction, by transaction journal. - * - * @param TransactionJournal $transactionJournal - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function showByJournal(TransactionJournal $transactionJournal): JsonResponse - { - return $this->show($transactionJournal->transactionGroup); - } - - /** - * Show a single transaction. - * - * @param TransactionGroup $transactionGroup - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function show(TransactionGroup $transactionGroup): JsonResponse - { - $manager = $this->getManager(); - /** @var User $admin */ - $admin = auth()->user(); - // use new group collector: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector - ->setUser($admin) - // filter on transaction group. - ->setTransactionGroup($transactionGroup) - // all info needed for the API: - ->withAPIInformation(); - - $selectedGroup = $collector->getGroups()->first(); - if (null === $selectedGroup) { - throw new NotFoundHttpException(); - } - /** @var TransactionGroupTransformer $transformer */ - $transformer = app(TransactionGroupTransformer::class); - $transformer->setParameters($this->parameters); - $resource = new Item($selectedGroup, $transformer, 'transactions'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Store a new transaction. - * - * @param TransactionStoreRequest $request - * - * @return JsonResponse - * @throws FireflyException|ValidationException - */ - public function store(TransactionStoreRequest $request): JsonResponse - { - Log::debug('Now in API TransactionController::store()'); - $data = $request->getAll(); - $data['user'] = auth()->user()->id; - - Log::channel('audit') - ->info('Store new transaction over API.', $data); - - try { - $transactionGroup = $this->groupRepository->store($data); - } catch (DuplicateTransactionException $e) { - Log::warning('Caught a duplicate transaction. Return error message.'); - $validator = Validator::make( - ['transactions' => [['description' => $e->getMessage()]]], ['transactions.0.description' => new IsDuplicateTransaction] - ); - throw new ValidationException($validator); - } catch (FireflyException $e) { - Log::warning('Caught an exception. Return error message.'); - Log::error($e->getMessage()); - $message = sprintf('Internal exception: %s', $e->getMessage()); - $validator = Validator::make(['transactions' => [['description' => $message]]], ['transactions.0.description' => new IsDuplicateTransaction]); - throw new ValidationException($validator); - } - app('preferences')->mark(); - event(new StoredTransactionGroup($transactionGroup, $data['apply_rules'] ?? true)); - - $manager = $this->getManager(); - /** @var User $admin */ - $admin = auth()->user(); - // use new group collector: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector - ->setUser($admin) - // filter on transaction group. - ->setTransactionGroup($transactionGroup) - // all info needed for the API: - ->withAPIInformation(); - - $selectedGroup = $collector->getGroups()->first(); - if (null === $selectedGroup) { - throw new FireflyException('Cannot find transaction. Possibly, a rule deleted this transaction after its creation.'); - } - /** @var TransactionGroupTransformer $transformer */ - $transformer = app(TransactionGroupTransformer::class); - $transformer->setParameters($this->parameters); - $resource = new Item($selectedGroup, $transformer, 'transactions'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - - /** - * Update a transaction. - * - * @param TransactionUpdateRequest $request - * @param TransactionGroup $transactionGroup - * - * @return JsonResponse - */ - public function update(TransactionUpdateRequest $request, TransactionGroup $transactionGroup): JsonResponse - { - Log::debug('Now in update routine.'); - $data = $request->getAll(); - $transactionGroup = $this->groupRepository->update($transactionGroup, $data); - $manager = $this->getManager(); - - app('preferences')->mark(); - event(new UpdatedTransactionGroup($transactionGroup, $data['apply_rules'] ?? true)); - - /** @var User $admin */ - $admin = auth()->user(); - // use new group collector: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector - ->setUser($admin) - // filter on transaction group. - ->setTransactionGroup($transactionGroup) - // all info needed for the API: - ->withAPIInformation(); - - $selectedGroup = $collector->getGroups()->first(); - if (null === $selectedGroup) { - throw new NotFoundHttpException(); // @codeCoverageIgnore - } - /** @var TransactionGroupTransformer $transformer */ - $transformer = app(TransactionGroupTransformer::class); - $transformer->setParameters($this->parameters); - $resource = new Item($selectedGroup, $transformer, 'transactions'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } -} diff --git a/app/Api/V1/Controllers/TransactionLinkController.php b/app/Api/V1/Controllers/TransactionLinkController.php deleted file mode 100644 index d4b3ade13d..0000000000 --- a/app/Api/V1/Controllers/TransactionLinkController.php +++ /dev/null @@ -1,215 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Api\V1\Controllers; - -use FireflyIII\Api\V1\Requests\TransactionLinkRequest; -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Models\TransactionJournalLink; -use FireflyIII\Repositories\Journal\JournalRepositoryInterface; -use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; -use FireflyIII\Support\Http\Api\TransactionFilter; -use FireflyIII\Transformers\TransactionLinkTransformer; -use FireflyIII\User; -use Illuminate\Http\JsonResponse; -use Illuminate\Http\Request; -use Illuminate\Pagination\LengthAwarePaginator; -use League\Fractal\Pagination\IlluminatePaginatorAdapter; -use League\Fractal\Resource\Collection as FractalCollection; -use League\Fractal\Resource\Item; - -/** - * Class TransactionLinkController - */ -class TransactionLinkController extends Controller -{ - use TransactionFilter; - - /** @var JournalRepositoryInterface The journal repository */ - private $journalRepository; - - /** @var LinkTypeRepositoryInterface The link type repository */ - private $repository; - - - /** - * TransactionLinkController constructor. - * - * @codeCoverageIgnore - */ - public function __construct() - { - parent::__construct(); - $this->middleware( - function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); - - $this->repository = app(LinkTypeRepositoryInterface::class); - $this->journalRepository = app(JournalRepositoryInterface::class); - - $this->repository->setUser($user); - $this->journalRepository->setUser($user); - - return $next($request); - } - ); - } - - /** - * Delete the resource. - * - * @param TransactionJournalLink $link - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function delete(TransactionJournalLink $link): JsonResponse - { - $this->repository->destroyLink($link); - - return response()->json([], 204); - } - - /** - * List all of them. - * - * @param Request $request - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function index(Request $request): JsonResponse - { - // create some objects: - $manager = $this->getManager(); - // read type from URI - $name = $request->get('name'); - - // types to get, page size: - $pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - $linkType = $this->repository->findByName($name); - - // get list of transaction links. Count it and split it. - $collection = $this->repository->getJournalLinks($linkType); - $count = $collection->count(); - $journalLinks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); - - // make paginator: - $paginator = new LengthAwarePaginator($journalLinks, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.transaction_links.index') . $this->buildParams()); - - /** @var TransactionLinkTransformer $transformer */ - $transformer = app(TransactionLinkTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new FractalCollection($journalLinks, $transformer, 'transaction_links'); - $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - - /** - * List single resource. - * - * @param TransactionJournalLink $journalLink - * - * @return JsonResponse - * @codeCoverageIgnore - */ - public function show(TransactionJournalLink $journalLink): JsonResponse - { - $manager = $this->getManager(); - - /** @var TransactionLinkTransformer $transformer */ - $transformer = app(TransactionLinkTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($journalLink, $transformer, 'transaction_links'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } - - /** - * Store new object. - * - * @param TransactionLinkRequest $request - * - * @return JsonResponse - * @throws FireflyException - */ - public function store(TransactionLinkRequest $request): JsonResponse - { - $manager = $this->getManager(); - $data = $request->getAll(); - $inward = $this->journalRepository->findNull($data['inward_id'] ?? 0); - $outward = $this->journalRepository->findNull($data['outward_id'] ?? 0); - if (null === $inward || null === $outward) { - throw new FireflyException('200024: Source or destination does not exist.'); - } - $data['direction'] = 'inward'; - - $journalLink = $this->repository->storeLink($data, $inward, $outward); - - /** @var TransactionLinkTransformer $transformer */ - $transformer = app(TransactionLinkTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($journalLink, $transformer, 'transaction_links'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - } - - /** - * Update object. - * - * @param TransactionLinkRequest $request - * @param TransactionJournalLink $journalLink - * - * @return JsonResponse - * @throws FireflyException - */ - public function update(TransactionLinkRequest $request, TransactionJournalLink $journalLink): JsonResponse - { - $manager = $this->getManager(); - $data = $request->getAll(); - $data['inward'] = $this->journalRepository->findNull($data['inward_id'] ?? 0); - $data['outward'] = $this->journalRepository->findNull($data['outward_id'] ?? 0); - if (null === $data['inward'] || null === $data['outward']) { - throw new FireflyException('200024: Source or destination does not exist.'); - } - $data['direction'] = 'inward'; - $journalLink = $this->repository->updateLink($journalLink, $data); - - /** @var TransactionLinkTransformer $transformer */ - $transformer = app(TransactionLinkTransformer::class); - $transformer->setParameters($this->parameters); - - $resource = new Item($journalLink, $transformer, 'transaction_links'); - - return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); - - } -} diff --git a/app/Api/V1/Controllers/User/PreferencesController.php b/app/Api/V1/Controllers/User/PreferencesController.php new file mode 100644 index 0000000000..255e993979 --- /dev/null +++ b/app/Api/V1/Controllers/User/PreferencesController.php @@ -0,0 +1,136 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Controllers\User; + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\User\PreferenceStoreRequest; +use FireflyIII\Api\V1\Requests\User\PreferenceUpdateRequest; +use FireflyIII\Models\Preference; +use FireflyIII\Transformers\PreferenceTransformer; +use Illuminate\Http\JsonResponse; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; + +/** + * Class PreferencesController + */ +class PreferencesController extends Controller +{ + public const DATE_FORMAT = 'Y-m-d'; + public const RESOURCE_KEY = 'preferences'; + + /** + * List all of them. + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function index(): JsonResponse + { + // TODO via repository. + $collection = auth()->user()->preferences()->get(); + $manager = $this->getManager(); + $count = $collection->count(); + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $preferences = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($preferences, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.preferences.index') . $this->buildParams()); + + /** @var PreferenceTransformer $transformer */ + $transformer = app(PreferenceTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($preferences, $transformer, self::RESOURCE_KEY); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + + } + + /** + * @param PreferenceStoreRequest $request + * + * @return JsonResponse + */ + public function store(PreferenceStoreRequest $request): JsonResponse + { + $manager = $this->getManager(); + $data = $request->getAll(); + $pref = app('preferences')->set($data['name'], $data['data']); + + /** @var PreferenceTransformer $transformer */ + $transformer = app(PreferenceTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($pref, $transformer, 'preferences'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + /** + * @param PreferenceUpdateRequest $request + * + * @return JsonResponse + */ + public function update(PreferenceUpdateRequest $request, Preference $preference): JsonResponse + { + $manager = $this->getManager(); + $data = $request->getAll(); + $pref = app('preferences')->set($preference->name, $data['data']); + + /** @var PreferenceTransformer $transformer */ + $transformer = app(PreferenceTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($pref, $transformer, 'preferences'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + /** + * Return a single preference by name. + * + * @param Preference $preference + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function show(Preference $preference): JsonResponse + { + $manager = $this->getManager(); + /** @var PreferenceTransformer $transformer */ + $transformer = app(PreferenceTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new Item($preference, $transformer, 'preferences'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + +} diff --git a/app/Api/V1/Controllers/Webhook/AttemptController.php b/app/Api/V1/Controllers/Webhook/AttemptController.php new file mode 100644 index 0000000000..25b1e76fc1 --- /dev/null +++ b/app/Api/V1/Controllers/Webhook/AttemptController.php @@ -0,0 +1,128 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Webhook; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Webhook; +use FireflyIII\Models\WebhookAttempt; +use FireflyIII\Models\WebhookMessage; +use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface; +use FireflyIII\Transformers\WebhookAttemptTransformer; +use FireflyIII\Transformers\WebhookMessageTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; + +/** + * Class AttemptController + */ +class AttemptController extends Controller +{ + private WebhookRepositoryInterface $repository; + public const RESOURCE_KEY = 'webhook_attempts'; + + /** + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $admin */ + $admin = auth()->user(); + + /** @var WebhookRepositoryInterface repository */ + $this->repository = app(WebhookRepositoryInterface::class); + $this->repository->setUser($admin); + + return $next($request); + } + ); + } + + /** + * @param Webhook $webhook + * + * @return JsonResponse + */ + public function index(Webhook $webhook, WebhookMessage $message): JsonResponse + { + if ($message->webhook_id !== $webhook->id) { + throw new FireflyException('Webhook and webhook message are no match'); + } + + $manager = $this->getManager(); + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $collection = $this->repository->getAttempts($message); + $count = $collection->count(); + $attempts = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($attempts, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.webhooks.attempts.index', [$webhook->id, $message->id]) . $this->buildParams()); + + /** @var WebhookAttemptTransformer $transformer */ + $transformer = app(WebhookAttemptTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($attempts, $transformer, 'webhook_attempts'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + /** + * Show single instance. + * + * @param Webhook $webhook + * @param WebhookMessage $message + * @param WebhookAttempt $attempt + * + * @return JsonResponse + * @throws FireflyException + */ + public function show(Webhook $webhook, WebhookMessage $message, WebhookAttempt $attempt): JsonResponse + { + if($message->webhook_id !== $webhook->id) { + throw new FireflyException('Webhook and webhook message are no match'); + } + if($attempt->webhook_message_id !== $message->id) { + throw new FireflyException('Webhook message and webhook attempt are no match'); + + } + + $manager = $this->getManager(); + + /** @var WebhookAttemptTransformer $transformer */ + $transformer = app(WebhookAttemptTransformer::class); + $transformer->setParameters($this->parameters); + $resource = new Item($attempt, $transformer, self::RESOURCE_KEY); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Webhook/DestroyController.php b/app/Api/V1/Controllers/Webhook/DestroyController.php new file mode 100644 index 0000000000..61fe2acec5 --- /dev/null +++ b/app/Api/V1/Controllers/Webhook/DestroyController.php @@ -0,0 +1,141 @@ +. + */ + +declare(strict_types=1); +/* + * DeleteController.php + * Copyright (c) 2020 james@firefly-iii.org + * + * This file is part of Firefly III (https://github.com/firefly-iii). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace FireflyIII\Api\V1\Controllers\Webhook; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Webhook; +use FireflyIII\Models\WebhookAttempt; +use FireflyIII\Models\WebhookMessage; +use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; + +/** + * Class DestroyController + */ +class DestroyController extends Controller +{ + private WebhookRepositoryInterface $repository; + + /** + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $admin */ + $admin = auth()->user(); + + /** @var WebhookRepositoryInterface repository */ + $this->repository = app(WebhookRepositoryInterface::class); + $this->repository->setUser($admin); + + return $next($request); + } + ); + } + + /** + * Remove the specified resource from storage. + * + * @param Webhook $webhook + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function destroy(Webhook $webhook): JsonResponse + { + $this->repository->destroy($webhook); + + return response()->json([], 204); + } + + /** + * Remove the specified resource from storage. + * + * @param Webhook $webhook + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function destroyMessage(Webhook $webhook, WebhookMessage $message): JsonResponse + { + if ($message->webhook_id !== $webhook->id) { + throw new FireflyException('Webhook and webhook message are no match'); + } + $this->repository->destroyMessage($message); + + return response()->json([], 204); + } + + /** + * Remove the specified resource from storage. + * + * @param Webhook $webhook + * + * @return JsonResponse + * @codeCoverageIgnore + */ + public function destroyAttempt(Webhook $webhook, WebhookMessage $message, WebhookAttempt $attempt): JsonResponse + { + if ($message->webhook_id !== $webhook->id) { + throw new FireflyException('Webhook and webhook message are no match'); + } + if($attempt->webhook_message_id !== $message->id) { + throw new FireflyException('Webhook message and webhook attempt are no match'); + + } + + $this->repository->destroyAttempt($attempt); + + return response()->json([], 204); + } + + + +} diff --git a/app/Api/V1/Controllers/Webhook/MessageController.php b/app/Api/V1/Controllers/Webhook/MessageController.php new file mode 100644 index 0000000000..25bab9f655 --- /dev/null +++ b/app/Api/V1/Controllers/Webhook/MessageController.php @@ -0,0 +1,116 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Webhook; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Webhook; +use FireflyIII\Models\WebhookMessage; +use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface; +use FireflyIII\Transformers\WebhookMessageTransformer; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Illuminate\Pagination\LengthAwarePaginator; +use League\Fractal\Pagination\IlluminatePaginatorAdapter; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; + +class MessageController extends Controller +{ + public const RESOURCE_KEY = 'webhook_messages'; + private WebhookRepositoryInterface $repository; + + /** + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $admin */ + $admin = auth()->user(); + + /** @var WebhookRepositoryInterface repository */ + $this->repository = app(WebhookRepositoryInterface::class); + $this->repository->setUser($admin); + + return $next($request); + } + ); + } + + /** + * @param Webhook $webhook + * + * @return JsonResponse + */ + public function index(Webhook $webhook): JsonResponse + { + $manager = $this->getManager(); + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $collection = $this->repository->getMessages($webhook); + + $count = $collection->count(); + $messages = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($messages, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.webhooks.messages.index', [$webhook->id]) . $this->buildParams()); + + /** @var WebhookMessageTransformer $transformer */ + $transformer = app(WebhookMessageTransformer::class); + $transformer->setParameters($this->parameters); + + $resource = new FractalCollection($messages, $transformer, 'webhook_messages'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + + /** + * Show single instance. + * + * @param Webhook $webhook + * @param WebhookMessage $message + * + * @return JsonResponse + * @throws FireflyException + */ + public function show(Webhook $webhook, WebhookMessage $message): JsonResponse + { + if($message->webhook_id !== $webhook->id) { + throw new FireflyException('Webhook and webhook message are no match'); + } + + $manager = $this->getManager(); + + /** @var WebhookMessageTransformer $transformer */ + $transformer = app(WebhookMessageTransformer::class); + $transformer->setParameters($this->parameters); + $resource = new Item($message, $transformer, self::RESOURCE_KEY); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Webhook/IndexController.php b/app/Api/V1/Controllers/Webhook/ShowController.php similarity index 62% rename from app/Api/V1/Controllers/Webhook/IndexController.php rename to app/Api/V1/Controllers/Webhook/ShowController.php index ddd98f15de..2fe432df79 100644 --- a/app/Api/V1/Controllers/Webhook/IndexController.php +++ b/app/Api/V1/Controllers/Webhook/ShowController.php @@ -1,8 +1,7 @@ repository->all(); - $manager = $this->getManager(); - $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; - $count = $webhooks->count(); - $bills = $webhooks->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + $manager = $this->getManager(); + $collection = $this->repository->all(); + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $count = $collection->count(); + $webhooks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: $paginator = new LengthAwarePaginator($webhooks, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.webhooks.index') . $this->buildParams()); /** @var WebhookTransformer $transformer */ $transformer = app(WebhookTransformer::class); $transformer->setParameters($this->parameters); - $resource = new FractalCollection($bills, $transformer, 'webhooks'); + $resource = new FractalCollection($webhooks, $transformer, self::RESOURCE_KEY); $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } -} + /** + * Show single instance. + * + * @param Webhook $webhook + * + * @return JsonResponse + */ + public function show(Webhook $webhook): JsonResponse + { + $manager = $this->getManager(); + + /** @var WebhookTransformer $transformer */ + $transformer = app(WebhookTransformer::class); + $transformer->setParameters($this->parameters); + $resource = new Item($webhook, $transformer, self::RESOURCE_KEY); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Webhook/CreateController.php b/app/Api/V1/Controllers/Webhook/StoreController.php similarity index 90% rename from app/Api/V1/Controllers/Webhook/CreateController.php rename to app/Api/V1/Controllers/Webhook/StoreController.php index 3de3ba88a6..73766a32b6 100644 --- a/app/Api/V1/Controllers/Webhook/CreateController.php +++ b/app/Api/V1/Controllers/Webhook/StoreController.php @@ -1,8 +1,7 @@ json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } - -} +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Webhook/DeleteController.php b/app/Api/V1/Controllers/Webhook/SubmitController.php similarity index 72% rename from app/Api/V1/Controllers/Webhook/DeleteController.php rename to app/Api/V1/Controllers/Webhook/SubmitController.php index 1edb050995..5e2f139d26 100644 --- a/app/Api/V1/Controllers/Webhook/DeleteController.php +++ b/app/Api/V1/Controllers/Webhook/SubmitController.php @@ -1,8 +1,7 @@ repository->destroy($webhook); + // count messages that can be sent. + $messages = $this->repository->getReadyMessages($webhook); + if (0 === $messages->count()) { + // nothing to send, return empty + return response()->json([], 204); + } + // send! + foreach ($messages as $message) { + SendWebhookMessage::dispatch($message)->afterResponse(); + } - return response()->json([], 204); + return response()->json([], 200); } - - -} +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Webhook/EditController.php b/app/Api/V1/Controllers/Webhook/UpdateController.php similarity index 91% rename from app/Api/V1/Controllers/Webhook/EditController.php rename to app/Api/V1/Controllers/Webhook/UpdateController.php index dcae942e05..ce4f371445 100644 --- a/app/Api/V1/Controllers/Webhook/EditController.php +++ b/app/Api/V1/Controllers/Webhook/UpdateController.php @@ -1,8 +1,7 @@ json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } -} + +} \ No newline at end of file diff --git a/app/Api/V1/Requests/BudgetLimitUpdateRequest.php b/app/Api/V1/Requests/BudgetLimitUpdateRequest.php deleted file mode 100644 index b83566a3db..0000000000 --- a/app/Api/V1/Requests/BudgetLimitUpdateRequest.php +++ /dev/null @@ -1,86 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Api\V1\Requests; - -use FireflyIII\Support\Request\ChecksLogin; -use FireflyIII\Support\Request\ConvertsDataTypes; -use Illuminate\Foundation\Http\FormRequest; - -/** - * Class BudgetLimitUpdateRequest - * - * @codeCoverageIgnore - */ -class BudgetLimitUpdateRequest extends FormRequest -{ - use ConvertsDataTypes, ChecksLogin; - - /** - * Get all data from the request. - * - * @return array - */ - public function getAll(): array - { - $data = [ - 'budget_id' => $this->integer('budget_id'), - 'start' => $this->date('start'), - 'end' => $this->date('end'), - 'amount' => $this->string('amount'), - 'currency_id' => $this->integer('currency_id'), - 'currency_code' => $this->string('currency_code'), - ]; - // if request has a budget already, drop the rule. - $budget = $this->route()->parameter('budget'); - if (null !== $budget) { - $data['budget_id'] = $budget->id; - } - return $data; - } - - /** - * The rules that the incoming request must be matched against. - * - * @return array - */ - public function rules(): array - { - $rules = [ - 'budget_id' => 'required|exists:budgets,id|belongsToUser:budgets,id', - 'start' => 'required|before:end|date', - 'end' => 'required|after:start|date', - 'amount' => 'required|gt:0', - 'currency_id' => 'numeric|exists:transaction_currencies,id', - 'currency_code' => 'min:3|max:3|exists:transaction_currencies,code', - ]; - // if request has a budget already, drop the rule. - $budget = $this->route()->parameter('budget'); - if (null !== $budget) { - unset($rules['budget_id']); - } - - return $rules; - } - -} diff --git a/app/Api/V1/Requests/DateRequest.php b/app/Api/V1/Requests/Data/DateRequest.php similarity index 94% rename from app/Api/V1/Requests/DateRequest.php rename to app/Api/V1/Requests/Data/DateRequest.php index d63c268089..c5ea73f04b 100644 --- a/app/Api/V1/Requests/DateRequest.php +++ b/app/Api/V1/Requests/Data/DateRequest.php @@ -1,8 +1,8 @@ . + */ + +namespace FireflyIII\Api\V1\Requests\Data\Export; + + +use Carbon\Carbon; +use FireflyIII\Models\AccountType; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Support\Request\ChecksLogin; +use FireflyIII\Support\Request\ConvertsDataTypes; +use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Collection; + +/** + * Class ExportRequest + */ +class ExportRequest extends FormRequest +{ + use ChecksLogin, ConvertsDataTypes; + + /** + * The rules that the incoming request must be matched against. + * + * @return array + */ + public function rules(): array + { + return [ + 'type' => 'in:csv', + 'accounts' => 'min:1', + 'start' => 'date|before:end', + 'end' => 'date|after:start', + ]; + } + + public function getAll(): array + { + $result = [ + 'start' => $this->date('start') ?? Carbon::now()->subYear(), + 'end' => $this->date('end') ?? Carbon::now(), + 'type' => $this->string('type'), + ]; + $parts = explode(',', $this->string('accounts')); + $repository = app(AccountRepositoryInterface::class); + $repository->setUser(auth()->user()); + + $accounts = new Collection; + foreach ($parts as $part) { + $accountId = (int)$part; + if (0 !== $accountId) { + $account = $repository->findNull($accountId); + if (null !== $account && AccountType::ASSET === $account->accountType->type) { + $accounts->push($account); + } + } + } + $result['accounts'] = $accounts; + + return $result; + } +} \ No newline at end of file diff --git a/app/Api/V1/Requests/Insight/GenericRequest.php b/app/Api/V1/Requests/Insight/GenericRequest.php new file mode 100644 index 0000000000..0ebac45ef1 --- /dev/null +++ b/app/Api/V1/Requests/Insight/GenericRequest.php @@ -0,0 +1,307 @@ +date('start'); + $date->startOfDay(); + + return $date; + } + + /** + * @return Carbon + */ + public function getEnd(): Carbon + { + $date = $this->date('end'); + $date->endOfDay(); + + return $date; + } + + /** + * + */ + private function parseBudgets(): void + { + if (null === $this->budgets) { + $this->budgets = new Collection; + } + if (0 !== $this->budgets->count()) { + return; + } + $repository = app(BudgetRepositoryInterface::class); + $repository->setUser(auth()->user()); + $array = $this->get('budgets'); + if (is_array($array)) { + foreach ($array as $budgetId) { + $budgetId = (int)$budgetId; + $budget = $repository->findNull($budgetId); + if (null !== $budgetId) { + $this->budgets->push($budget); + } + } + } + } + + /** + * @return Collection + */ + public function getBudgets(): Collection + { + $this->parseBudgets(); + + return $this->budgets; + } + + /** + * @return Collection + */ + public function getCategories(): Collection + { + $this->parseCategories(); + + return $this->categories; + } + + /** + * @return Collection + */ + public function getBills(): Collection + { + $this->parseBills(); + + return $this->bills; + } + + /** + * @return Collection + */ + public function getTags(): Collection + { + $this->parseTags(); + + return $this->tags; + } + + + /** + * @return Collection + */ + public function getAssetAccounts(): Collection + { + $this->parseAccounts(); + $return = new Collection; + /** @var Account $account */ + foreach ($this->accounts as $account) { + $type = $account->accountType->type; + if (in_array($type, [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE])) { + $return->push($account); + } + } + + return $return; + } + + /** + * @return Collection + */ + public function getExpenseAccounts(): Collection + { + $this->parseAccounts(); + $return = new Collection; + /** @var Account $account */ + foreach ($this->accounts as $account) { + $type = $account->accountType->type; + if (in_array($type, [AccountType::EXPENSE])) { + $return->push($account); + } + } + + return $return; + } + + /** + * @return Collection + */ + public function getRevenueAccounts(): Collection + { + $this->parseAccounts(); + $return = new Collection; + /** @var Account $account */ + foreach ($this->accounts as $account) { + $type = $account->accountType->type; + if (in_array($type, [AccountType::REVENUE])) { + $return->push($account); + } + } + + return $return; + } + + /** + * Get all data from the request. + * + * @return array + */ + public function getAll(): array + { + return [ + 'start' => $this->date('start'), + 'end' => $this->date('end'), + ]; + } + + /** + * The rules that the incoming request must be matched against. + * + * @return array + */ + public function rules(): array + { + // this is cheating but it works: + $this->accounts = new Collection; + $this->budgets = new Collection; + $this->categories = new Collection; + $this->bills = new Collection; + $this->tags = new Collection; + + return [ + 'start' => 'required|date', + 'end' => 'required|date|after:start', + ]; + } + + /** + * + */ + private function parseAccounts(): void + { + if (null === $this->accounts) { + $this->accounts = new Collection; + } + if (0 !== $this->accounts->count()) { + return; + } + $repository = app(AccountRepositoryInterface::class); + $repository->setUser(auth()->user()); + $array = $this->get('accounts'); + if (is_array($array)) { + foreach ($array as $accountId) { + $accountId = (int)$accountId; + $account = $repository->findNull($accountId); + if (null !== $account) { + $this->accounts->push($account); + } + } + } + } + + /** + * + */ + private function parseBills(): void + { + if (null === $this->bills) { + $this->bills = new Collection; + } + if (0 !== $this->bills->count()) { + return; + } + $repository = app(BillRepositoryInterface::class); + $repository->setUser(auth()->user()); + $array = $this->get('bills'); + if (is_array($array)) { + foreach ($array as $billId) { + $billId = (int)$billId; + $bill = $repository->find($billId); + if (null !== $billId) { + $this->bills->push($bill); + } + } + } + } + + /** + * + */ + private function parseCategories(): void + { + if (null === $this->categories) { + $this->categories = new Collection; + } + if (0 !== $this->categories->count()) { + return; + } + $repository = app(CategoryRepositoryInterface::class); + $repository->setUser(auth()->user()); + $array = $this->get('categories'); + if (is_array($array)) { + foreach ($array as $categoryId) { + $categoryId = (int)$categoryId; + $category = $repository->findNull($categoryId); + if (null !== $categoryId) { + $this->categories->push($category); + } + } + } + } + + /** + * + */ + private function parseTags(): void + { + if (null === $this->tags) { + $this->tags = new Collection; + } + if (0 !== $this->tags->count()) { + return; + } + $repository = app(TagRepositoryInterface::class); + $repository->setUser(auth()->user()); + $array = $this->get('tags'); + if (is_array($array)) { + foreach ($array as $tagId) { + $tagId = (int)$tagId; + $tag = $repository->findNull($tagId); + if (null !== $tagId) { + $this->tags->push($tag); + } + } + } + } +} \ No newline at end of file diff --git a/app/Api/V1/Requests/AccountStoreRequest.php b/app/Api/V1/Requests/Models/Account/StoreRequest.php similarity index 95% rename from app/Api/V1/Requests/AccountStoreRequest.php rename to app/Api/V1/Requests/Models/Account/StoreRequest.php index 29274f6e57..0049d2b350 100644 --- a/app/Api/V1/Requests/AccountStoreRequest.php +++ b/app/Api/V1/Requests/Models/Account/StoreRequest.php @@ -1,8 +1,8 @@ appendLocationData($data, null); - if ('liability' === $data['account_type']) { + if ('liability' === $data['account_type'] || 'liabilities' === $data['account_type']) { $data['opening_balance'] = bcmul($this->string('liability_amount'), '-1'); $data['opening_balance_date'] = $this->date('liability_start_date'); $data['account_type'] = $this->string('liability_type'); diff --git a/app/Api/V1/Requests/AccountUpdateRequest.php b/app/Api/V1/Requests/Models/Account/UpdateRequest.php similarity index 89% rename from app/Api/V1/Requests/AccountUpdateRequest.php rename to app/Api/V1/Requests/Models/Account/UpdateRequest.php index e9a9267638..0a110e8d16 100644 --- a/app/Api/V1/Requests/AccountUpdateRequest.php +++ b/app/Api/V1/Requests/Models/Account/UpdateRequest.php @@ -1,8 +1,8 @@ $includeNetWorth, 'account_type' => $this->nullableString('type'), 'account_type_id' => null, - 'currency_id' => $this->nullableInteger('currency_id'), - 'order' => $this->integer('order'), - 'currency_code' => $this->nullableString('currency_code'), 'virtual_balance' => $this->nullableString('virtual_balance'), 'iban' => $this->nullableString('iban'), 'BIC' => $this->nullableString('bic'), 'account_number' => $this->nullableString('account_number'), 'account_role' => $this->nullableString('account_role'), + 'liability_type' => $this->nullableString('liability_type'), 'opening_balance' => $this->nullableString('opening_balance'), 'opening_balance_date' => $this->date('opening_balance_date'), 'cc_type' => $this->nullableString('credit_card_type'), @@ -77,6 +75,15 @@ class AccountUpdateRequest extends FormRequest 'interest' => $this->nullableString('interest'), 'interest_period' => $this->nullableString('interest_period'), ]; + if (null !== $this->get('order')) { + $data['order'] = $this->integer('order'); + } + if (null !== $this->get('currency_id')) { + $data['currency_id'] = $this->nullableInteger('currency_id'); + } + if (null !== $this->get('currency_code')) { + $data['currency_code'] = $this->nullableString('currency_code'); + } $data = $this->appendLocationData($data, null); @@ -120,8 +127,6 @@ class AccountUpdateRequest extends FormRequest 'credit_card_type' => sprintf('in:%s|required_if:account_role,ccAsset', $ccPaymentTypes), 'monthly_payment_date' => 'date' . '|required_if:account_role,ccAsset|required_if:credit_card_type,monthlyFull', 'liability_type' => 'required_if:type,liability|in:loan,debt,mortgage', - 'liability_amount' => 'required_if:type,liability|min:0|numeric', - 'liability_start_date' => 'required_if:type,liability|date', 'interest' => 'required_if:type,liability|between:0,100|numeric', 'interest_period' => 'required_if:type,liability|in:daily,monthly,yearly', 'notes' => 'min:0|max:65536', diff --git a/app/Api/V1/Requests/AttachmentStoreRequest.php b/app/Api/V1/Requests/Models/Attachment/StoreRequest.php similarity index 83% rename from app/Api/V1/Requests/AttachmentStoreRequest.php rename to app/Api/V1/Requests/Models/Attachment/StoreRequest.php index 76e75eeedb..97c43cd933 100644 --- a/app/Api/V1/Requests/AttachmentStoreRequest.php +++ b/app/Api/V1/Requests/Models/Attachment/StoreRequest.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\Attachment; use FireflyIII\Rules\IsValidAttachmentModel; use FireflyIII\Support\Request\ChecksLogin; @@ -29,11 +29,11 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; /** - * Class AttachmentStoreRequest + * Class StoreRequest * * @codeCoverageIgnore */ -class AttachmentStoreRequest extends FormRequest +class StoreRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin; @@ -45,11 +45,11 @@ class AttachmentStoreRequest extends FormRequest public function getAll(): array { return [ - 'filename' => $this->string('filename'), - 'title' => $this->string('title'), - 'notes' => $this->nlString('notes'), - 'model' => $this->string('attachable_type'), - 'model_id' => $this->integer('attachable_id'), + 'filename' => $this->string('filename'), + 'title' => $this->string('title'), + 'notes' => $this->nlString('notes'), + 'attachable_type' => $this->string('attachable_type'), + 'attachable_id' => $this->integer('attachable_id'), ]; } diff --git a/app/Api/V1/Requests/AttachmentUpdateRequest.php b/app/Api/V1/Requests/Models/Attachment/UpdateRequest.php similarity index 54% rename from app/Api/V1/Requests/AttachmentUpdateRequest.php rename to app/Api/V1/Requests/Models/Attachment/UpdateRequest.php index 9b12e58008..288fd27354 100644 --- a/app/Api/V1/Requests/AttachmentUpdateRequest.php +++ b/app/Api/V1/Requests/Models/Attachment/UpdateRequest.php @@ -21,18 +21,19 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\Attachment; +use FireflyIII\Rules\IsValidAttachmentModel; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; /** - * ClassAttachmentUpdateRequest + * Class UpdateRequest * * @codeCoverageIgnore */ -class AttachmentUpdateRequest extends FormRequest +class UpdateRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin; @@ -43,13 +44,15 @@ class AttachmentUpdateRequest extends FormRequest */ public function getAll(): array { - return [ - 'filename' => $this->string('filename'), - 'title' => $this->string('title'), - 'notes' => $this->nlString('notes'), - 'model' => $this->string('attachable_type'), - 'model_id' => $this->integer('attachable_id'), + $fields = [ + 'filename' => ['filename', 'string'], + 'title' => ['title', 'string'], + 'notes' => ['notes', 'nlString'], + 'attachable_type' => ['attachable_type', 'string'], + 'attachable_id' => ['attachable_id', 'integer'], ]; + + return $this->getAllData($fields); } /** @@ -59,10 +62,23 @@ class AttachmentUpdateRequest extends FormRequest */ public function rules(): array { + $models = config('firefly.valid_attachment_models'); + $models = array_map( + + static function (string $className) { + return str_replace('FireflyIII\\Models\\', '', $className); + }, $models + ); + $models = implode(',', $models); + $model = $this->string('attachable_type'); + + return [ - 'filename' => 'between:1,255', - 'title' => 'between:1,255', - 'notes' => 'between:1,65000', + 'filename' => 'between:1,255', + 'title' => 'between:1,255', + 'notes' => 'between:1,65000', + 'attachable_type' => sprintf('in:%s', $models), + 'attachable_id' => ['numeric', new IsValidAttachmentModel($model)], ]; } } diff --git a/app/Api/V1/Requests/AvailableBudgetRequest.php b/app/Api/V1/Requests/Models/AvailableBudget/Request.php similarity index 50% rename from app/Api/V1/Requests/AvailableBudgetRequest.php rename to app/Api/V1/Requests/Models/AvailableBudget/Request.php index 0cd2e738aa..c421d2a9df 100644 --- a/app/Api/V1/Requests/AvailableBudgetRequest.php +++ b/app/Api/V1/Requests/Models/AvailableBudget/Request.php @@ -21,23 +21,23 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\AvailableBudget; +use Carbon\Carbon; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Validator; /** - * Class AvailableBudgetRequest + * Class Request * * @codeCoverageIgnore */ -class AvailableBudgetRequest extends FormRequest +class Request extends FormRequest { use ConvertsDataTypes, ChecksLogin; - - /** * Get all data from the request. * @@ -45,13 +45,16 @@ class AvailableBudgetRequest extends FormRequest */ public function getAll(): array { - return [ - 'currency_id' => $this->integer('currency_id'), - 'currency_code' => $this->string('currency_code'), - 'amount' => $this->string('amount'), - 'start' => $this->date('start'), - 'end' => $this->date('end'), + // this is the way: + $fields = [ + 'currency_id' => ['currency_id', 'integer'], + 'currency_code' => ['currency_code', 'string'], + 'amount' => ['amount', 'string'], + 'start' => ['start', 'date'], + 'end' => ['end', 'date'], ]; + + return $this->getAllData($fields); } /** @@ -64,11 +67,35 @@ class AvailableBudgetRequest extends FormRequest return [ 'currency_id' => 'numeric|exists:transaction_currencies,id', 'currency_code' => 'min:3|max:3|exists:transaction_currencies,code', - 'amount' => 'required|numeric|gt:0', - 'start' => 'required|date|before:end', - 'end' => 'required|date|after:start', + 'amount' => 'numeric|gt:0', + 'start' => 'date', + 'end' => 'date', ]; } + /** + * Configure the validator instance with special rules for after the basic validation rules. + * + * @param Validator $validator + * + * @return void + */ + public function withValidator(Validator $validator): void + { + $validator->after( + function (Validator $validator) { + // validate start before end only if both are there. + $data = $validator->getData(); + if (array_key_exists('start', $data) && array_key_exists('end', $data)) { + $start = new Carbon($data['start']); + $end = new Carbon($data['end']); + if ($end->isBefore($start)) { + $validator->errors()->add('end', (string)trans('validation.date_after')); + } + } + } + ); + } + } diff --git a/app/Api/V1/Requests/BillStoreRequest.php b/app/Api/V1/Requests/Models/Bill/StoreRequest.php similarity index 67% rename from app/Api/V1/Requests/BillStoreRequest.php rename to app/Api/V1/Requests/Models/Bill/StoreRequest.php index d9705538ab..f3f0e1ed6c 100644 --- a/app/Api/V1/Requests/BillStoreRequest.php +++ b/app/Api/V1/Requests/Models/Bill/StoreRequest.php @@ -22,20 +22,21 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\Bill; use FireflyIII\Rules\IsBoolean; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Validator; +use Log; /** - * Class BillStoreRequest + * Class StoreRequest * * @codeCoverageIgnore */ -class BillStoreRequest extends FormRequest +class StoreRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin; @@ -46,24 +47,24 @@ class BillStoreRequest extends FormRequest */ public function getAll(): array { - $active = true; - if (null !== $this->get('active')) { - $active = $this->boolean('active'); - } - - return [ - 'name' => $this->string('name'), - 'amount_min' => $this->string('amount_min'), - 'amount_max' => $this->string('amount_max'), - 'currency_id' => $this->integer('currency_id'), - 'currency_code' => $this->string('currency_code'), - 'date' => $this->date('date'), - 'repeat_freq' => $this->string('repeat_freq'), - 'skip' => $this->integer('skip'), - 'active' => $active, - 'order' => $this->integer('order'), - 'notes' => $this->nlString('notes'), + Log::debug('Raw fields in Bill StoreRequest', $this->all()); + $fields = [ + 'name' => ['name', 'string'], + 'amount_min' => ['amount_min', 'string'], + 'amount_max' => ['amount_max', 'string'], + 'currency_id' => ['currency_id', 'integer'], + 'currency_code' => ['currency_code', 'string'], + 'date' => ['date', 'date'], + 'repeat_freq' => ['repeat_freq', 'string'], + 'skip' => ['skip', 'integer'], + 'active' => ['active', 'boolean'], + 'order' => ['order', 'integer'], + 'notes' => ['notes', 'nlString'], + 'object_group_id' => ['object_group_id', 'integer'], + 'object_group_title' => ['object_group_title', 'string'], ]; + + return $this->getAllData($fields); } /** @@ -99,10 +100,10 @@ class BillStoreRequest extends FormRequest $validator->after( static function (Validator $validator) { $data = $validator->getData(); - $min = (float) ($data['amount_min'] ?? 0); - $max = (float) ($data['amount_max'] ?? 0); + $min = (float)($data['amount_min'] ?? 0); + $max = (float)($data['amount_max'] ?? 0); if ($min > $max) { - $validator->errors()->add('amount_min', (string) trans('validation.amount_min_over_max')); + $validator->errors()->add('amount_min', (string)trans('validation.amount_min_over_max')); } } ); diff --git a/app/Api/V1/Requests/BillUpdateRequest.php b/app/Api/V1/Requests/Models/Bill/UpdateRequest.php similarity index 63% rename from app/Api/V1/Requests/BillUpdateRequest.php rename to app/Api/V1/Requests/Models/Bill/UpdateRequest.php index 7838ee46df..3f01053bba 100644 --- a/app/Api/V1/Requests/BillUpdateRequest.php +++ b/app/Api/V1/Requests/Models/Bill/UpdateRequest.php @@ -22,7 +22,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\Bill; use FireflyIII\Rules\IsBoolean; use FireflyIII\Support\Request\ChecksLogin; @@ -31,11 +31,11 @@ use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Validator; /** - * Class BillUpdateRequest + * Class UpdateRequest * * @codeCoverageIgnore */ -class BillUpdateRequest extends FormRequest +class UpdateRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin; @@ -46,24 +46,23 @@ class BillUpdateRequest extends FormRequest */ public function getAll(): array { - $active = true; - if (null !== $this->get('active')) { - $active = $this->boolean('active'); - } - - return [ - 'name' => $this->string('name'), - 'amount_min' => $this->string('amount_min'), - 'amount_max' => $this->string('amount_max'), - 'currency_id' => $this->integer('currency_id'), - 'currency_code' => $this->string('currency_code'), - 'date' => $this->date('date'), - 'repeat_freq' => $this->string('repeat_freq'), - 'skip' => $this->integer('skip'), - 'active' => $active, - 'order' => $this->integer('order'), - 'notes' => $this->nlString('notes'), + $fields = [ + 'name' => ['name', 'string'], + 'amount_min' => ['amount_min', 'string'], + 'amount_max' => ['amount_max', 'string'], + 'currency_id' => ['currency_id', 'integer'], + 'currency_code' => ['currency_code', 'string'], + 'date' => ['date', 'date'], + 'repeat_freq' => ['repeat_freq', 'string'], + 'skip' => ['skip', 'integer'], + 'active' => ['active', 'boolean'], + 'order' => ['order', 'integer'], + 'notes' => ['notes', 'nlString'], + 'object_group_id' => ['object_group_id', 'integer'], + 'object_group_title' => ['object_group_title', 'string'], ]; + + return $this->getAllData($fields); } /** @@ -73,7 +72,8 @@ class BillUpdateRequest extends FormRequest */ public function rules(): array { - $bill = $this->route()->parameter('bill'); + $bill = $this->route()->parameter('bill'); + return [ 'name' => sprintf('between:1,255|uniqueObjectForUser:bills,name,%d', $bill->id), 'amount_min' => 'numeric|gt:0', @@ -100,10 +100,12 @@ class BillUpdateRequest extends FormRequest $validator->after( static function (Validator $validator) { $data = $validator->getData(); - $min = (float) ($data['amount_min'] ?? 0); - $max = (float) ($data['amount_max'] ?? 0); - if ($min > $max) { - $validator->errors()->add('amount_min', (string) trans('validation.amount_min_over_max')); + if (array_key_exists('amount_min', $data) && array_key_exists('amount_max', $data)) { + $min = (float)($data['amount_min'] ?? 0); + $max = (float)($data['amount_max'] ?? 0); + if ($min > $max) { + $validator->errors()->add('amount_min', (string)trans('validation.amount_min_over_max')); + } } } ); diff --git a/app/Api/V1/Requests/BudgetStoreRequest.php b/app/Api/V1/Requests/Models/Budget/StoreRequest.php similarity index 59% rename from app/Api/V1/Requests/BudgetStoreRequest.php rename to app/Api/V1/Requests/Models/Budget/StoreRequest.php index fb1f121098..ecaa8f28b8 100644 --- a/app/Api/V1/Requests/BudgetStoreRequest.php +++ b/app/Api/V1/Requests/Models/Budget/StoreRequest.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\Budget; use FireflyIII\Rules\IsBoolean; use FireflyIII\Support\Request\ChecksLogin; @@ -31,11 +31,11 @@ use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Validator; /** - * Class BudgetStoreRequest + * Class StoreRequest * * @codeCoverageIgnore */ -class BudgetStoreRequest extends FormRequest +class StoreRequest extends FormRequest { use ConvertsDataTypes, ValidatesAutoBudgetRequest, ChecksLogin; @@ -46,21 +46,20 @@ class BudgetStoreRequest extends FormRequest */ public function getAll(): array { - $active = true; - if (null !== $this->get('active')) { - $active = $this->boolean('active'); - } + $fields = [ + 'name' => ['name', 'string'], + 'active' => ['active', 'boolean'], + 'order' => ['active', 'integer'], - return [ - 'name' => $this->string('name'), - 'active' => $active, - 'order' => 0, - 'auto_budget_type' => $this->string('auto_budget_type'), - 'transaction_currency_id' => $this->integer('auto_budget_currency_id'), - 'transaction_currency_code' => $this->string('auto_budget_currency_code'), - 'auto_budget_amount' => $this->string('auto_budget_amount'), - 'auto_budget_period' => $this->string('auto_budget_period'), + // auto budget currency: + 'currency_id' => ['auto_budget_currency_id', 'integer'], + 'currency_code' => ['auto_budget_currency_code', 'string'], + 'auto_budget_type' => ['auto_budget_type', 'string'], + 'auto_budget_amount' => ['auto_budget_amount', 'string'], + 'auto_budget_period' => ['auto_budget_period', 'string'], ]; + + return $this->getAllData($fields); } /** @@ -71,17 +70,17 @@ class BudgetStoreRequest extends FormRequest public function rules(): array { return [ - 'name' => 'required|between:1,100|uniqueObjectForUser:budgets,name', - 'active' => [new IsBoolean], - 'auto_budget_type' => 'in:reset,rollover,none', - 'auto_budget_currency_id' => 'exists:transaction_currencies,id', - 'auto_budget_currency_code' => 'exists:transaction_currencies,code', - 'auto_budget_amount' => 'min:0|max:1000000000', - 'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly', + 'name' => 'required|between:1,100|uniqueObjectForUser:budgets,name', + 'active' => [new IsBoolean], + 'currency_id' => 'exists:transaction_currencies,id', + 'currency_code' => 'exists:transaction_currencies,code', + // auto budget info + 'auto_budget_type' => 'in:reset,rollover,none', + 'auto_budget_amount' => 'min:0|max:1000000000', + 'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly', ]; } - /** * Configure the validator instance with special rules for after the basic validation rules. * diff --git a/app/Api/V1/Requests/BudgetUpdateRequest.php b/app/Api/V1/Requests/Models/Budget/UpdateRequest.php similarity index 72% rename from app/Api/V1/Requests/BudgetUpdateRequest.php rename to app/Api/V1/Requests/Models/Budget/UpdateRequest.php index 17b3bbd13c..d8b149fcda 100644 --- a/app/Api/V1/Requests/BudgetUpdateRequest.php +++ b/app/Api/V1/Requests/Models/Budget/UpdateRequest.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\Budget; use FireflyIII\Rules\IsBoolean; use FireflyIII\Support\Request\ChecksLogin; @@ -31,11 +31,11 @@ use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Validator; /** - * Class BudgetUpdateRequest + * Class UpdateRequest * * @codeCoverageIgnore */ -class BudgetUpdateRequest extends FormRequest +class UpdateRequest extends FormRequest { use ConvertsDataTypes, ValidatesAutoBudgetRequest, ChecksLogin; @@ -46,21 +46,19 @@ class BudgetUpdateRequest extends FormRequest */ public function getAll(): array { - $active = true; - if (null !== $this->get('active')) { - $active = $this->boolean('active'); - } - - return [ - 'name' => $this->string('name'), - 'active' => $active, - 'order' => 0, - 'auto_budget_type' => $this->string('auto_budget_type'), - 'transaction_currency_id' => $this->integer('auto_budget_currency_id'), - 'transaction_currency_code' => $this->string('auto_budget_currency_code'), - 'auto_budget_amount' => $this->string('auto_budget_amount'), - 'auto_budget_period' => $this->string('auto_budget_period'), + // this is the way: + $fields = [ + 'name' => ['name', 'string'], + 'active' => ['active', 'boolean'], + 'order' => ['order', 'integer'], + 'currency_id' => ['auto_budget_currency_id', 'integer'], + 'currency_code' => ['auto_budget_currency_code', 'string'], + 'auto_budget_type' => ['auto_budget_type', 'string'], + 'auto_budget_amount' => ['auto_budget_amount', 'string'], + 'auto_budget_period' => ['auto_budget_period', 'string'], ]; + + return $this->getAllData($fields); } /** @@ -73,7 +71,7 @@ class BudgetUpdateRequest extends FormRequest $budget = $this->route()->parameter('budget'); return [ - 'name' => sprintf('required|between:1,100|uniqueObjectForUser:budgets,name,%d', $budget->id), + 'name' => sprintf('between:1,100|uniqueObjectForUser:budgets,name,%d', $budget->id), 'active' => [new IsBoolean], 'auto_budget_type' => 'in:reset,rollover,none', 'auto_budget_currency_id' => 'exists:transaction_currencies,id', diff --git a/app/Api/V1/Requests/BudgetLimitStoreRequest.php b/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php similarity index 88% rename from app/Api/V1/Requests/BudgetLimitStoreRequest.php rename to app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php index 944df63de1..e97d870d66 100644 --- a/app/Api/V1/Requests/BudgetLimitStoreRequest.php +++ b/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php @@ -21,18 +21,18 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\BudgetLimit; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; /** - * Class BudgetLimitStoreRequest + * Class StoreRequest * * @codeCoverageIgnore */ -class BudgetLimitStoreRequest extends FormRequest +class StoreRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin; @@ -44,7 +44,6 @@ class BudgetLimitStoreRequest extends FormRequest public function getAll(): array { return [ - 'budget_id' => $this->integer('budget_id'), 'start' => $this->date('start'), 'end' => $this->date('end'), 'amount' => $this->string('amount'), @@ -61,7 +60,6 @@ class BudgetLimitStoreRequest extends FormRequest public function rules(): array { return [ - 'budget_id' => 'required|exists:budgets,id|belongsToUser:budgets,id', 'start' => 'required|before:end|date', 'end' => 'required|after:start|date', 'amount' => 'required|gt:0', diff --git a/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php b/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php new file mode 100644 index 0000000000..019a849e80 --- /dev/null +++ b/app/Api/V1/Requests/Models/BudgetLimit/UpdateRequest.php @@ -0,0 +1,100 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Requests\Models\BudgetLimit; + +use Carbon\Carbon; +use FireflyIII\Support\Request\ChecksLogin; +use FireflyIII\Support\Request\ConvertsDataTypes; +use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Validator; + +/** + * Class UpdateRequest + * + * @codeCoverageIgnore + */ +class UpdateRequest extends FormRequest +{ + use ConvertsDataTypes, ChecksLogin; + + /** + * Get all data from the request. + * + * @return array + */ + public function getAll(): array + { + $fields = [ + 'start' => ['start', 'date'], + 'end' => ['end', 'date'], + 'amount' => ['amount', 'string'], + 'currency_id' => ['currency_id', 'integer'], + 'currency_code' => ['currency_code', 'string'], + ]; + + return $this->getAllData($fields); + } + + /** + * The rules that the incoming request must be matched against. + * + * @return array + */ + public function rules(): array + { + return [ + 'start' => 'date', + 'end' => 'date', + 'amount' => 'gt:0', + 'currency_id' => 'numeric|exists:transaction_currencies,id', + 'currency_code' => 'min:3|max:3|exists:transaction_currencies,code', + ]; + } + + /** + * Configure the validator instance with special rules for after the basic validation rules. + * + * @param Validator $validator + * TODO duplicate code. + * + * @return void + */ + public function withValidator(Validator $validator): void + { + $validator->after( + function (Validator $validator) { + // validate start before end only if both are there. + $data = $validator->getData(); + if (array_key_exists('start', $data) && array_key_exists('end', $data)) { + $start = new Carbon($data['start']); + $end = new Carbon($data['end']); + if ($end->isBefore($start)) { + $validator->errors()->add('end', (string)trans('validation.date_after')); + } + } + } + ); + } + +} diff --git a/app/Api/V1/Requests/CategoryStoreRequest.php b/app/Api/V1/Requests/Models/Category/StoreRequest.php similarity index 93% rename from app/Api/V1/Requests/CategoryStoreRequest.php rename to app/Api/V1/Requests/Models/Category/StoreRequest.php index 405739c833..ddd5bc3216 100644 --- a/app/Api/V1/Requests/CategoryStoreRequest.php +++ b/app/Api/V1/Requests/Models/Category/StoreRequest.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\Category; use FireflyIII\Rules\ZeroOrMore; use FireflyIII\Support\Request\ChecksLogin; @@ -29,11 +29,11 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; /** - * Class CategoryRequest + * Class StoreRequest * * @codeCoverageIgnore */ -class CategoryStoreRequest extends FormRequest +class StoreRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin; diff --git a/app/Api/V1/Requests/CategoryUpdateRequest.php b/app/Api/V1/Requests/Models/Category/UpdateRequest.php similarity index 75% rename from app/Api/V1/Requests/CategoryUpdateRequest.php rename to app/Api/V1/Requests/Models/Category/UpdateRequest.php index 29b6e003fe..befa561963 100644 --- a/app/Api/V1/Requests/CategoryUpdateRequest.php +++ b/app/Api/V1/Requests/Models/Category/UpdateRequest.php @@ -21,18 +21,18 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\Category; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; /** - * Class CategoryUpdateRequest + * Class UpdateRequest * * @codeCoverageIgnore */ -class CategoryUpdateRequest extends FormRequest +class UpdateRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin; @@ -43,16 +43,11 @@ class CategoryUpdateRequest extends FormRequest */ public function getAll(): array { - $notes = null; - $all = $this->all(); - if (array_key_exists('notes', $all)) { - $notes = $this->nlString('notes'); - } - - return [ - 'name' => $this->string('name'), - 'notes' => $notes, + $fields = [ + 'name' => ['name', 'string'], + 'notes' => ['notes', 'nlString'] ]; + return $this->getAllData($fields); } /** @@ -65,7 +60,7 @@ class CategoryUpdateRequest extends FormRequest $category = $this->route()->parameter('category'); return [ - 'name' => sprintf('required|between:1,100|uniqueObjectForUser:categories,name,%d', $category->id), + 'name' => sprintf('between:1,100|uniqueObjectForUser:categories,name,%d', $category->id), ]; } } diff --git a/app/Api/V1/Requests/ObjectGroupUpdateRequest.php b/app/Api/V1/Requests/Models/ObjectGroup/UpdateRequest.php similarity index 85% rename from app/Api/V1/Requests/ObjectGroupUpdateRequest.php rename to app/Api/V1/Requests/Models/ObjectGroup/UpdateRequest.php index fd51661414..e0e8c282df 100644 --- a/app/Api/V1/Requests/ObjectGroupUpdateRequest.php +++ b/app/Api/V1/Requests/Models/ObjectGroup/UpdateRequest.php @@ -22,18 +22,18 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\ObjectGroup; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; /** - * Class AccountObjectGroupUpdateRequestUpdateRequest + * Class UpdateRequest * * @codeCoverageIgnore */ -class ObjectGroupUpdateRequest extends FormRequest +class UpdateRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin; @@ -42,10 +42,11 @@ class ObjectGroupUpdateRequest extends FormRequest */ public function getUpdateData(): array { - return [ - 'title' => $this->string('title'), - 'order' => $this->integer('order'), + $fields = [ + 'title' => ['title', 'string'], + 'order' =>['order', 'integer'] ]; + return $this->getAllData($fields); } /** diff --git a/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php b/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php new file mode 100644 index 0000000000..7a3e264d49 --- /dev/null +++ b/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php @@ -0,0 +1,86 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Requests\Models\PiggyBank; + +use FireflyIII\Support\Request\ChecksLogin; +use FireflyIII\Support\Request\ConvertsDataTypes; +use Illuminate\Foundation\Http\FormRequest; + +/** + * Class StoreRequest + * + * @codeCoverageIgnore + */ +class StoreRequest extends FormRequest +{ + use ConvertsDataTypes, ChecksLogin; + + /** + * Get all data from the request. + * + * @return array + */ + public function getAll(): array + { + $fields = [ + 'order' => ['order', 'integer'], + ]; + $data = $this->getAllData($fields); + + + $data['name'] = $this->string('name'); + $data['account_id'] = $this->integer('account_id'); + $data['targetamount'] = $this->string('target_amount'); + $data['current_amount'] = $this->string('current_amount'); + $data['startdate'] = $this->date('start_date'); + $data['targetdate'] = $this->date('target_date'); + $data['notes'] = $this->nlString('notes'); + $data['object_group_id'] = $this->integer('object_group_id'); + $data['object_group_title'] = $this->string('object_group_title'); + + return $data; + + } + + /** + * The rules that the incoming request must be matched against. + * + * @return array + */ + public function rules(): array + { + return [ + 'name' => 'required|between:1,255|uniquePiggyBankForUser', + 'current_amount' => ['numeric', 'gte:0', 'lte:target_amount'], + 'account_id' => 'required|numeric|belongsToUser:accounts,id', + 'object_group_id' => 'numeric|belongsToUser:object_groups,id', + 'object_group_title' => 'between:1,255', + 'target_amount' => ['numeric', 'gte:0', 'lte:target_amount', 'required'], + 'start_date' => 'date|nullable', + 'target_date' => 'date|nullable|after:start_date', + 'notes' => 'max:65000', + ]; + } + +} diff --git a/app/Api/V1/Requests/PiggyBankUpdateRequest.php b/app/Api/V1/Requests/Models/PiggyBank/UpdateRequest.php similarity index 66% rename from app/Api/V1/Requests/PiggyBankUpdateRequest.php rename to app/Api/V1/Requests/Models/PiggyBank/UpdateRequest.php index e62203b4c0..e5816dc018 100644 --- a/app/Api/V1/Requests/PiggyBankUpdateRequest.php +++ b/app/Api/V1/Requests/Models/PiggyBank/UpdateRequest.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\PiggyBank; use FireflyIII\Rules\IsAssetAccountId; use FireflyIII\Rules\LessThanPiggyTarget; @@ -30,11 +30,11 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; /** - * Class PiggyBankUpdateRequest + * Class UpdateRequest * * @codeCoverageIgnore */ -class PiggyBankUpdateRequest extends FormRequest +class UpdateRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin; @@ -45,24 +45,19 @@ class PiggyBankUpdateRequest extends FormRequest */ public function getAll(): array { - // if the value isn't present, dont return it at all. - // TODO this should be the way to collect fields for all API things. - // TODO make sure piggy bank uses 'start_date' etc. until right up to DB update. - // TODO can we configure this and return it from config? - - // TODO this is the way. $fields = [ - 'name' => ['name', 'string'], - 'account_id' => ['account_id', 'integer'], - 'targetamount' => ['target_amount', 'string'], - 'current_amount' => ['current_amount', 'string'], - 'startdate' => ['start_date', 'date'], - 'targetdate' => ['target_date', 'string'], - 'notes' => ['notes', 'nlString'], - 'order' => ['order', 'integer'], - 'object_group' => ['object_group', 'string'], - 'object_group_id' => ['object_group_id', 'integer'], + 'name' => ['name', 'string'], + 'account_id' => ['account_id', 'integer'], + 'targetamount' => ['target_amount', 'string'], + 'current_amount' => ['current_amount', 'string'], + 'startdate' => ['start_date', 'date'], + 'targetdate' => ['target_date', 'string'], + 'notes' => ['notes', 'nlString'], + 'order' => ['order', 'integer'], + 'object_group_title' => ['object_group_title', 'string'], + 'object_group_id' => ['object_group_id', 'integer'], ]; + return $this->getAllData($fields); } diff --git a/app/Api/V1/Requests/RecurrenceStoreRequest.php b/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php similarity index 77% rename from app/Api/V1/Requests/RecurrenceStoreRequest.php rename to app/Api/V1/Requests/Models/Recurrence/StoreRequest.php index f9c880bac1..476b8ab4f4 100644 --- a/app/Api/V1/Requests/RecurrenceStoreRequest.php +++ b/app/Api/V1/Requests/Models/Recurrence/StoreRequest.php @@ -1,7 +1,7 @@ get('active')) { - $active = $this->boolean('active'); - } - if (null !== $this->get('apply_rules')) { - $applyRules = $this->boolean('apply_rules'); - } + $fields = [ + 'type' => ['type', 'string'], + 'title' => ['title', 'string'], + 'description' => ['description', 'string'], + 'first_date' => ['first_date', 'date'], + 'repeat_until' => ['repeat_until', 'date'], + 'nr_of_repetitions' => ['nr_of_repetitions', 'integer'], + 'apply_rules' => ['apply_rules', 'boolean'], + 'active' => ['active', 'boolean'], + 'notes' => ['notes', 'nlString'], + ]; + $recurrence = $this->getAllData($fields); return [ - 'recurrence' => [ - 'type' => $this->string('type'), - 'title' => $this->string('title'), - 'description' => $this->string('description'), - 'first_date' => $this->date('first_date'), - 'repeat_until' => $this->date('repeat_until'), - 'repetitions' => $this->integer('nr_of_repetitions'), - 'apply_rules' => $applyRules, - 'active' => $active, - ], + 'recurrence' => $recurrence, 'transactions' => $this->getTransactionData(), 'repetitions' => $this->getRepetitionData(), ]; @@ -93,7 +87,7 @@ class RecurrenceStoreRequest extends FormRequest } /** @var array $transaction */ foreach ($transactions as $transaction) { - $return[] = $this->getSingleRecurrenceData($transaction); + $return[] = $this->getSingleTransactionData($transaction); } return $return; @@ -115,12 +109,21 @@ class RecurrenceStoreRequest extends FormRequest } /** @var array $repetition */ foreach ($repetitions as $repetition) { - $return[] = [ - 'type' => $repetition['type'], - 'moment' => $repetition['moment'], - 'skip' => (int) $repetition['skip'], - 'weekend' => (int) $repetition['weekend'], - ]; + $current = []; + if (array_key_exists('type', $repetition)) { + $current['type'] = $repetition['type']; + } + if (array_key_exists('moment', $repetition)) { + $current['moment'] = $repetition['moment']; + } + if (array_key_exists('skip', $repetition)) { + $current['skip'] = (int)$repetition['skip']; + } + if (array_key_exists('weekend', $repetition)) { + $current['weekend'] = (int)$repetition['weekend']; + } + + $return[] = $current; } return $return; @@ -142,12 +145,12 @@ class RecurrenceStoreRequest extends FormRequest 'first_date' => 'required|date', 'apply_rules' => [new IsBoolean], 'active' => [new IsBoolean], - 'repeat_until' => sprintf('date|after:%s', $today->format('Y-m-d')), + 'repeat_until' => 'date', 'nr_of_repetitions' => 'numeric|between:1,31', 'repetitions.*.type' => 'required|in:daily,weekly,ndom,monthly,yearly', 'repetitions.*.moment' => 'between:0,10', - 'repetitions.*.skip' => 'required|numeric|between:0,31', - 'repetitions.*.weekend' => 'required|numeric|min:1|max:4', + 'repetitions.*.skip' => 'numeric|between:0,31', + 'repetitions.*.weekend' => 'numeric|min:1|max:4', 'transactions.*.description' => 'required|between:1,255', 'transactions.*.amount' => 'required|numeric|gt:0', 'transactions.*.foreign_amount' => 'numeric|gt:0', @@ -184,6 +187,7 @@ class RecurrenceStoreRequest extends FormRequest { $validator->after( function (Validator $validator) { + $this->validateRecurringConfig($validator); $this->validateOneRecurrenceTransaction($validator); $this->validateOneRepetition($validator); $this->validateRecurrenceRepetition($validator); diff --git a/app/Api/V1/Requests/RecurrenceUpdateRequest.php b/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php similarity index 70% rename from app/Api/V1/Requests/RecurrenceUpdateRequest.php rename to app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php index e98c2f5349..b632c4ddcc 100644 --- a/app/Api/V1/Requests/RecurrenceUpdateRequest.php +++ b/app/Api/V1/Requests/Models/Recurrence/UpdateRequest.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\Recurrence; use FireflyIII\Models\Recurrence; use FireflyIII\Rules\BelongsUser; @@ -36,14 +36,13 @@ use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Validator; /** - * Class RecurrenceUpdateRequest + * Class UpdateRequest */ -class RecurrenceUpdateRequest extends FormRequest +class UpdateRequest extends FormRequest { use ConvertsDataTypes, RecurrenceValidation, TransactionValidation, CurrencyValidation, GetRecurrenceData, ChecksLogin; - /** * Get all data from the request. * @@ -51,46 +50,46 @@ class RecurrenceUpdateRequest extends FormRequest */ public function getAll(): array { - $active = null; - $applyRules = null; - if (null !== $this->get('active')) { - $active = $this->boolean('active'); + // this is the way: + $fields = [ + 'title' => ['title', 'string'], + 'description' => ['description', 'string'], + 'first_date' => ['first_date', 'date'], + 'repeat_until' => ['repeat_until', 'date'], + 'nr_of_repetitions' => ['nr_of_repetitions', 'integer'], + 'apply_rules' => ['apply_rules', 'boolean'], + 'active' => ['active', 'boolean'], + 'notes' => ['notes', 'string'], + ]; + $reps = $this->getRepetitionData(); + $transactions = $this->getTransactionData(); + $return = [ + 'recurrence' => $this->getAllData($fields), + ]; + if (null !== $reps) { + $return['repetitions'] = $reps; } - if (null !== $this->get('apply_rules')) { - $applyRules = $this->boolean('apply_rules'); + if (null !== $transactions) { + $return['transactions'] = $transactions; } - return [ - 'recurrence' => [ - 'type' => $this->nullableString('type'), - 'title' => $this->nullableString('title'), - 'description' => $this->nullableString('description'), - 'first_date' => $this->date('first_date'), - 'notes' => $this->nullableNlString('notes'), - 'repeat_until' => $this->date('repeat_until'), - 'nr_of_repetitions' => $this->nullableInteger('nr_of_repetitions'), - 'apply_rules' => $applyRules, - 'active' => $active, - ], - 'transactions' => $this->getTransactionData(), - 'repetitions' => $this->getRepetitionData(), - ]; + return $return; } /** * Returns the transaction data as it is found in the submitted data. It's a complex method according to code * standards but it just has a lot of ??-statements because of the fields that may or may not exist. * - * @return array + * @return array|null */ - private function getTransactionData(): array + private function getTransactionData(): ?array { $return = []; // transaction data: /** @var array $transactions */ $transactions = $this->get('transactions'); if (null === $transactions) { - return []; + return null; } /** @var array $transaction */ foreach ($transactions as $transaction) { @@ -103,25 +102,39 @@ class RecurrenceUpdateRequest extends FormRequest /** * Returns the repetition data as it is found in the submitted data. * - * @return array + * @return array|null */ - private function getRepetitionData(): array + private function getRepetitionData(): ?array { $return = []; // repetition data: /** @var array $repetitions */ $repetitions = $this->get('repetitions'); if (null === $repetitions) { - return []; + return null; } /** @var array $repetition */ foreach ($repetitions as $repetition) { - $return[] = [ - 'type' => $repetition['type'], - 'moment' => $repetition['moment'], - 'skip' => (int) $repetition['skip'], - 'weekend' => (int) $repetition['weekend'], - ]; + $current = []; + if (array_key_exists('type', $repetition)) { + $current['type'] = $repetition['type']; + } + + if (array_key_exists('moment', $repetition)) { + $current['moment'] = (string)$repetition['moment']; + } + + if (array_key_exists('skip', $repetition)) { + $current['skip'] = (int)$repetition['skip']; + } + + if (array_key_exists('weekend', $repetition)) { + $current['weekend'] = (int)$repetition['weekend']; + } + $return[] = $current; + } + if (0 === count($return)) { + return null; } return $return; @@ -138,7 +151,6 @@ class RecurrenceUpdateRequest extends FormRequest $recurrence = $this->route()->parameter('recurrence'); return [ - 'type' => 'in:withdrawal,transfer,deposit', 'title' => sprintf('between:1,255|uniqueObjectForUser:recurrences,title,%d', $recurrence->id), 'description' => 'between:1,65000', 'first_date' => 'date', @@ -148,11 +160,11 @@ class RecurrenceUpdateRequest extends FormRequest 'nr_of_repetitions' => 'numeric|between:1,31', 'repetitions.*.type' => 'in:daily,weekly,ndom,monthly,yearly', 'repetitions.*.moment' => 'between:0,10', - 'repetitions.*.skip' => 'required|numeric|between:0,31', - 'repetitions.*.weekend' => 'required|numeric|min:1|max:4', + 'repetitions.*.skip' => 'numeric|between:0,31', + 'repetitions.*.weekend' => 'numeric|min:1|max:4', - 'transactions.*.description' => 'required|between:1,255', - 'transactions.*.amount' => 'required|numeric|gt:0', + 'transactions.*.description' => 'between:1,255', + 'transactions.*.amount' => 'numeric|gt:0', 'transactions.*.foreign_amount' => 'numeric|gt:0', 'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id', 'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code', @@ -186,8 +198,8 @@ class RecurrenceUpdateRequest extends FormRequest { $validator->after( function (Validator $validator) { - $this->validateOneRecurrenceTransaction($validator); - $this->validateOneRepetitionUpdate($validator); + //$this->validateOneRecurrenceTransaction($validator); + //$this->validateOneRepetitionUpdate($validator); $this->validateRecurrenceRepetition($validator); $this->validateRepetitionMoment($validator); $this->validateForeignCurrencyInformation($validator); diff --git a/app/Api/V1/Requests/RuleStoreRequest.php b/app/Api/V1/Requests/Models/Rule/StoreRequest.php similarity index 76% rename from app/Api/V1/Requests/RuleStoreRequest.php rename to app/Api/V1/Requests/Models/Rule/StoreRequest.php index 51e0c1da1e..94446a49f2 100644 --- a/app/Api/V1/Requests/RuleStoreRequest.php +++ b/app/Api/V1/Requests/Models/Rule/StoreRequest.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\Rule; use FireflyIII\Rules\IsBoolean; use FireflyIII\Support\Request\ChecksLogin; @@ -33,14 +33,13 @@ use function is_array; /** - * Class RuleStoreRequest + * Class StoreRequest */ -class RuleStoreRequest extends FormRequest +class StoreRequest extends FormRequest { use ConvertsDataTypes, GetRuleConfiguration, ChecksLogin; - /** * Get all data from the request. * @@ -48,31 +47,23 @@ class RuleStoreRequest extends FormRequest */ public function getAll(): array { - $strict = true; - $active = true; - $stopProcessing = false; - if (null !== $this->get('active')) { - $active = $this->boolean('active'); - } - if (null !== $this->get('strict')) { - $strict = $this->boolean('strict'); - } - if (null !== $this->get('stop_processing')) { - $stopProcessing = $this->boolean('stop_processing'); - } - - return [ - 'title' => $this->string('title'), - 'description' => $this->string('description'), - 'rule_group_id' => $this->integer('rule_group_id'), - 'rule_group_title' => $this->string('rule_group_title'), - 'trigger' => $this->string('trigger'), - 'strict' => $strict, - 'stop_processing' => $stopProcessing, - 'active' => $active, - 'triggers' => $this->getRuleTriggers(), - 'actions' => $this->getRuleActions(), + $fields = [ + 'title' => ['title', 'string'], + 'description' => ['description', 'string'], + 'rule_group_id' => ['rule_group_id', 'integer'], + 'order' => ['order', 'integer'], + 'rule_group_title' => ['rule_group_title', 'string'], + 'trigger' => ['trigger', 'string'], + 'strict' => ['strict', 'boolean'], + 'stop_processing' => ['stop_processing', 'boolean'], + 'active' => ['active', 'boolean'], ]; + $data = $this->getAllData($fields); + + $data['triggers'] = $this->getRuleTriggers(); + $data['actions'] = $this->getRuleActions(); + + return $data; } /** @@ -87,8 +78,8 @@ class RuleStoreRequest extends FormRequest $return[] = [ 'type' => $trigger['type'], 'value' => $trigger['value'], - 'active' => $this->convertBoolean((string) ($trigger['active'] ?? 'false')), - 'stop_processing' => $this->convertBoolean((string) ($trigger['stop_processing'] ?? 'false')), + 'active' => $this->convertBoolean((string)($trigger['active'] ?? 'false')), + 'stop_processing' => $this->convertBoolean((string)($trigger['stop_processing'] ?? 'false')), ]; } } @@ -108,8 +99,8 @@ class RuleStoreRequest extends FormRequest $return[] = [ 'type' => $action['type'], 'value' => $action['value'], - 'active' => $this->convertBoolean((string) ($action['active'] ?? 'false')), - 'stop_processing' => $this->convertBoolean((string) ($action['stop_processing'] ?? 'false')), + 'active' => $this->convertBoolean((string)($action['active'] ?? 'false')), + 'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')), ]; } } @@ -134,7 +125,7 @@ class RuleStoreRequest extends FormRequest return [ 'title' => 'required|between:1,100|uniqueObjectForUser:rules,title', 'description' => 'between:1,5000|nullable', - 'rule_group_id' => 'required|belongsToUser:rule_groups|required_without:rule_group_title', + 'rule_group_id' => 'belongsToUser:rule_groups|required_without:rule_group_title', 'rule_group_title' => 'nullable|between:1,255|required_without:rule_group_id|belongsToUser:rule_groups,title', 'trigger' => 'required|in:store-journal,update-journal', 'triggers.*.type' => 'required|in:' . implode(',', $validTriggers), @@ -178,8 +169,8 @@ class RuleStoreRequest extends FormRequest $data = $validator->getData(); $triggers = $data['triggers'] ?? []; // need at least one trigger - if (0 === count($triggers)) { - $validator->errors()->add('title', (string) trans('validation.at_least_one_trigger')); + if (!is_countable($triggers) || 0 === count($triggers)) { + $validator->errors()->add('title', (string)trans('validation.at_least_one_trigger')); } } @@ -193,8 +184,8 @@ class RuleStoreRequest extends FormRequest $data = $validator->getData(); $actions = $data['actions'] ?? []; // need at least one trigger - if (0 === count($actions)) { - $validator->errors()->add('title', (string) trans('validation.at_least_one_action')); + if (!is_countable($actions) || 0 === count($actions)) { + $validator->errors()->add('title', (string)trans('validation.at_least_one_action')); } } } diff --git a/app/Api/V1/Requests/RuleTestRequest.php b/app/Api/V1/Requests/Models/Rule/TestRequest.php similarity index 87% rename from app/Api/V1/Requests/RuleTestRequest.php rename to app/Api/V1/Requests/Models/Rule/TestRequest.php index 1b0e5acad0..bcfb564c37 100644 --- a/app/Api/V1/Requests/RuleTestRequest.php +++ b/app/Api/V1/Requests/Models/Rule/TestRequest.php @@ -22,7 +22,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\Rule; use Carbon\Carbon; @@ -31,14 +31,13 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; /** - * Class RuleTestRequest + * Class TestRequest */ -class RuleTestRequest extends FormRequest +class TestRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin; - /** * @return array */ @@ -58,7 +57,7 @@ class RuleTestRequest extends FormRequest */ private function getPage(): int { - return 0 === (int) $this->query('page') ? 1 : (int) $this->query('page'); + return 0 === (int)$this->query('page') ? 1 : (int)$this->query('page'); } @@ -73,11 +72,11 @@ class RuleTestRequest extends FormRequest } /** - * @return string + * @return array */ - private function getAccounts(): string + private function getAccounts(): array { - return (string) $this->query('accounts'); + return $this->get('accounts'); } /** diff --git a/app/Api/V1/Requests/RuleTriggerRequest.php b/app/Api/V1/Requests/Models/Rule/TriggerRequest.php similarity index 89% rename from app/Api/V1/Requests/RuleTriggerRequest.php rename to app/Api/V1/Requests/Models/Rule/TriggerRequest.php index d3e8adc892..8fe6b92fcf 100644 --- a/app/Api/V1/Requests/RuleTriggerRequest.php +++ b/app/Api/V1/Requests/Models/Rule/TriggerRequest.php @@ -22,7 +22,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\Rule; use Carbon\Carbon; @@ -31,9 +31,9 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; /** - * Class RuleTriggerRequest + * Class TriggerRequest */ -class RuleTriggerRequest extends FormRequest +class TriggerRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin; @@ -62,11 +62,11 @@ class RuleTriggerRequest extends FormRequest } /** - * @return string + * @return array */ - private function getAccounts(): string + private function getAccounts(): array { - return (string) $this->query('accounts'); + return $this->get('accounts'); } /** diff --git a/app/Api/V1/Requests/RuleUpdateRequest.php b/app/Api/V1/Requests/Models/Rule/UpdateRequest.php similarity index 77% rename from app/Api/V1/Requests/RuleUpdateRequest.php rename to app/Api/V1/Requests/Models/Rule/UpdateRequest.php index 0c2ba23ad7..f1d3ef43be 100644 --- a/app/Api/V1/Requests/RuleUpdateRequest.php +++ b/app/Api/V1/Requests/Models/Rule/UpdateRequest.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\Rule; use FireflyIII\Rules\IsBoolean; use FireflyIII\Support\Request\ChecksLogin; @@ -33,14 +33,12 @@ use function is_array; /** - * Class RuleUpdateRequest + * Class UpdateRequest */ -class RuleUpdateRequest extends FormRequest +class UpdateRequest extends FormRequest { use ConvertsDataTypes, GetRuleConfiguration, ChecksLogin; - - /** * Get all data from the request. * @@ -48,31 +46,27 @@ class RuleUpdateRequest extends FormRequest */ public function getAll(): array { - $strict = null; - $active = null; - $stopProcessing = null; - if (null !== $this->get('active')) { - $active = $this->boolean('active'); + $fields = [ + 'title' => ['title', 'string'], + 'description' => ['description', 'nlString'], + 'rule_group_id' => ['rule_group_id', 'integer'], + 'trigger' => ['trigger', 'string'], + 'strict' => ['strict', 'boolean'], + 'stop_processing' => ['stop_processing', 'boolean'], + 'active' => ['active', 'boolean'], + ]; + + $return = $this->getAllData($fields); + $triggers = $this->getRuleTriggers(); + $actions = $this->getRuleActions(); + if(null !== $triggers) { + $return['triggers'] = $triggers; } - if (null !== $this->get('strict')) { - $strict = $this->boolean('strict'); - } - if (null !== $this->get('stop_processing')) { - $stopProcessing = $this->boolean('stop_processing'); + if(null !== $actions) { + $return['actions'] = $actions; } - return [ - 'title' => $this->nullableString('title'), - 'description' => $this->nullableString('description'), - 'rule_group_id' => $this->nullableInteger('rule_group_id'), - 'rule_group_title' => $this->nullableString('rule_group_title'), - 'trigger' => $this->nullableString('trigger'), - 'strict' => $strict, - 'stop_processing' => $stopProcessing, - 'active' => $active, - 'triggers' => $this->getRuleTriggers(), - 'actions' => $this->getRuleActions(), - ]; + return $return; } /** @@ -87,11 +81,13 @@ class RuleUpdateRequest extends FormRequest $return = []; if (is_array($triggers)) { foreach ($triggers as $trigger) { + $active = array_key_exists('active', $trigger) ? $trigger['active'] : true; + $stopProcessing= array_key_exists('stop_processing', $trigger) ? $trigger['stop_processing'] : false; $return[] = [ 'type' => $trigger['type'], 'value' => $trigger['value'], - 'active' => $this->convertBoolean((string) ($trigger['active'] ?? 'false')), - 'stop_processing' => $this->convertBoolean((string) ($trigger['stop_processing'] ?? 'false')), + 'active' => $active, + 'stop_processing' => $stopProcessing, ]; } } @@ -114,8 +110,8 @@ class RuleUpdateRequest extends FormRequest $return[] = [ 'type' => $action['type'], 'value' => $action['value'], - 'active' => $this->convertBoolean((string) ($action['active'] ?? 'false')), - 'stop_processing' => $this->convertBoolean((string) ($action['stop_processing'] ?? 'false')), + 'active' => $this->convertBoolean((string)($action['active'] ?? 'false')), + 'stop_processing' => $this->convertBoolean((string)($action['stop_processing'] ?? 'false')), ]; } } @@ -186,7 +182,7 @@ class RuleUpdateRequest extends FormRequest $triggers = $data['triggers'] ?? null; // need at least one trigger if (is_array($triggers) && 0 === count($triggers)) { - $validator->errors()->add('title', (string) trans('validation.at_least_one_trigger')); + $validator->errors()->add('title', (string)trans('validation.at_least_one_trigger')); } } @@ -201,7 +197,7 @@ class RuleUpdateRequest extends FormRequest $actions = $data['actions'] ?? null; // need at least one action if (is_array($actions) && 0 === count($actions)) { - $validator->errors()->add('title', (string) trans('validation.at_least_one_action')); + $validator->errors()->add('title', (string)trans('validation.at_least_one_action')); } } } diff --git a/app/Api/V1/Requests/RuleGroupStoreRequest.php b/app/Api/V1/Requests/Models/RuleGroup/StoreRequest.php similarity index 87% rename from app/Api/V1/Requests/RuleGroupStoreRequest.php rename to app/Api/V1/Requests/Models/RuleGroup/StoreRequest.php index be906b3daa..5f61b7257a 100644 --- a/app/Api/V1/Requests/RuleGroupStoreRequest.php +++ b/app/Api/V1/Requests/Models/RuleGroup/StoreRequest.php @@ -21,9 +21,8 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\RuleGroup; -use FireflyIII\Models\RuleGroup; use FireflyIII\Rules\IsBoolean; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; @@ -32,9 +31,9 @@ use Illuminate\Foundation\Http\FormRequest; /** * @codeCoverageIgnore - * Class RuleGroupStoreRequest + * Class StoreRequest */ -class RuleGroupStoreRequest extends FormRequest +class StoreRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin; @@ -46,15 +45,19 @@ class RuleGroupStoreRequest extends FormRequest public function getAll(): array { $active = true; - + $order = 31337; if (null !== $this->get('active')) { $active = $this->boolean('active'); } + if (null !== $this->get('order')) { + $order = $this->integer('order'); + } return [ 'title' => $this->string('title'), 'description' => $this->string('description'), 'active' => $active, + 'order' => $order, ]; } diff --git a/app/Api/V1/Requests/RuleGroupTestRequest.php b/app/Api/V1/Requests/Models/RuleGroup/TestRequest.php similarity index 89% rename from app/Api/V1/Requests/RuleGroupTestRequest.php rename to app/Api/V1/Requests/Models/RuleGroup/TestRequest.php index 9d4063cadf..ef7d1bedb8 100644 --- a/app/Api/V1/Requests/RuleGroupTestRequest.php +++ b/app/Api/V1/Requests/Models/RuleGroup/TestRequest.php @@ -22,7 +22,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\RuleGroup; use Carbon\Carbon; @@ -32,9 +32,9 @@ use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Collection; /** - * Class RuleGroupTestRequest + * Class TestRequest */ -class RuleGroupTestRequest extends FormRequest +class TestRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin; @@ -63,11 +63,11 @@ class RuleGroupTestRequest extends FormRequest } /** - * @return Collection + * @return array */ - private function getAccounts(): string + private function getAccounts(): array { - return (string) $this->query('accounts'); + return $this->get('accounts'); } /** diff --git a/app/Api/V1/Requests/RuleGroupTriggerRequest.php b/app/Api/V1/Requests/Models/RuleGroup/TriggerRequest.php similarity index 88% rename from app/Api/V1/Requests/RuleGroupTriggerRequest.php rename to app/Api/V1/Requests/Models/RuleGroup/TriggerRequest.php index ef65b5f6ae..033f5feae9 100644 --- a/app/Api/V1/Requests/RuleGroupTriggerRequest.php +++ b/app/Api/V1/Requests/Models/RuleGroup/TriggerRequest.php @@ -22,7 +22,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\RuleGroup; use Carbon\Carbon; @@ -31,9 +31,9 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; /** - * Class RuleGroupTriggerRequest + * Class TriggerRequest */ -class RuleGroupTriggerRequest extends FormRequest +class TriggerRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin; @@ -62,11 +62,11 @@ class RuleGroupTriggerRequest extends FormRequest } /** - * @return string + * @return array */ - private function getAccounts(): string + private function getAccounts(): array { - return (string) $this->query('accounts'); + return $this->get('accounts'); } /** diff --git a/app/Api/V1/Requests/RuleGroupUpdateRequest.php b/app/Api/V1/Requests/Models/RuleGroup/UpdateRequest.php similarity index 75% rename from app/Api/V1/Requests/RuleGroupUpdateRequest.php rename to app/Api/V1/Requests/Models/RuleGroup/UpdateRequest.php index 99de728a19..c41e927dd1 100644 --- a/app/Api/V1/Requests/RuleGroupUpdateRequest.php +++ b/app/Api/V1/Requests/Models/RuleGroup/UpdateRequest.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\RuleGroup; use FireflyIII\Rules\IsBoolean; use FireflyIII\Support\Request\ChecksLogin; @@ -31,9 +31,9 @@ use Illuminate\Foundation\Http\FormRequest; /** * @codeCoverageIgnore - * Class RuleGroupUpdateRequest + * Class UpdateRequest */ -class RuleGroupUpdateRequest extends FormRequest +class UpdateRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin; @@ -44,17 +44,15 @@ class RuleGroupUpdateRequest extends FormRequest */ public function getAll(): array { - $active = true; - - if (null !== $this->get('active')) { - $active = $this->boolean('active'); - } - - return [ - 'title' => $this->string('title'), - 'description' => $this->string('description'), - 'active' => $active, + // This is the way. + $fields = [ + 'title' => ['title', 'string'], + 'description' => ['description', 'nlString'], + 'active' => ['active', 'boolean'], + 'order' => ['order', 'integer'], ]; + + return $this->getAllData($fields); } /** @@ -65,8 +63,9 @@ class RuleGroupUpdateRequest extends FormRequest public function rules(): array { $ruleGroup = $this->route()->parameter('ruleGroup'); + return [ - 'title' => 'required|between:1,100|uniqueObjectForUser:rule_groups,title,' . $ruleGroup->id, + 'title' => 'between:1,100|uniqueObjectForUser:rule_groups,title,' . $ruleGroup->id, 'description' => 'between:1,5000|nullable', 'active' => [new IsBoolean], ]; diff --git a/app/Api/V1/Requests/TagStoreRequest.php b/app/Api/V1/Requests/Models/Tag/StoreRequest.php similarity index 94% rename from app/Api/V1/Requests/TagStoreRequest.php rename to app/Api/V1/Requests/Models/Tag/StoreRequest.php index 02fa2fd7b3..e23101ae47 100644 --- a/app/Api/V1/Requests/TagStoreRequest.php +++ b/app/Api/V1/Requests/Models/Tag/StoreRequest.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\Tag; use FireflyIII\Models\Location; use FireflyIII\Support\Request\AppendsLocationData; @@ -30,15 +30,14 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; /** - * Class TagStoreRequest + * Class StoreRequest * * @codeCoverageIgnore */ -class TagStoreRequest extends FormRequest +class StoreRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin, AppendsLocationData; - /** * Get all data from the request. * diff --git a/app/Api/V1/Requests/TagUpdateRequest.php b/app/Api/V1/Requests/Models/Tag/UpdateRequest.php similarity index 75% rename from app/Api/V1/Requests/TagUpdateRequest.php rename to app/Api/V1/Requests/Models/Tag/UpdateRequest.php index d247837d18..86c17ee38e 100644 --- a/app/Api/V1/Requests/TagUpdateRequest.php +++ b/app/Api/V1/Requests/Models/Tag/UpdateRequest.php @@ -22,7 +22,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\Tag; use FireflyIII\Models\Location; use FireflyIII\Support\Request\AppendsLocationData; @@ -31,11 +31,11 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; /** - * Class TagUpdateRequest + * Class UpdateRequest * * @codeCoverageIgnore */ -class TagUpdateRequest extends FormRequest +class UpdateRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin, AppendsLocationData; @@ -47,13 +47,14 @@ class TagUpdateRequest extends FormRequest */ public function getAll(): array { - $data = [ - 'tag' => $this->string('tag'), - 'date' => $this->date('date'), - 'description' => $this->string('description'), - 'has_location' => true, // pretend location is present. + // This is the way. + $fields = [ + 'tag' => ['tag', 'string'], + 'date' => ['date', 'date'], + 'description' => ['description', 'string'], ]; - $data = $this->appendLocationData($data, null); + $data = $this->getAllData($fields); + $data = $this->appendLocationData($data, null); return $data; } @@ -66,9 +67,9 @@ class TagUpdateRequest extends FormRequest public function rules(): array { $tag = $this->route()->parameter('tagOrId'); - + // TODO is uniqueObjectForUser not obsolete? $rules = [ - 'tag' => 'required|min:1|uniqueObjectForUser:tags,tag,' . $tag->id, + 'tag' => 'min:1|uniqueObjectForUser:tags,tag,' . $tag->id, 'description' => 'min:1|nullable', 'date' => 'date|nullable', ]; diff --git a/app/Api/V1/Requests/TransactionStoreRequest.php b/app/Api/V1/Requests/Models/Transaction/StoreRequest.php similarity index 98% rename from app/Api/V1/Requests/TransactionStoreRequest.php rename to app/Api/V1/Requests/Models/Transaction/StoreRequest.php index dc029afe3f..c6ab9280e2 100644 --- a/app/Api/V1/Requests/TransactionStoreRequest.php +++ b/app/Api/V1/Requests/Models/Transaction/StoreRequest.php @@ -22,12 +22,13 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\Transaction; use FireflyIII\Rules\BelongsUser; use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsDateOrTime; use FireflyIII\Support\NullArrayObject; +use FireflyIII\Support\Request\AppendsLocationData; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Validation\CurrencyValidation; @@ -38,11 +39,11 @@ use Illuminate\Validation\Validator; use Log; /** - * Class TransactionStoreRequest + * Class StoreRequest */ -class TransactionStoreRequest extends FormRequest +class StoreRequest extends FormRequest { - use TransactionValidation, GroupValidation, CurrencyValidation, ConvertsDataTypes, ChecksLogin; + use TransactionValidation, GroupValidation, CurrencyValidation, ConvertsDataTypes, ChecksLogin, AppendsLocationData; /** * Get all data. Is pretty complex because of all the ??-statements. @@ -52,13 +53,14 @@ class TransactionStoreRequest extends FormRequest public function getAll(): array { Log::debug('get all data in TransactionStoreRequest'); - - return [ + $data = [ 'group_title' => $this->string('group_title'), 'error_if_duplicate_hash' => $this->boolean('error_if_duplicate_hash'), 'apply_rules' => $this->boolean('apply_rules', true), 'transactions' => $this->getTransactionData(), ]; + // TODO location + return $data; } /** diff --git a/app/Api/V1/Requests/TransactionUpdateRequest.php b/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php similarity index 90% rename from app/Api/V1/Requests/TransactionUpdateRequest.php rename to app/Api/V1/Requests/Models/Transaction/UpdateRequest.php index 449bd452ab..ac82f4a817 100644 --- a/app/Api/V1/Requests/TransactionUpdateRequest.php +++ b/app/Api/V1/Requests/Models/Transaction/UpdateRequest.php @@ -22,7 +22,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\Transaction; use FireflyIII\Models\TransactionGroup; use FireflyIII\Rules\BelongsUser; @@ -37,9 +37,9 @@ use Illuminate\Validation\Validator; use Log; /** - * Class TransactionUpdateRequest + * Class UpdateRequest */ -class TransactionUpdateRequest extends FormRequest +class UpdateRequest extends FormRequest { use TransactionValidation, GroupValidation, ConvertsDataTypes, ChecksLogin; @@ -50,9 +50,6 @@ class TransactionUpdateRequest extends FormRequest private array $stringFields; private array $textareaFields; - - - /** * Get all data. Is pretty complex because of all the ??-statements. * @@ -125,12 +122,13 @@ class TransactionUpdateRequest extends FormRequest $this->arrayFields = [ 'tags', ]; - - - $data = [ - 'transactions' => $this->getTransactionData(), - 'apply_rules' => $this->boolean('apply_rules', true), - ]; + $data = []; + if ($this->has('transactions')) { + $data['transactions'] = $this->getTransactionData(); + } + if ($this->has('apply_rules')) { + $data['apply_rules'] = $this->boolean('apply_rules', true); + } if ($this->has('group_title')) { $data['group_title'] = $this->string('group_title'); } @@ -147,19 +145,24 @@ class TransactionUpdateRequest extends FormRequest { Log::debug('Now in getTransactionData()'); $return = []; + + if (!is_countable($this->get('transactions'))) { + return $return; + } + /** * @var int $index * @var array $transaction */ foreach ($this->get('transactions') as $transaction) { // default response is to update nothing in the transaction: - $current = []; - $current = $this->getIntegerData($current, $transaction); - $current = $this->getStringData($current, $transaction); - $current = $this->getNlStringData($current, $transaction); - $current = $this->getDateData($current, $transaction); - $current = $this->getBooleanData($current, $transaction); - $current = $this->getArrayData($current, $transaction); + $current = []; + $current = $this->getIntegerData($current, $transaction); + $current = $this->getStringData($current, $transaction); + $current = $this->getNlStringData($current, $transaction); + $current = $this->getDateData($current, $transaction); + $current = $this->getBooleanData($current, $transaction); + $current = $this->getArrayData($current, $transaction); $return[] = $current; } @@ -177,7 +180,7 @@ class TransactionUpdateRequest extends FormRequest { foreach ($this->integerFields as $fieldName) { if (array_key_exists($fieldName, $transaction)) { - $current[$fieldName] = $this->integerFromValue((string) $transaction[$fieldName]); + $current[$fieldName] = $this->integerFromValue((string)$transaction[$fieldName]); } } @@ -194,7 +197,7 @@ class TransactionUpdateRequest extends FormRequest { foreach ($this->stringFields as $fieldName) { if (array_key_exists($fieldName, $transaction)) { - $current[$fieldName] = $this->stringFromValue((string) $transaction[$fieldName]); + $current[$fieldName] = $this->stringFromValue((string)$transaction[$fieldName]); } } @@ -211,7 +214,7 @@ class TransactionUpdateRequest extends FormRequest { foreach ($this->textareaFields as $fieldName) { if (array_key_exists($fieldName, $transaction)) { - $current[$fieldName] = $this->nlStringFromValue((string) $transaction[$fieldName]); + $current[$fieldName] = $this->nlStringFromValue((string)$transaction[$fieldName]); } } @@ -229,8 +232,8 @@ class TransactionUpdateRequest extends FormRequest foreach ($this->dateFields as $fieldName) { Log::debug(sprintf('Now at date field %s', $fieldName)); if (array_key_exists($fieldName, $transaction)) { - Log::debug(sprintf('New value: "%s"', (string) $transaction[$fieldName])); - $current[$fieldName] = $this->dateFromValue((string) $transaction[$fieldName]); + Log::debug(sprintf('New value: "%s"', (string)$transaction[$fieldName])); + $current[$fieldName] = $this->dateFromValue((string)$transaction[$fieldName]); } } @@ -247,7 +250,7 @@ class TransactionUpdateRequest extends FormRequest { foreach ($this->booleanFields as $fieldName) { if (array_key_exists($fieldName, $transaction)) { - $current[$fieldName] = $this->convertBoolean((string) $transaction[$fieldName]); + $current[$fieldName] = $this->convertBoolean((string)$transaction[$fieldName]); } } @@ -362,21 +365,18 @@ class TransactionUpdateRequest extends FormRequest $transactionGroup = $this->route()->parameter('transactionGroup'); $validator->after( function (Validator $validator) use ($transactionGroup) { - // must submit at least one transaction. - $this->validateOneTransaction($validator); - // if more than one, verify that there are journal ID's present. $this->validateJournalIds($validator, $transactionGroup); // all transaction types must be equal: - $this->validateTransactionTypesForUpdate($validator); + $this->validateTransactionTypesForUpdate($validator, $transactionGroup); // validate source/destination is equal, depending on the transaction journal type. $this->validateEqualAccountsForUpdate($validator, $transactionGroup); // validate that the currency fits the source and/or destination account. // validate all account info - $this->validateAccountInformationUpdate($validator); + $this->validateAccountInformationUpdate($validator, $transactionGroup); } ); diff --git a/app/Api/V1/Requests/CurrencyStoreRequest.php b/app/Api/V1/Requests/Models/TransactionCurrency/StoreRequest.php similarity index 95% rename from app/Api/V1/Requests/CurrencyStoreRequest.php rename to app/Api/V1/Requests/Models/TransactionCurrency/StoreRequest.php index 0d912cff05..2146b685c8 100644 --- a/app/Api/V1/Requests/CurrencyStoreRequest.php +++ b/app/Api/V1/Requests/Models/TransactionCurrency/StoreRequest.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\TransactionCurrency; use FireflyIII\Rules\IsBoolean; use FireflyIII\Support\Request\ChecksLogin; @@ -30,11 +30,11 @@ use Illuminate\Foundation\Http\FormRequest; /** - * Class CurrencyStoreRequest + * Class StoreRequest * * @codeCoverageIgnore */ -class CurrencyStoreRequest extends FormRequest +class StoreRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin; diff --git a/app/Api/V1/Requests/CurrencyUpdateRequest.php b/app/Api/V1/Requests/Models/TransactionCurrency/UpdateRequest.php similarity index 58% rename from app/Api/V1/Requests/CurrencyUpdateRequest.php rename to app/Api/V1/Requests/Models/TransactionCurrency/UpdateRequest.php index 57c8a4a321..359b1533cd 100644 --- a/app/Api/V1/Requests/CurrencyUpdateRequest.php +++ b/app/Api/V1/Requests/Models/TransactionCurrency/UpdateRequest.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\TransactionCurrency; use FireflyIII\Rules\IsBoolean; use FireflyIII\Support\Request\ChecksLogin; @@ -30,11 +30,11 @@ use Illuminate\Foundation\Http\FormRequest; /** - * Class CurrencyUpdateRequest + * Class UpdateRequest * * @codeCoverageIgnore */ -class CurrencyUpdateRequest extends FormRequest +class UpdateRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin; @@ -45,23 +45,20 @@ class CurrencyUpdateRequest extends FormRequest */ public function getAll(): array { - $enabled = true; - $default = false; - if (null !== $this->get('enabled')) { - $enabled = $this->boolean('enabled'); - } - if (null !== $this->get('default')) { - $default = $this->boolean('default'); - } - - return [ - 'name' => $this->string('name'), - 'code' => $this->string('code'), - 'symbol' => $this->string('symbol'), - 'decimal_places' => $this->integer('decimal_places'), - 'default' => $default, - 'enabled' => $enabled, + // return nothing that isn't explicitely in the array: + $fields = [ + 'name' => ['name', 'string'], + 'code' => ['code', 'string'], + 'symbol' => ['symbol', 'string'], + 'decimal_places' => ['decimal_places', 'integer'], + 'default' => ['default', 'boolean'], + 'enabled' => ['enabled', 'boolean'], ]; + + $return = $this->getAllData($fields); + + return $return; + } /** @@ -71,11 +68,12 @@ class CurrencyUpdateRequest extends FormRequest */ public function rules(): array { - $currency = $this->route()->parameter('currency_code'); + $currency = $this->route()->parameter('currency_code'); + return [ - 'name' => sprintf('required|between:1,255|unique:transaction_currencies,name,%d', $currency->id), - 'code' => sprintf('required|between:3,3|unique:transaction_currencies,code,%d', $currency->id), - 'symbol' => sprintf('required|between:1,8|unique:transaction_currencies,symbol,%d', $currency->id), + 'name' => sprintf('between:1,255|unique:transaction_currencies,name,%d', $currency->id), + 'code' => sprintf('between:3,3|unique:transaction_currencies,code,%d', $currency->id), + 'symbol' => sprintf('between:1,8|unique:transaction_currencies,symbol,%d', $currency->id), 'decimal_places' => 'between:0,20|numeric|min:0|max:20', 'enabled' => [new IsBoolean()], 'default' => [new IsBoolean()], diff --git a/app/Api/V1/Requests/TransactionLinkRequest.php b/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php similarity index 97% rename from app/Api/V1/Requests/TransactionLinkRequest.php rename to app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php index 00c94d7306..1b8540728f 100644 --- a/app/Api/V1/Requests/TransactionLinkRequest.php +++ b/app/Api/V1/Requests/Models/TransactionLink/StoreRequest.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\TransactionLink; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; @@ -32,14 +32,12 @@ use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Validator; /** - * Class TransactionLinkRequest + * Class StoreRequest */ -class TransactionLinkRequest extends FormRequest +class StoreRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin; - - /** * Get all data from the request. * diff --git a/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php b/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php new file mode 100644 index 0000000000..143c284056 --- /dev/null +++ b/app/Api/V1/Requests/Models/TransactionLink/UpdateRequest.php @@ -0,0 +1,135 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Requests\Models\TransactionLink; + +use FireflyIII\Models\TransactionJournalLink; +use FireflyIII\Repositories\Journal\JournalRepositoryInterface; +use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; +use FireflyIII\Support\Request\ChecksLogin; +use FireflyIII\Support\Request\ConvertsDataTypes; +use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Validator; + +/** + * Class UpdateRequest + */ +class UpdateRequest extends FormRequest +{ + use ConvertsDataTypes, ChecksLogin; + + /** + * Get all data from the request. + * + * @return array + */ + public function getAll(): array + { + return [ + 'link_type_id' => $this->integer('link_type_id'), + 'link_type_name' => $this->string('link_type_name'), + 'inward_id' => $this->integer('inward_id'), + 'outward_id' => $this->integer('outward_id'), + 'notes' => $this->nullableNlString('notes'), + ]; + } + + /** + * The rules that the incoming request must be matched against. + * + * @return array + */ + public function rules(): array + { + return [ + 'link_type_id' => 'exists:link_types,id', + 'link_type_name' => 'exists:link_types,name', + 'inward_id' => 'belongsToUser:transaction_journals,id|different:outward_id', + 'outward_id' => 'belongsToUser:transaction_journals,id|different:inward_id', + 'notes' => 'between:0,65000', + ]; + } + + /** + * Configure the validator instance. + * + * @param Validator $validator + * + * @return void + */ + public function withValidator(Validator $validator): void + { + $validator->after( + function (Validator $validator) { + $this->validateUpdate($validator); + } + ); + } + + /** + * @param Validator $validator + */ + private function validateUpdate(Validator $validator): void + { + /** @var TransactionJournalLink $existing */ + $existing = $this->route()->parameter('journalLink'); + $data = $validator->getData(); + /** @var LinkTypeRepositoryInterface $repository */ + $repository = app(LinkTypeRepositoryInterface::class); + $repository->setUser(auth()->user()); + + /** @var JournalRepositoryInterface $journalRepos */ + $journalRepos = app(JournalRepositoryInterface::class); + $journalRepos->setUser(auth()->user()); + + $inwardId = $data['inward_id'] ?? $existing->source_id; + $outwardId = $data['outward_id'] ?? $existing->destination_id; + $inward = $journalRepos->findNull((int)$inwardId); + $outward = $journalRepos->findNull((int)$outwardId); + if (null === $inward) { + $inward = $existing->source; + } + if (null === $outward) { + $outward = $existing->destination; + } + if ($inward->id === $outward->id) { + $validator->errors()->add('inward_id', 'Inward ID must be different from outward ID.'); + $validator->errors()->add('outward_id', 'Inward ID must be different from outward ID.'); + } + + if (null === $inward) { + $validator->errors()->add('inward_id', 'This is not a valid inward journal.'); + } + if (null === $outward) { + $validator->errors()->add('inward_id', 'This is not a valid outward journal.'); + } + $inDB = $repository->findSpecificLink($existing->linkType, $inward, $outward); + if (null === $inDB) { + return; + } + if ($inDB->id !== $existing->id) { + $validator->errors()->add('outward_id', 'Already have a link between inward and outward.'); + $validator->errors()->add('inward_id', 'Already have a link between inward and outward.'); + } + } +} diff --git a/app/Api/V1/Requests/LinkTypeStoreRequest.php b/app/Api/V1/Requests/Models/TransactionLinkType/StoreRequest.php similarity index 93% rename from app/Api/V1/Requests/LinkTypeStoreRequest.php rename to app/Api/V1/Requests/Models/TransactionLinkType/StoreRequest.php index 260b3d8afe..3794b87a83 100644 --- a/app/Api/V1/Requests/LinkTypeStoreRequest.php +++ b/app/Api/V1/Requests/Models/TransactionLinkType/StoreRequest.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\TransactionLinkType; use FireflyIII\Models\LinkType; use FireflyIII\Support\Request\ChecksLogin; @@ -30,11 +30,11 @@ use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Rule; /** - * Class LinkTypeStoreRequest + * Class StoreRequest * * @codeCoverageIgnore */ -class LinkTypeStoreRequest extends FormRequest +class StoreRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin; diff --git a/app/Api/V1/Requests/LinkTypeUpdateRequest.php b/app/Api/V1/Requests/Models/TransactionLinkType/UpdateRequest.php similarity index 77% rename from app/Api/V1/Requests/LinkTypeUpdateRequest.php rename to app/Api/V1/Requests/Models/TransactionLinkType/UpdateRequest.php index 229fc32e84..fb50a34a7d 100644 --- a/app/Api/V1/Requests/LinkTypeUpdateRequest.php +++ b/app/Api/V1/Requests/Models/TransactionLinkType/UpdateRequest.php @@ -21,7 +21,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\Models\TransactionLinkType; use FireflyIII\Models\LinkType; use FireflyIII\Support\Request\ChecksLogin; @@ -30,11 +30,11 @@ use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Rule; /** - * Class LinkTypeUpdateRequest + * Class UpdateRequest * * @codeCoverageIgnore */ -class LinkTypeUpdateRequest extends FormRequest +class UpdateRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin; @@ -63,9 +63,9 @@ class LinkTypeUpdateRequest extends FormRequest { $linkType = $this->route()->parameter('linkType'); return [ - 'name' => ['required', Rule::unique('link_types', 'name')->ignore($linkType->id), 'min:1'], - 'outward' => ['required', 'different:inward', Rule::unique('link_types', 'outward')->ignore($linkType->id), 'min:1'], - 'inward' => ['required', 'different:outward', Rule::unique('link_types', 'inward')->ignore($linkType->id), 'min:1'], + 'name' => [Rule::unique('link_types', 'name')->ignore($linkType->id), 'min:1'], + 'outward' => ['different:inward', Rule::unique('link_types', 'outward')->ignore($linkType->id), 'min:1'], + 'inward' => ['different:outward', Rule::unique('link_types', 'inward')->ignore($linkType->id), 'min:1'], ]; } } diff --git a/app/Api/V1/Requests/Webhook/CreateRequest.php b/app/Api/V1/Requests/Models/Webhook/CreateRequest.php similarity index 76% rename from app/Api/V1/Requests/Webhook/CreateRequest.php rename to app/Api/V1/Requests/Models/Webhook/CreateRequest.php index 8ec557bd24..f96c524813 100644 --- a/app/Api/V1/Requests/Webhook/CreateRequest.php +++ b/app/Api/V1/Requests/Models/Webhook/CreateRequest.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * CreateRequest.php @@ -20,7 +41,7 @@ declare(strict_types=1); * along with this program. If not, see . */ -namespace FireflyIII\Api\V1\Requests\Webhook; +namespace FireflyIII\Api\V1\Requests\Models\Webhook; use FireflyIII\Rules\IsBoolean; use FireflyIII\Support\Request\ChecksLogin; diff --git a/app/Api/V1/Requests/Webhook/UpdateRequest.php b/app/Api/V1/Requests/Models/Webhook/UpdateRequest.php similarity index 58% rename from app/Api/V1/Requests/Webhook/UpdateRequest.php rename to app/Api/V1/Requests/Models/Webhook/UpdateRequest.php index 8abd735841..6b5f779043 100644 --- a/app/Api/V1/Requests/Webhook/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Webhook/UpdateRequest.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * UpdateRequest.php @@ -20,7 +41,7 @@ declare(strict_types=1); * along with this program. If not, see . */ -namespace FireflyIII\Api\V1\Requests\Webhook; +namespace FireflyIII\Api\V1\Requests\Models\Webhook; use FireflyIII\Rules\IsBoolean; use FireflyIII\Support\Request\ChecksLogin; @@ -53,11 +74,20 @@ class UpdateRequest extends FormRequest ]; // this is the way. - $return = $this->getAllData($fields); - $return['trigger'] = $triggers[$return['trigger']] ?? 0; - $return['response'] = $responses[$return['response']] ?? 0; - $return['delivery'] = $deliveries[$return['delivery']] ?? 0; - $return['secret'] = null !== $this->get('secret'); + $return = $this->getAllData($fields); + if (array_key_exists('trigger', $return)) { + $return['trigger'] = $triggers[$return['trigger']] ?? 0; + } + if (array_key_exists('response', $return)) { + $return['response'] = $responses[$return['response']] ?? 0; + } + if (array_key_exists('delivery', $return)) { + $return['delivery'] = $deliveries[$return['delivery']] ?? 0; + } + $return['secret'] = null !== $this->get('secret'); + if (null !== $this->get('title')) { + $return['title'] = $this->string('title'); + } return $return; } @@ -77,10 +107,10 @@ class UpdateRequest extends FormRequest return [ 'title' => sprintf('between:1,512|uniqueObjectForUser:webhooks,title,%d', $webhook->id), 'active' => [new IsBoolean], - 'trigger' => sprintf('required|in:%s', $triggers), - 'response' => sprintf('required|in:%s', $responses), - 'delivery' => sprintf('required|in:%s', $deliveries), - 'url' => ['required', 'url', 'starts_with:https://', sprintf('uniqueExistingWebhook:%d', $webhook->id)], + 'trigger' => sprintf('in:%s', $triggers), + 'response' => sprintf('in:%s', $responses), + 'delivery' => sprintf('in:%s', $deliveries), + 'url' => ['url', 'starts_with:https://', sprintf('uniqueExistingWebhook:%d', $webhook->id)], ]; } } diff --git a/app/Api/V1/Requests/PiggyBankStoreRequest.php b/app/Api/V1/Requests/PiggyBankStoreRequest.php deleted file mode 100644 index 5099df3a5e..0000000000 --- a/app/Api/V1/Requests/PiggyBankStoreRequest.php +++ /dev/null @@ -1,78 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Api\V1\Requests; - -use FireflyIII\Support\Request\ChecksLogin; -use FireflyIII\Support\Request\ConvertsDataTypes; -use Illuminate\Foundation\Http\FormRequest; - -/** - * Class PiggyBankStoreRequest - * - * @codeCoverageIgnore - */ -class PiggyBankStoreRequest extends FormRequest -{ - use ConvertsDataTypes, ChecksLogin; - - /** - * Get all data from the request. - * - * @return array - */ - public function getAll(): array - { - return [ - 'name' => $this->string('name'), - 'account_id' => $this->integer('account_id'), - 'targetamount' => $this->string('target_amount'), - 'current_amount' => $this->string('current_amount'), - 'startdate' => $this->date('start_date'), - 'targetdate' => $this->date('target_date'), - 'notes' => $this->nlString('notes'), - 'object_group_id' => $this->integer('object_group_id'), - 'object_group' => $this->string('object_group_name'), - ]; - } - - /** - * The rules that the incoming request must be matched against. - * - * @return array - */ - public function rules(): array - { - return [ - 'name' => 'required|between:1,255|uniquePiggyBankForUser', - 'current_amount' => ['numeric', 'gte:0', 'lte:target_amount'], - 'account_id' => 'required|numeric|belongsToUser:accounts,id', - 'object_group_id' => 'numeric|belongsToUser:object_groups,id', - 'target_amount' => ['numeric', 'gte:0', 'lte:target_amount', 'required'], - 'start_date' => 'date|nullable', - 'target_date' => 'date|nullable|after:start_date', - 'notes' => 'max:65000', - ]; - } - -} diff --git a/app/Api/V1/Requests/ConfigurationRequest.php b/app/Api/V1/Requests/System/UpdateRequest.php similarity index 75% rename from app/Api/V1/Requests/ConfigurationRequest.php rename to app/Api/V1/Requests/System/UpdateRequest.php index 8de01f8985..cf6ddf5c6d 100644 --- a/app/Api/V1/Requests/ConfigurationRequest.php +++ b/app/Api/V1/Requests/System/UpdateRequest.php @@ -22,7 +22,7 @@ declare(strict_types=1); -namespace FireflyIII\Api\V1\Requests; +namespace FireflyIII\Api\V1\Requests\System; use FireflyIII\Rules\IsBoolean; use FireflyIII\Support\Request\ChecksLogin; @@ -30,11 +30,11 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; /** - * Class ConfigurationRequest + * Class UpdateRequest * * @codeCoverageIgnore */ -class ConfigurationRequest extends FormRequest +class UpdateRequest extends FormRequest { use ConvertsDataTypes, ChecksLogin; @@ -47,14 +47,15 @@ class ConfigurationRequest extends FormRequest */ public function getAll(): array { - $name = $this->route()->parameter('configName'); + $name = $this->route()->parameter('dynamicConfigKey'); switch ($name) { default: break; - case 'is_demo_site': - case 'single_user_mode': + case 'configuration.is_demo_site': + case 'configuration.single_user_mode': return ['value' => $this->boolean('value')]; - case 'permission_update_check': + case 'configuration.permission_update_check': + case 'configuration.last_update_check': return ['value' => $this->integer('value')]; } @@ -72,11 +73,13 @@ class ConfigurationRequest extends FormRequest switch ($name) { default: break; - case 'is_demo_site': - case 'single_user_mode': + case 'configuration.is_demo_site': + case 'configuration.single_user_mode': return ['value' => ['required', new IsBoolean]]; - case 'permission_update_check': + case 'configuration.permission_update_check': return ['value' => 'required|numeric|between:-1,1']; + case 'configuration.last_update_check': + return ['value' => 'required|numeric|min:464272080']; } return ['value' => 'required']; // @codeCoverageIgnore diff --git a/app/Api/V1/Requests/UserStoreRequest.php b/app/Api/V1/Requests/System/UserStoreRequest.php similarity index 95% rename from app/Api/V1/Requests/UserStoreRequest.php rename to app/Api/V1/Requests/System/UserStoreRequest.php index 0938a37d15..dc7c4603e2 100644 --- a/app/Api/V1/Requests/UserStoreRequest.php +++ b/app/Api/V1/Requests/System/UserStoreRequest.php @@ -1,8 +1,8 @@ . + */ + +namespace FireflyIII\Api\V1\Requests\User; + + +use FireflyIII\Support\Request\ChecksLogin; +use FireflyIII\Support\Request\ConvertsDataTypes; +use Illuminate\Foundation\Http\FormRequest; + +class PreferenceStoreRequest extends FormRequest +{ + use ChecksLogin, ConvertsDataTypes; + + /** + * @return array + */ + public function getAll(): array + { + $array = [ + 'name' => $this->string('name'), + 'data' => $this->get('data'), + ]; + if ('true' === $array['data']) { + $array['data'] = true; + } + if ('false' === $array['data']) { + $array['data'] = false; + } + if(is_numeric($array['data'])) { + $array['data'] = (float)$array['data']; + } + + return $array; + } + + /** + * @return string[] + */ + public function rules(): array + { + return [ + 'name' => 'required', + 'data' => 'required', + ]; + } + +} \ No newline at end of file diff --git a/app/Api/V1/Requests/PreferenceRequest.php b/app/Api/V1/Requests/User/PreferenceUpdateRequest.php similarity index 64% rename from app/Api/V1/Requests/PreferenceRequest.php rename to app/Api/V1/Requests/User/PreferenceUpdateRequest.php index 9676385a61..2dcf0f1f86 100644 --- a/app/Api/V1/Requests/PreferenceRequest.php +++ b/app/Api/V1/Requests/User/PreferenceUpdateRequest.php @@ -1,7 +1,7 @@ . */ -declare(strict_types=1); +namespace FireflyIII\Api\V1\Requests\User; -namespace FireflyIII\Api\V1\Requests; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; -/** - * Class PreferenceRequest - * - * @codeCoverageIgnore - */ -class PreferenceRequest extends FormRequest +class PreferenceUpdateRequest extends FormRequest { - use ConvertsDataTypes, ChecksLogin; - - + use ChecksLogin, ConvertsDataTypes; /** - * Get all data from the request. - * * @return array */ public function getAll(): array { - return [ + $array = [ + 'name' => $this->string('name'), 'data' => $this->get('data'), ]; + if ('true' === $array['data']) { + $array['data'] = true; + } + if ('false' === $array['data']) { + $array['data'] = false; + } + if(is_numeric($array['data'])) { + $array['data'] = (float)$array['data']; + } + + return $array; } /** - * The rules that the incoming request must be matched against. - * - * @return array + * @return string[] */ public function rules(): array { return [ - 'data' => 'required|between:1,65000', + 'data' => 'required', ]; } -} +} \ No newline at end of file diff --git a/app/Console/Commands/Correction/CorrectOpeningBalanceCurrencies.php b/app/Console/Commands/Correction/CorrectOpeningBalanceCurrencies.php index 29cdbdf516..4e44b49307 100644 --- a/app/Console/Commands/Correction/CorrectOpeningBalanceCurrencies.php +++ b/app/Console/Commands/Correction/CorrectOpeningBalanceCurrencies.php @@ -89,6 +89,7 @@ class CorrectOpeningBalanceCurrencies extends Command } Log::debug(sprintf('Done with %s', __METHOD__)); + return 0; } @@ -133,6 +134,7 @@ class CorrectOpeningBalanceCurrencies extends Command $account = $transaction->account()->first(); if (null !== $account && AccountType::INITIAL_BALANCE !== $account->accountType()->first()->type) { Log::debug(sprintf('Account of transaction #%d is opposite of IB account (%s).', $transaction->id, $account->accountType()->first()->type)); + return $account; } } @@ -143,6 +145,7 @@ class CorrectOpeningBalanceCurrencies extends Command /** * @param Account $account + * * @return TransactionCurrency * @throws JsonException */ @@ -156,15 +159,16 @@ class CorrectOpeningBalanceCurrencies extends Command } /** - * @param TransactionJournal $journal + * @param TransactionJournal $journal * @param TransactionCurrency $currency + * * @return int */ private function setCurrency(TransactionJournal $journal, TransactionCurrency $currency): int { Log::debug('Now in setCurrency'); $count = 0; - if ((int) $journal->transaction_currency_id !== (int) $currency->id) { + if ((int)$journal->transaction_currency_id !== (int)$currency->id) { Log::debug(sprintf('Currency ID of journal #%d was #%d, now set to #%d', $journal->id, $journal->transaction_currency_id, $currency->id)); $journal->transaction_currency_id = $currency->id; $journal->save(); @@ -173,8 +177,10 @@ class CorrectOpeningBalanceCurrencies extends Command /** @var Transaction $transaction */ foreach ($journal->transactions as $transaction) { - if ((int) $transaction->transaction_currency_id !== (int) $currency->id) { - Log::debug(sprintf('Currency ID of transaction #%d was #%d, now set to #%d', $transaction->id, $transaction->transaction_currency_id, $currency->id)); + if ((int)$transaction->transaction_currency_id !== (int)$currency->id) { + Log::debug( + sprintf('Currency ID of transaction #%d was #%d, now set to #%d', $transaction->id, $transaction->transaction_currency_id, $currency->id) + ); $transaction->transaction_currency_id = $currency->id; $transaction->save(); $count = 1; diff --git a/app/Console/Commands/Correction/CreateAccessTokens.php b/app/Console/Commands/Correction/CreateAccessTokens.php index 7314645cf8..f98c76274e 100644 --- a/app/Console/Commands/Correction/CreateAccessTokens.php +++ b/app/Console/Commands/Correction/CreateAccessTokens.php @@ -50,8 +50,8 @@ class CreateAccessTokens extends Command /** * Execute the console command. * - * @throws Exception * @return int + * @throws Exception */ public function handle(): int { diff --git a/app/Console/Commands/Correction/DeleteEmptyGroups.php b/app/Console/Commands/Correction/DeleteEmptyGroups.php index f8c4d49c9f..7daec17572 100644 --- a/app/Console/Commands/Correction/DeleteEmptyGroups.php +++ b/app/Console/Commands/Correction/DeleteEmptyGroups.php @@ -50,9 +50,9 @@ class DeleteEmptyGroups extends Command /** * Execute the console command. * + * @return int * @throws Exception; * - * @return int */ public function handle(): int { diff --git a/app/Console/Commands/Correction/DeleteEmptyJournals.php b/app/Console/Commands/Correction/DeleteEmptyJournals.php index 5cdef1cc89..a61250f692 100644 --- a/app/Console/Commands/Correction/DeleteEmptyJournals.php +++ b/app/Console/Commands/Correction/DeleteEmptyJournals.php @@ -62,6 +62,38 @@ class DeleteEmptyJournals extends Command return 0; } + /** + * Delete transactions and their journals if they have an uneven number of transactions. + */ + private function deleteUnevenJournals(): void + { + $set = Transaction + ::whereNull('deleted_at') + ->groupBy('transactions.transaction_journal_id') + ->get([DB::raw('COUNT(transactions.transaction_journal_id) as the_count'), 'transaction_journal_id']); + $total = 0; + foreach ($set as $row) { + $count = (int)$row->the_count; + if (1 === $count % 2) { + // uneven number, delete journal and transactions: + try { + TransactionJournal::find((int)$row->transaction_journal_id)->delete(); + // @codeCoverageIgnoreStart + } catch (Exception $e) { + Log::info(sprintf('Could not delete journal: %s', $e->getMessage())); + } + // @codeCoverageIgnoreEnd + + Transaction::where('transaction_journal_id', (int)$row->transaction_journal_id)->delete(); + $this->info(sprintf('Deleted transaction journal #%d because it had an uneven number of transactions.', $row->transaction_journal_id)); + $total++; + } + } + if (0 === $total) { + $this->info('No uneven transaction journals.'); + } + } + private function deleteEmptyJournals(): void { $start = microtime(true); @@ -90,36 +122,4 @@ class DeleteEmptyJournals extends Command $this->info(sprintf('Verified empty journals in %s seconds', $end)); } - /** - * Delete transactions and their journals if they have an uneven number of transactions. - */ - private function deleteUnevenJournals(): void - { - $set = Transaction - ::whereNull('deleted_at') - ->groupBy('transactions.transaction_journal_id') - ->get([DB::raw('COUNT(transactions.transaction_journal_id) as the_count'), 'transaction_journal_id']); - $total = 0; - foreach ($set as $row) { - $count = (int) $row->the_count; - if (1 === $count % 2) { - // uneven number, delete journal and transactions: - try { - TransactionJournal::find((int) $row->transaction_journal_id)->delete(); - // @codeCoverageIgnoreStart - } catch (Exception $e) { - Log::info(sprintf('Could not delete journal: %s', $e->getMessage())); - } - // @codeCoverageIgnoreEnd - - Transaction::where('transaction_journal_id', (int) $row->transaction_journal_id)->delete(); - $this->info(sprintf('Deleted transaction journal #%d because it had an uneven number of transactions.', $row->transaction_journal_id)); - $total++; - } - } - if (0 === $total) { - $this->info('No uneven transaction journals.'); - } - } - } diff --git a/app/Console/Commands/Correction/DeleteOrphanedTransactions.php b/app/Console/Commands/Correction/DeleteOrphanedTransactions.php index 8752694e12..d0414029c1 100644 --- a/app/Console/Commands/Correction/DeleteOrphanedTransactions.php +++ b/app/Console/Commands/Correction/DeleteOrphanedTransactions.php @@ -52,8 +52,8 @@ class DeleteOrphanedTransactions extends Command /** * Execute the console command. * - * @throws Exception * @return int + * @throws Exception */ public function handle(): int { @@ -66,45 +66,6 @@ class DeleteOrphanedTransactions extends Command return 0; } - /** - * - */ - private function deleteFromOrphanedAccounts(): void - { - $set - = Transaction - ::leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id') - ->whereNotNull('accounts.deleted_at') - ->get(['transactions.*']); - $count = 0; - /** @var Transaction $transaction */ - foreach ($set as $transaction) { - // delete journals - $journal = TransactionJournal::find((int) $transaction->transaction_journal_id); - if ($journal) { - try { - $journal->delete(); - // @codeCoverageIgnoreStart - } catch (Exception $e) { - Log::info(sprintf('Could not delete journal %s', $e->getMessage())); - } - // @codeCoverageIgnoreEnd - } - Transaction::where('transaction_journal_id', (int) $transaction->transaction_journal_id)->delete(); - $this->line( - sprintf( - 'Deleted transaction journal #%d because account #%d was already deleted.', - $transaction->transaction_journal_id, - $transaction->account_id - ) - ); - $count++; - } - if (0 === $count) { - $this->info('No orphaned accounts.'); - } - } - /** * @throws Exception */ @@ -124,7 +85,7 @@ class DeleteOrphanedTransactions extends Command ); /** @var stdClass $entry */ foreach ($set as $entry) { - $transaction = Transaction::find((int) $entry->transaction_id); + $transaction = Transaction::find((int)$entry->transaction_id); $transaction->delete(); $this->info( sprintf( @@ -139,4 +100,43 @@ class DeleteOrphanedTransactions extends Command $this->info('No orphaned transactions.'); } } + + /** + * + */ + private function deleteFromOrphanedAccounts(): void + { + $set + = Transaction + ::leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id') + ->whereNotNull('accounts.deleted_at') + ->get(['transactions.*']); + $count = 0; + /** @var Transaction $transaction */ + foreach ($set as $transaction) { + // delete journals + $journal = TransactionJournal::find((int)$transaction->transaction_journal_id); + if ($journal) { + try { + $journal->delete(); + // @codeCoverageIgnoreStart + } catch (Exception $e) { + Log::info(sprintf('Could not delete journal %s', $e->getMessage())); + } + // @codeCoverageIgnoreEnd + } + Transaction::where('transaction_journal_id', (int)$transaction->transaction_journal_id)->delete(); + $this->line( + sprintf( + 'Deleted transaction journal #%d because account #%d was already deleted.', + $transaction->transaction_journal_id, + $transaction->account_id + ) + ); + $count++; + } + if (0 === $count) { + $this->info('No orphaned accounts.'); + } + } } diff --git a/app/Console/Commands/Correction/EnableCurrencies.php b/app/Console/Commands/Correction/EnableCurrencies.php index 9ac763d49d..3901b7036b 100644 --- a/app/Console/Commands/Correction/EnableCurrencies.php +++ b/app/Console/Commands/Correction/EnableCurrencies.php @@ -64,35 +64,39 @@ class EnableCurrencies extends Command /** @var Collection $meta */ $meta = AccountMeta::where('name', 'currency_id')->groupBy('data')->get(['data']); foreach ($meta as $entry) { - $found[] = (int) $entry->data; + $found[] = (int)$entry->data; } // get all from journals: /** @var Collection $journals */ $journals = TransactionJournal::groupBy('transaction_currency_id')->get(['transaction_currency_id']); foreach ($journals as $entry) { - $found[] = (int) $entry->transaction_currency_id; + $found[] = (int)$entry->transaction_currency_id; } // get all from transactions /** @var Collection $transactions */ - $transactions = Transaction::groupBy('transaction_currency_id', 'foreign_currency_id')->get(['transaction_currency_id','foreign_currency_id']); + $transactions = Transaction::groupBy('transaction_currency_id', 'foreign_currency_id')->get(['transaction_currency_id', 'foreign_currency_id']); foreach ($transactions as $entry) { - $found[] = (int) $entry->transaction_currency_id; - $found[] = (int) $entry->foreign_currency_id; + $found[] = (int)$entry->transaction_currency_id; + $found[] = (int)$entry->foreign_currency_id; } // get all from budget limits /** @var Collection $limits */ $limits = BudgetLimit::groupBy('transaction_currency_id')->get(['transaction_currency_id']); foreach ($limits as $entry) { - $found[] = (int) $entry->transaction_currency_id; + $found[] = (int)$entry->transaction_currency_id; } $found = array_values(array_unique($found)); - $found = array_values(array_filter($found, function (int $currencyId) { - return $currencyId !== 0; - })); + $found = array_values( + array_filter( + $found, function (int $currencyId) { + return $currencyId !== 0; + } + ) + ); $message = sprintf('%d different currencies are currently in use.', count($found)); $this->info($message); Log::debug($message, $found); diff --git a/app/Console/Commands/Correction/FixAccountOrder.php b/app/Console/Commands/Correction/FixAccountOrder.php index 3c9ebb53ee..1cf44a765a 100644 --- a/app/Console/Commands/Correction/FixAccountOrder.php +++ b/app/Console/Commands/Correction/FixAccountOrder.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Correction; -use FireflyIII\Models\AccountType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\User; use Illuminate\Console\Command; @@ -62,16 +61,7 @@ class FixAccountOrder extends Command $users = User::get(); foreach ($users as $user) { $this->repository->setUser($user); - $sets = [ - [AccountType::DEFAULT, AccountType::ASSET], - [AccountType::EXPENSE, AccountType::BENEFICIARY], - [AccountType::REVENUE], - [AccountType::LOAN, AccountType::DEBT, AccountType::CREDITCARD, AccountType::MORTGAGE], - [AccountType::CASH, AccountType::INITIAL_BALANCE, AccountType::IMPORT, AccountType::RECONCILIATION], - ]; - foreach ($sets as $set) { - $this->repository->resetAccountOrder($set); - } + $this->repository->resetAccountOrder(); } $end = round(microtime(true) - $start, 2); diff --git a/app/Console/Commands/Correction/FixAccountTypes.php b/app/Console/Commands/Correction/FixAccountTypes.php index f4638c9f4e..80d5ab9bcb 100644 --- a/app/Console/Commands/Correction/FixAccountTypes.php +++ b/app/Console/Commands/Correction/FixAccountTypes.php @@ -39,11 +39,13 @@ class FixAccountTypes extends Command { /** * The console command description. + * * @var string */ protected $description = 'Make sure all journals have the correct from/to account types.'; /** * The name and signature of the console command. + * * @var string */ protected $signature = 'firefly-iii:fix-account-types'; @@ -54,6 +56,7 @@ class FixAccountTypes extends Command /** * Execute the console command. + * * @return int * @throws FireflyException */ @@ -84,11 +87,87 @@ class FixAccountTypes extends Command return 0; } + /** + * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is + * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should + * be called from the handle method instead of using the constructor to initialize the command. + * + * @codeCoverageIgnore + */ + private function stupidLaravel(): void + { + $this->count = 0; + } + + /** + * @param TransactionJournal $journal + * + * @throws FireflyException + */ + private function inspectJournal(TransactionJournal $journal): void + { + $transactions = $journal->transactions()->count(); + if (2 !== $transactions) { + Log::debug(sprintf('Journal has %d transactions, so can\'t fix.', $transactions)); + $this->info(sprintf('Cannot inspect transaction journal #%d because it has %d transaction(s) instead of 2.', $journal->id, $transactions)); + + return; + } + $type = $journal->transactionType->type; + $sourceTransaction = $this->getSourceTransaction($journal); + $destTransaction = $this->getDestinationTransaction($journal); + $sourceAccount = $sourceTransaction->account; + $sourceAccountType = $sourceAccount->accountType->type; + $destAccount = $destTransaction->account; + $destAccountType = $destAccount->accountType->type; + + if (!array_key_exists($type, $this->expected)) { + // @codeCoverageIgnoreStart + Log::info(sprintf('No source/destination info for transaction type %s.', $type)); + $this->info(sprintf('No source/destination info for transaction type %s.', $type)); + + return; + // @codeCoverageIgnoreEnd + } + if (!array_key_exists($sourceAccountType, $this->expected[$type])) { + Log::debug(sprintf('Going to fix journal #%d', $journal->id)); + $this->fixJournal($journal, $type, $sourceTransaction, $destTransaction); + + return; + } + $expectedTypes = $this->expected[$type][$sourceAccountType]; + if (!in_array($destAccountType, $expectedTypes, true)) { + Log::debug(sprintf('Going to fix journal #%d', $journal->id)); + $this->fixJournal($journal, $type, $sourceTransaction, $destTransaction); + } + } + + /** + * @param TransactionJournal $journal + * + * @return Transaction + */ + private function getSourceTransaction(TransactionJournal $journal): Transaction + { + return $journal->transactions->firstWhere('amount', '<', 0); + } + + /** + * @param TransactionJournal $journal + * + * @return Transaction + */ + private function getDestinationTransaction(TransactionJournal $journal): Transaction + { + return $journal->transactions->firstWhere('amount', '>', 0); + } + /** * @param TransactionJournal $journal * @param string $type * @param Transaction $source * @param Transaction $dest + * * @throws FireflyException */ private function fixJournal(TransactionJournal $journal, string $type, Transaction $source, Transaction $dest): void @@ -132,7 +211,10 @@ class FixAccountTypes extends Command $result = $this->factory->findOrCreate($dest->account->name, AccountType::EXPENSE); $dest->account()->associate($result); $dest->save(); - $message = sprintf('Transaction journal #%d, destination account changed from #%d ("%s") to #%d ("%s").', $journal->id, $oldDest->id, $oldDest->name, $result->id, $result->name); + $message = sprintf( + 'Transaction journal #%d, destination account changed from #%d ("%s") to #%d ("%s").', $journal->id, $oldDest->id, $oldDest->name, + $result->id, $result->name + ); $this->info($message); Log::debug($message); $this->inspectJournal($journal); @@ -145,7 +227,10 @@ class FixAccountTypes extends Command $oldSource = $dest->account; $source->account()->associate($result); $source->save(); - $message = sprintf('Transaction journal #%d, source account changed from #%d ("%s") to #%d ("%s").', $journal->id, $oldSource->id, $oldSource->name, $result->id, $result->name); + $message = sprintf( + 'Transaction journal #%d, source account changed from #%d ("%s") to #%d ("%s").', $journal->id, $oldSource->id, $oldSource->name, + $result->id, $result->name + ); $this->info($message); Log::debug($message); $this->inspectJournal($journal); @@ -163,75 +248,4 @@ class FixAccountTypes extends Command } } - - /** - * @param TransactionJournal $journal - * @return Transaction - */ - private function getDestinationTransaction(TransactionJournal $journal): Transaction - { - return $journal->transactions->firstWhere('amount', '>', 0); - } - - /** - * @param TransactionJournal $journal - * @return Transaction - */ - private function getSourceTransaction(TransactionJournal $journal): Transaction - { - return $journal->transactions->firstWhere('amount', '<', 0); - } - - /** - * @param TransactionJournal $journal - * @throws FireflyException - */ - private function inspectJournal(TransactionJournal $journal): void - { - $transactions = $journal->transactions()->count(); - if (2 !== $transactions) { - Log::debug(sprintf('Journal has %d transactions, so can\'t fix.', $transactions)); - $this->info(sprintf('Cannot inspect transaction journal #%d because it has %d transaction(s) instead of 2.', $journal->id, $transactions)); - - return; - } - $type = $journal->transactionType->type; - $sourceTransaction = $this->getSourceTransaction($journal); - $destTransaction = $this->getDestinationTransaction($journal); - $sourceAccount = $sourceTransaction->account; - $sourceAccountType = $sourceAccount->accountType->type; - $destAccount = $destTransaction->account; - $destAccountType = $destAccount->accountType->type; - - if (!array_key_exists($type, $this->expected)) { - // @codeCoverageIgnoreStart - Log::info(sprintf('No source/destination info for transaction type %s.', $type)); - $this->info(sprintf('No source/destination info for transaction type %s.', $type)); - - return; - // @codeCoverageIgnoreEnd - } - if (!array_key_exists($sourceAccountType, $this->expected[$type])) { - Log::debug(sprintf('Going to fix journal #%d', $journal->id)); - $this->fixJournal($journal, $type, $sourceTransaction, $destTransaction); - - return; - } - $expectedTypes = $this->expected[$type][$sourceAccountType]; - if (!in_array($destAccountType, $expectedTypes, true)) { - Log::debug(sprintf('Going to fix journal #%d', $journal->id)); - $this->fixJournal($journal, $type, $sourceTransaction, $destTransaction); - } - } - - /** - * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is - * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should - * be called from the handle method instead of using the constructor to initialize the command. - * @codeCoverageIgnore - */ - private function stupidLaravel(): void - { - $this->count = 0; - } } diff --git a/app/Console/Commands/Correction/FixGroupAccounts.php b/app/Console/Commands/Correction/FixGroupAccounts.php index 43239e09e6..37bb4986dc 100644 --- a/app/Console/Commands/Correction/FixGroupAccounts.php +++ b/app/Console/Commands/Correction/FixGroupAccounts.php @@ -62,12 +62,12 @@ class FixGroupAccounts extends Command ::groupBy('transaction_group_id') ->get(['transaction_group_id', DB::raw('COUNT(transaction_group_id) as the_count')]); foreach ($res as $journal) { - if ((int) $journal->the_count > 1) { - $groups[] = (int) $journal->transaction_group_id; + if ((int)$journal->the_count > 1) { + $groups[] = (int)$journal->transaction_group_id; } } $handler = new UpdatedGroupEventHandler; - foreach($groups as $groupId) { + foreach ($groups as $groupId) { $group = TransactionGroup::find($groupId); $event = new UpdatedTransactionGroup($group); $handler->unifyAccounts($event); diff --git a/app/Console/Commands/Correction/FixLongDescriptions.php b/app/Console/Commands/Correction/FixLongDescriptions.php index bcc5288ff1..12ade346d2 100644 --- a/app/Console/Commands/Correction/FixLongDescriptions.php +++ b/app/Console/Commands/Correction/FixLongDescriptions.php @@ -69,7 +69,7 @@ class FixLongDescriptions extends Command $groups = TransactionGroup::get(['id', 'title']); /** @var TransactionGroup $group */ foreach ($groups as $group) { - if (strlen((string) $group->title) > self::MAX_LENGTH) { + if (strlen((string)$group->title) > self::MAX_LENGTH) { $group->title = substr($group->title, 0, self::MAX_LENGTH); $group->save(); $this->line(sprintf('Truncated description of transaction group #%d', $group->id)); diff --git a/app/Console/Commands/Correction/FixRecurringTransactions.php b/app/Console/Commands/Correction/FixRecurringTransactions.php index ce51e3a10e..c34bb68a31 100644 --- a/app/Console/Commands/Correction/FixRecurringTransactions.php +++ b/app/Console/Commands/Correction/FixRecurringTransactions.php @@ -73,18 +73,6 @@ class FixRecurringTransactions extends Command return 0; } - /** - * - */ - private function correctTransactions(): void - { - $users = $this->userRepos->all(); - /** @var User $user */ - foreach ($users as $user) { - $this->processUser($user); - } - } - /** * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should @@ -98,6 +86,18 @@ class FixRecurringTransactions extends Command $this->userRepos = app(UserRepositoryInterface::class); } + /** + * + */ + private function correctTransactions(): void + { + $users = $this->userRepos->all(); + /** @var User $user */ + foreach ($users as $user) { + $this->processUser($user); + } + } + /** * @param User $user */ diff --git a/app/Console/Commands/Correction/FixTransactionTypes.php b/app/Console/Commands/Correction/FixTransactionTypes.php index de083aa82a..3c51059e1f 100644 --- a/app/Console/Commands/Correction/FixTransactionTypes.php +++ b/app/Console/Commands/Correction/FixTransactionTypes.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); namespace FireflyIII\Console\Commands\Correction; @@ -58,19 +79,6 @@ class FixTransactionTypes extends Command return 0; } - /** - * @param TransactionJournal $journal - * @param string $expectedType - */ - private function changeJournal(TransactionJournal $journal, string $expectedType): void - { - $type = TransactionType::whereType($expectedType)->first(); - if (null !== $type) { - $journal->transaction_type_id = $type->id; - $journal->save(); - } - } - /** * Collect all transaction journals. * @@ -99,7 +107,7 @@ class FixTransactionTypes extends Command return false; } - $expectedType = (string) config(sprintf('firefly.account_to_transaction.%s.%s', $source->accountType->type, $destination->accountType->type)); + $expectedType = (string)config(sprintf('firefly.account_to_transaction.%s.%s', $source->accountType->type, $destination->accountType->type)); if ($expectedType !== $type) { $this->line(sprintf('Transaction journal #%d was of type "%s" but is corrected to "%s"', $journal->id, $type, $expectedType)); $this->changeJournal($journal, $expectedType); @@ -113,8 +121,37 @@ class FixTransactionTypes extends Command /** * @param TransactionJournal $journal * - * @throws FireflyException * @return Account + * @throws FireflyException + */ + private function getSourceAccount(TransactionJournal $journal): Account + { + $collection = $journal->transactions->filter( + static function (Transaction $transaction) { + return $transaction->amount < 0; + } + ); + if (0 === $collection->count()) { + throw new FireflyException(sprintf('Journal #%d has no source transaction.', $journal->id)); + } + if (1 !== $collection->count()) { + throw new FireflyException(sprintf('Journal #%d has multiple source transactions.', $journal->id)); + } + /** @var Transaction $transaction */ + $transaction = $collection->first(); + $account = $transaction->account; + if (null === $account) { + throw new FireflyException(sprintf('Journal #%d, transaction #%d has no source account.', $journal->id, $transaction->id)); + } + + return $account; + } + + /** + * @param TransactionJournal $journal + * + * @return Account + * @throws FireflyException */ private function getDestinationAccount(TransactionJournal $journal): Account { @@ -141,30 +178,14 @@ class FixTransactionTypes extends Command /** * @param TransactionJournal $journal - * - * @throws FireflyException - * @return Account + * @param string $expectedType */ - private function getSourceAccount(TransactionJournal $journal): Account + private function changeJournal(TransactionJournal $journal, string $expectedType): void { - $collection = $journal->transactions->filter( - static function (Transaction $transaction) { - return $transaction->amount < 0; - } - ); - if (0 === $collection->count()) { - throw new FireflyException(sprintf('Journal #%d has no source transaction.', $journal->id)); + $type = TransactionType::whereType($expectedType)->first(); + if (null !== $type) { + $journal->transaction_type_id = $type->id; + $journal->save(); } - if (1 !== $collection->count()) { - throw new FireflyException(sprintf('Journal #%d has multiple source transactions.', $journal->id)); - } - /** @var Transaction $transaction */ - $transaction = $collection->first(); - $account = $transaction->account; - if (null === $account) { - throw new FireflyException(sprintf('Journal #%d, transaction #%d has no source account.', $journal->id, $transaction->id)); - } - - return $account; } } diff --git a/app/Console/Commands/Correction/RemoveBills.php b/app/Console/Commands/Correction/RemoveBills.php index fb85b507af..53f7a2d126 100644 --- a/app/Console/Commands/Correction/RemoveBills.php +++ b/app/Console/Commands/Correction/RemoveBills.php @@ -56,10 +56,10 @@ class RemoveBills extends Command $start = microtime(true); /** @var TransactionType $withdrawal */ $withdrawal = TransactionType::where('type', TransactionType::WITHDRAWAL)->first(); - if(null === $withdrawal) { + if (null === $withdrawal) { return 0; } - $journals = TransactionJournal::whereNotNull('bill_id')->where('transaction_type_id', '!=', $withdrawal->id)->get(); + $journals = TransactionJournal::whereNotNull('bill_id')->where('transaction_type_id', '!=', $withdrawal->id)->get(); /** @var TransactionJournal $journal */ foreach ($journals as $journal) { $this->line(sprintf('Transaction journal #%d should not be linked to bill #%d.', $journal->id, $journal->bill_id)); diff --git a/app/Console/Commands/CreateFirstUser.php b/app/Console/Commands/CreateFirstUser.php index 8110dad030..be7bcb1155 100644 --- a/app/Console/Commands/CreateFirstUser.php +++ b/app/Console/Commands/CreateFirstUser.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); namespace FireflyIII\Console\Commands; diff --git a/app/Console/Commands/Export/ExportData.php b/app/Console/Commands/Export/ExportData.php index 55b05d9824..e06f2ff9b8 100644 --- a/app/Console/Commands/Export/ExportData.php +++ b/app/Console/Commands/Export/ExportData.php @@ -111,8 +111,12 @@ class ExportData extends Command /** @var ExportDataGenerator $exporter */ $exporter = app(ExportDataGenerator::class); $exporter->setUser($this->user); + $exporter->setStart($options['start']); $exporter->setEnd($options['end']); + $exporter->setAccounts($options['accounts']); + + $exporter->setExportTransactions($options['export']['transactions']); $exporter->setExportAccounts($options['export']['accounts']); $exporter->setExportBudgets($options['export']['budgets']); @@ -143,108 +147,16 @@ class ExportData extends Command } /** - * @param array $options - * @param array $data + * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is + * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should + * be called from the handle method instead of using the constructor to initialize the command. * - * @throws FireflyException + * @codeCoverageIgnore */ - private function exportData(array $options, array $data): void + private function stupidLaravel(): void { - $date = date('Y_m_d'); - foreach ($data as $key => $content) { - $file = sprintf('%s%s_%s.csv', $options['directory'], $date, $key); - if (false === $options['force'] && file_exists($file)) { - throw new FireflyException(sprintf('File "%s" exists already. Use --force to overwrite.', $file)); - } - if (true === $options['force'] && file_exists($file)) { - $this->warn(sprintf('File "%s" exists already but will be replaced.', $file)); - } - // continue to write to file. - file_put_contents($file, $content); - $this->info(sprintf('Wrote %s-export to file "%s".', $key, $file)); - } - } - - /** - * @return Collection - * @throws FireflyException - */ - private function getAccountsParameter(): Collection - { - $final = new Collection; - $accounts = new Collection; - $accountList = $this->option('accounts'); - $types = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]; - if (null !== $accountList && '' !== (string)$accountList) { - $accountIds = explode(',', $accountList); - $accounts = $this->accountRepository->getAccountsById($accountIds); - } - if (null === $accountList) { - $accounts = $this->accountRepository->getAccountsByType($types); - } - // filter accounts, - /** @var AccountType $account */ - foreach ($accounts as $account) { - if (in_array($account->accountType->type, $types, true)) { - $final->push($account); - } - } - if (0 === $final->count()) { - throw new FireflyException('Ended up with zero valid accounts to export from.'); - } - - return $final; - } - - /** - * @param string $field - * - * @return Carbon - * @throws Exception - */ - private function getDateParameter(string $field): Carbon - { - $date = Carbon::now()->subYear(); - $error = false; - if (null !== $this->option($field)) { - try { - $date = Carbon::createFromFormat('Y-m-d', $this->option($field)); - } catch (InvalidArgumentException $e) { - Log::error($e->getMessage()); - $this->error(sprintf('%s date "%s" must be formatted YYYY-MM-DD. Field will be ignored.', $field, $this->option('start'))); - $error = true; - } - } - if (false === $error && 'start' === $field) { - $journal = $this->journalRepository->firstNull(); - $date = null === $journal ? Carbon::now()->subYear() : $journal->date; - $date->startOfDay(); - } - if (false === $error && 'end' === $field) { - $date = today(config('app.timezone')); - $date->endOfDay(); - } - - // fallback - return $date; - } - - /** - * @return string - * @throws FireflyException - * - */ - private function getExportDirectory(): string - { - $directory = (string)$this->option('export_directory'); - if (null === $directory) { - $directory = './'; - } - if (!is_writable($directory)) { - throw new FireflyException(sprintf('Directory "%s" isn\'t writeable.', $directory)); - } - - return $directory; + $this->journalRepository = app(JournalRepositoryInterface::class); + $this->accountRepository = app(AccountRepositoryInterface::class); } /** @@ -279,16 +191,108 @@ class ExportData extends Command } /** - * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is - * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should - * be called from the handle method instead of using the constructor to initialize the command. + * @param string $field * - * @codeCoverageIgnore + * @return Carbon + * @throws Exception */ - private function stupidLaravel(): void + private function getDateParameter(string $field): Carbon { - $this->journalRepository = app(JournalRepositoryInterface::class); - $this->accountRepository = app(AccountRepositoryInterface::class); + $date = Carbon::now()->subYear(); + $error = false; + if (null !== $this->option($field)) { + try { + $date = Carbon::createFromFormat('Y-m-d', $this->option($field)); + } catch (InvalidArgumentException $e) { + Log::error($e->getMessage()); + $this->error(sprintf('%s date "%s" must be formatted YYYY-MM-DD. Field will be ignored.', $field, $this->option('start'))); + $error = true; + } + } + if (false === $error && 'start' === $field) { + $journal = $this->journalRepository->firstNull(); + $date = null === $journal ? Carbon::now()->subYear() : $journal->date; + $date->startOfDay(); + } + if (false === $error && 'end' === $field) { + $date = today(config('app.timezone')); + $date->endOfDay(); + } + + // fallback + return $date; + } + + /** + * @return Collection + * @throws FireflyException + */ + private function getAccountsParameter(): Collection + { + $final = new Collection; + $accounts = new Collection; + $accountList = $this->option('accounts'); + $types = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]; + if (null !== $accountList && '' !== (string)$accountList) { + $accountIds = explode(',', $accountList); + $accounts = $this->accountRepository->getAccountsById($accountIds); + } + if (null === $accountList) { + $accounts = $this->accountRepository->getAccountsByType($types); + } + // filter accounts, + /** @var AccountType $account */ + foreach ($accounts as $account) { + if (in_array($account->accountType->type, $types, true)) { + $final->push($account); + } + } + if (0 === $final->count()) { + throw new FireflyException('Ended up with zero valid accounts to export from.'); + } + + return $final; + } + + /** + * @return string + * @throws FireflyException + * + */ + private function getExportDirectory(): string + { + $directory = (string)$this->option('export_directory'); + if (null === $directory) { + $directory = './'; + } + if (!is_writable($directory)) { + throw new FireflyException(sprintf('Directory "%s" isn\'t writeable.', $directory)); + } + + return $directory; + } + + /** + * @param array $options + * @param array $data + * + * @throws FireflyException + */ + private function exportData(array $options, array $data): void + { + $date = date('Y_m_d'); + foreach ($data as $key => $content) { + $file = sprintf('%s%s_%s.csv', $options['directory'], $date, $key); + if (false === $options['force'] && file_exists($file)) { + throw new FireflyException(sprintf('File "%s" exists already. Use --force to overwrite.', $file)); + } + if (true === $options['force'] && file_exists($file)) { + $this->warn(sprintf('File "%s" exists already but will be replaced.', $file)); + } + // continue to write to file. + file_put_contents($file, $content); + $this->info(sprintf('Wrote %s-export to file "%s".', $key, $file)); + } } diff --git a/app/Console/Commands/Integrity/ReportEmptyObjects.php b/app/Console/Commands/Integrity/ReportEmptyObjects.php index 0e11400d30..fe8f33d061 100644 --- a/app/Console/Commands/Integrity/ReportEmptyObjects.php +++ b/app/Console/Commands/Integrity/ReportEmptyObjects.php @@ -68,51 +68,6 @@ class ReportEmptyObjects extends Command return 0; } - /** - * Reports on accounts with no transactions. - */ - private function reportAccounts(): void - { - $set = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id') - ->leftJoin('users', 'accounts.user_id', '=', 'users.id') - ->groupBy(['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email']) - ->whereNull('transactions.account_id') - ->get( - ['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email'] - ); - - /** @var stdClass $entry */ - foreach ($set as $entry) { - $line = 'User #%d (%s) has account #%d ("%s") which has no transactions.'; - $line = sprintf($line, $entry->user_id, $entry->email, $entry->id, $entry->name); - $this->line($line); - } - } - - /** - * Reports on budgets with no budget limits (which makes them pointless). - */ - private function reportBudgetLimits(): void - { - $set = Budget::leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id') - ->leftJoin('users', 'budgets.user_id', '=', 'users.id') - ->groupBy(['budgets.id', 'budgets.name', 'budgets.encrypted', 'budgets.user_id', 'users.email']) - ->whereNull('budget_limits.id') - ->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'budgets.encrypted', 'users.email']); - - /** @var Budget $entry */ - foreach ($set as $entry) { - $line = sprintf( - 'User #%d (%s) has budget #%d ("%s") which has no budget limits.', - $entry->user_id, - $entry->email, - $entry->id, - $entry->name - ); - $this->line($line); - } - } - /** * Report on budgets with no transactions or journals. */ @@ -188,4 +143,49 @@ class ReportEmptyObjects extends Command $this->line($line); } } + + /** + * Reports on accounts with no transactions. + */ + private function reportAccounts(): void + { + $set = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id') + ->leftJoin('users', 'accounts.user_id', '=', 'users.id') + ->groupBy(['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email']) + ->whereNull('transactions.account_id') + ->get( + ['accounts.id', 'accounts.encrypted', 'accounts.name', 'accounts.user_id', 'users.email'] + ); + + /** @var stdClass $entry */ + foreach ($set as $entry) { + $line = 'User #%d (%s) has account #%d ("%s") which has no transactions.'; + $line = sprintf($line, $entry->user_id, $entry->email, $entry->id, $entry->name); + $this->line($line); + } + } + + /** + * Reports on budgets with no budget limits (which makes them pointless). + */ + private function reportBudgetLimits(): void + { + $set = Budget::leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id') + ->leftJoin('users', 'budgets.user_id', '=', 'users.id') + ->groupBy(['budgets.id', 'budgets.name', 'budgets.encrypted', 'budgets.user_id', 'users.email']) + ->whereNull('budget_limits.id') + ->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'budgets.encrypted', 'users.email']); + + /** @var Budget $entry */ + foreach ($set as $entry) { + $line = sprintf( + 'User #%d (%s) has budget #%d ("%s") which has no budget limits.', + $entry->user_id, + $entry->email, + $entry->id, + $entry->name + ); + $this->line($line); + } + } } diff --git a/app/Console/Commands/Integrity/ReportSum.php b/app/Console/Commands/Integrity/ReportSum.php index 8b6f7f6cff..f11366c8f4 100644 --- a/app/Console/Commands/Integrity/ReportSum.php +++ b/app/Console/Commands/Integrity/ReportSum.php @@ -70,7 +70,7 @@ class ReportSum extends Command /** @var User $user */ foreach ($userRepository->all() as $user) { - $sum = (string) $user->transactions()->sum('amount'); + $sum = (string)$user->transactions()->sum('amount'); if (0 !== bccomp($sum, '0')) { $message = sprintf('Error: Transactions for user #%d (%s) are off by %s!', $user->id, $user->email, $sum); $this->error($message); diff --git a/app/Console/Commands/Integrity/RestoreOAuthKeys.php b/app/Console/Commands/Integrity/RestoreOAuthKeys.php index 1d2913c108..9f8c66e76c 100644 --- a/app/Console/Commands/Integrity/RestoreOAuthKeys.php +++ b/app/Console/Commands/Integrity/RestoreOAuthKeys.php @@ -59,38 +59,6 @@ class RestoreOAuthKeys extends Command return 0; } - /** - * - */ - private function generateKeys(): void - { - OAuthKeys::generateKeys(); - } - - /** - * @return bool - */ - private function keysInDatabase(): bool - { - return OAuthKeys::keysInDatabase(); - } - - /** - * @return bool - */ - private function keysOnDrive(): bool - { - return OAuthKeys::hasKeyFiles(); - } - - /** - * - */ - private function restoreKeysFromDB(): void - { - OAuthKeys::restoreKeysFromDB(); - } - /** * */ @@ -122,6 +90,30 @@ class RestoreOAuthKeys extends Command $this->line('OAuth keys are OK'); } + /** + * @return bool + */ + private function keysInDatabase(): bool + { + return OAuthKeys::keysInDatabase(); + } + + /** + * @return bool + */ + private function keysOnDrive(): bool + { + return OAuthKeys::hasKeyFiles(); + } + + /** + * + */ + private function generateKeys(): void + { + OAuthKeys::generateKeys(); + } + /** * */ @@ -129,4 +121,12 @@ class RestoreOAuthKeys extends Command { OAuthKeys::storeKeysInDB(); } + + /** + * + */ + private function restoreKeysFromDB(): void + { + OAuthKeys::restoreKeysFromDB(); + } } diff --git a/app/Console/Commands/Tools/ApplyRules.php b/app/Console/Commands/Tools/ApplyRules.php index 553358e918..04295f3dc5 100644 --- a/app/Console/Commands/Tools/ApplyRules.php +++ b/app/Console/Commands/Tools/ApplyRules.php @@ -102,6 +102,7 @@ class ApplyRules extends Command $result = $this->verifyInput(); if (false === $result) { app('telemetry')->feature('system.command.errored', $this->signature); + return 1; } @@ -121,6 +122,7 @@ class ApplyRules extends Command $this->warn(' --all_rules'); app('telemetry')->feature('system.command.errored', $this->signature); + return 1; } @@ -132,7 +134,7 @@ class ApplyRules extends Command // add the accounts as filter: $filterAccountList = []; - foreach($this->accounts as $account) { + foreach ($this->accounts as $account) { $filterAccountList[] = $account->id; } $list = implode(',', $filterAccountList); @@ -157,49 +159,6 @@ class ApplyRules extends Command return 0; } - /** - * @return Collection - */ - private function getRulesToApply(): Collection - { - $rulesToApply = new Collection; - /** @var RuleGroup $group */ - foreach ($this->groups as $group) { - $rules = $this->ruleGroupRepository->getActiveStoreRules($group); - /** @var Rule $rule */ - foreach ($rules as $rule) { - // if in rule selection, or group in selection or all rules, it's included. - $test = $this->includeRule($rule, $group); - if (true === $test) { - Log::debug(sprintf('Will include rule #%d "%s"', $rule->id, $rule->title)); - $rulesToApply->push($rule); - } - } - } - - return $rulesToApply; - } - - /** - */ - private function grabAllRules(): void - { - $this->groups = $this->ruleGroupRepository->getActiveGroups(); - } - - /** - * @param Rule $rule - * @param RuleGroup $group - * - * @return bool - */ - private function includeRule(Rule $rule, RuleGroup $group): bool - { - return in_array($group->id, $this->ruleGroupSelection, true) - || in_array($rule->id, $this->ruleSelection, true) - || $this->allRules; - } - /** * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should @@ -271,7 +230,7 @@ class ApplyRules extends Command foreach ($accountList as $accountId) { - $accountId = (int) $accountId; + $accountId = (int)$accountId; $account = $accountRepository->findNull($accountId); if (null !== $account && in_array($account->accountType->type, $this->acceptedAccounts, true)) { $finalList->push($account); @@ -289,42 +248,6 @@ class ApplyRules extends Command } - /** - * @throws FireflyException - */ - private function verifyInputDates(): void - { - // parse start date. - $inputStart = Carbon::now()->startOfMonth(); - $startString = $this->option('start_date'); - if (null === $startString) { - /** @var JournalRepositoryInterface $repository */ - $repository = app(JournalRepositoryInterface::class); - $repository->setUser($this->getUser()); - $first = $repository->firstNull(); - if (null !== $first) { - $inputStart = $first->date; - } - } - if (null !== $startString && '' !== $startString) { - $inputStart = Carbon::createFromFormat('Y-m-d', $startString); - } - - // parse end date - $inputEnd = Carbon::now(); - $endString = $this->option('end_date'); - if (null !== $endString && '' !== $endString) { - $inputEnd = Carbon::createFromFormat('Y-m-d', $endString); - } - - if ($inputStart > $inputEnd) { - [$inputEnd, $inputStart] = [$inputStart, $inputEnd]; - } - - $this->startDate = $inputStart; - $this->endDate = $inputEnd; - } - /** * @return bool */ @@ -343,7 +266,7 @@ class ApplyRules extends Command } // @codeCoverageIgnoreEnd foreach ($ruleGroupList as $ruleGroupId) { - $ruleGroup = $this->ruleGroupRepository->find((int) $ruleGroupId); + $ruleGroup = $this->ruleGroupRepository->find((int)$ruleGroupId); if ($ruleGroup->active) { $this->ruleGroupSelection[] = $ruleGroup->id; } @@ -376,7 +299,7 @@ class ApplyRules extends Command // @codeCoverageIgnoreEnd foreach ($ruleList as $ruleId) { - $rule = $this->ruleRepository->find((int) $ruleId); + $rule = $this->ruleRepository->find((int)$ruleId); if (null !== $rule && $rule->active) { $this->ruleSelection[] = $rule->id; } @@ -384,4 +307,83 @@ class ApplyRules extends Command return true; } + + /** + * @throws FireflyException + */ + private function verifyInputDates(): void + { + // parse start date. + $inputStart = Carbon::now()->startOfMonth(); + $startString = $this->option('start_date'); + if (null === $startString) { + /** @var JournalRepositoryInterface $repository */ + $repository = app(JournalRepositoryInterface::class); + $repository->setUser($this->getUser()); + $first = $repository->firstNull(); + if (null !== $first) { + $inputStart = $first->date; + } + } + if (null !== $startString && '' !== $startString) { + $inputStart = Carbon::createFromFormat('Y-m-d', $startString); + } + + // parse end date + $inputEnd = Carbon::now(); + $endString = $this->option('end_date'); + if (null !== $endString && '' !== $endString) { + $inputEnd = Carbon::createFromFormat('Y-m-d', $endString); + } + + if ($inputStart > $inputEnd) { + [$inputEnd, $inputStart] = [$inputStart, $inputEnd]; + } + + $this->startDate = $inputStart; + $this->endDate = $inputEnd; + } + + /** + */ + private function grabAllRules(): void + { + $this->groups = $this->ruleGroupRepository->getActiveGroups(); + } + + /** + * @return Collection + */ + private function getRulesToApply(): Collection + { + $rulesToApply = new Collection; + /** @var RuleGroup $group */ + foreach ($this->groups as $group) { + $rules = $this->ruleGroupRepository->getActiveStoreRules($group); + /** @var Rule $rule */ + foreach ($rules as $rule) { + // if in rule selection, or group in selection or all rules, it's included. + $test = $this->includeRule($rule, $group); + if (true === $test) { + Log::debug(sprintf('Will include rule #%d "%s"', $rule->id, $rule->title)); + $rulesToApply->push($rule); + } + } + } + + return $rulesToApply; + } + + /** + * @param Rule $rule + * @param RuleGroup $group + * + * @return bool + */ + private function includeRule(Rule $rule, RuleGroup $group): bool + { + return in_array($group->id, $this->ruleGroupSelection, true) + || in_array($rule->id, $this->ruleSelection, true) + || $this->allRules; + } } diff --git a/app/Console/Commands/Tools/Cron.php b/app/Console/Commands/Tools/Cron.php index e5d2a89b5e..0012981ff5 100644 --- a/app/Console/Commands/Tools/Cron.php +++ b/app/Console/Commands/Tools/Cron.php @@ -66,11 +66,11 @@ class Cron extends Command $date = null; try { $date = new Carbon($this->option('date')); - } catch (InvalidArgumentException|Exception $e) { + } catch (InvalidArgumentException | Exception $e) { $this->error(sprintf('"%s" is not a valid date', $this->option('date'))); $e->getMessage(); } - $force = (bool) $this->option('force'); + $force = (bool)$this->option('force'); /* * Fire recurring transaction cron job. @@ -108,9 +108,36 @@ class Cron extends Command $this->info('More feedback on the cron jobs can be found in the log files.'); app('telemetry')->feature('system.command.executed', $this->signature); + return 0; } + /** + * @param bool $force + * @param Carbon|null $date + * + * @throws FireflyException + */ + private function recurringCronJob(bool $force, ?Carbon $date): void + { + $recurring = new RecurringCronjob; + $recurring->setForce($force); + + // set date in cron job: + if (null !== $date) { + $recurring->setDate($date); + } + + $result = $recurring->fire(); + + if (false === $result) { + $this->line('The recurring transaction cron job did not fire.'); + } + if (true === $result) { + $this->line('The recurring transaction cron job fired successfully.'); + } + } + /** * @param bool $force * @param Carbon|null $date @@ -138,32 +165,6 @@ class Cron extends Command } - /** - * @param bool $force - * @param Carbon|null $date - * - * @throws FireflyException - */ - private function recurringCronJob(bool $force, ?Carbon $date): void - { - $recurring = new RecurringCronjob; - $recurring->setForce($force); - - // set date in cron job: - if (null !== $date) { - $recurring->setDate($date); - } - - $result = $recurring->fire(); - - if (false === $result) { - $this->line('The recurring transaction cron job did not fire.'); - } - if (true === $result) { - $this->line('The recurring transaction cron job fired successfully.'); - } - } - /** * @param bool $force * @param Carbon|null $date diff --git a/app/Console/Commands/Upgrade/AccountCurrencies.php b/app/Console/Commands/Upgrade/AccountCurrencies.php index 27a14030a6..ae331fa8e4 100644 --- a/app/Console/Commands/Upgrade/AccountCurrencies.php +++ b/app/Console/Commands/Upgrade/AccountCurrencies.php @@ -92,27 +92,6 @@ class AccountCurrencies extends Command return 0; } - /** - * @return bool - */ - private function isExecuted(): bool - { - $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } - - return false; // @codeCoverageIgnore - } - - /** - * - */ - private function markAsExecuted(): void - { - app('fireflyconfig')->set(self::CONFIG_NAME, true); - } - /** * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should @@ -127,6 +106,66 @@ class AccountCurrencies extends Command $this->count = 0; } + /** + * @return bool + */ + private function isExecuted(): bool + { + $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + if (null !== $configVar) { + return (bool)$configVar->data; + } + + return false; // @codeCoverageIgnore + } + + /** + * + */ + private function updateAccountCurrencies(): void + { + Log::debug('Now in updateAccountCurrencies()'); + $users = $this->userRepos->all(); + $defaultCurrencyCode = (string)config('firefly.default_currency', 'EUR'); + Log::debug(sprintf('Default currency is %s', $defaultCurrencyCode)); + foreach ($users as $user) { + $this->updateCurrenciesForUser($user, $defaultCurrencyCode); + } + } + + /** + * @param User $user + * @param string $systemCurrencyCode + */ + private function updateCurrenciesForUser(User $user, string $systemCurrencyCode): void + { + Log::debug(sprintf('Now in updateCurrenciesForUser(%s, %s)', $user->email, $systemCurrencyCode)); + $this->accountRepos->setUser($user); + $accounts = $this->accountRepos->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); + + // get user's currency preference: + $defaultCurrencyCode = app('preferences')->getForUser($user, 'currencyPreference', $systemCurrencyCode)->data; + if (!is_string($defaultCurrencyCode)) { + $defaultCurrencyCode = $systemCurrencyCode; + } + Log::debug(sprintf('Users currency pref is %s', $defaultCurrencyCode)); + + /** @var TransactionCurrency $defaultCurrency */ + $defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first(); + + if (null === $defaultCurrency) { + Log::error(sprintf('Users currency pref "%s" does not exist!', $defaultCurrencyCode)); + $this->error(sprintf('User has a preference for "%s", but this currency does not exist.', $defaultCurrencyCode)); + + return; + } + + /** @var Account $account */ + foreach ($accounts as $account) { + $this->updateAccount($account, $defaultCurrency); + } + } + /** * @param Account $account * @param TransactionCurrency $currency @@ -136,13 +175,13 @@ class AccountCurrencies extends Command Log::debug(sprintf('Now in updateAccount(%d, %s)', $account->id, $currency->code)); $this->accountRepos->setUser($account->user); - $accountCurrency = (int) $this->accountRepos->getMetaValue($account, 'currency_id'); + $accountCurrency = (int)$this->accountRepos->getMetaValue($account, 'currency_id'); Log::debug(sprintf('Account currency is #%d', $accountCurrency)); $openingBalance = $this->accountRepos->getOpeningBalance($account); $obCurrency = 0; if (null !== $openingBalance) { - $obCurrency = (int) $openingBalance->transaction_currency_id; + $obCurrency = (int)$openingBalance->transaction_currency_id; Log::debug('Account has opening balance.'); } Log::debug(sprintf('Account OB currency is #%d.', $obCurrency)); @@ -192,47 +231,8 @@ class AccountCurrencies extends Command /** * */ - private function updateAccountCurrencies(): void + private function markAsExecuted(): void { - Log::debug('Now in updateAccountCurrencies()'); - $users = $this->userRepos->all(); - $defaultCurrencyCode = (string) config('firefly.default_currency', 'EUR'); - Log::debug(sprintf('Default currency is %s', $defaultCurrencyCode)); - foreach ($users as $user) { - $this->updateCurrenciesForUser($user, $defaultCurrencyCode); - } - } - - /** - * @param User $user - * @param string $systemCurrencyCode - */ - private function updateCurrenciesForUser(User $user, string $systemCurrencyCode): void - { - Log::debug(sprintf('Now in updateCurrenciesForUser(%s, %s)', $user->email, $systemCurrencyCode)); - $this->accountRepos->setUser($user); - $accounts = $this->accountRepos->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); - - // get user's currency preference: - $defaultCurrencyCode = app('preferences')->getForUser($user, 'currencyPreference', $systemCurrencyCode)->data; - if (!is_string($defaultCurrencyCode)) { - $defaultCurrencyCode = $systemCurrencyCode; - } - Log::debug(sprintf('Users currency pref is %s', $defaultCurrencyCode)); - - /** @var TransactionCurrency $defaultCurrency */ - $defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first(); - - if (null === $defaultCurrency) { - Log::error(sprintf('Users currency pref "%s" does not exist!', $defaultCurrencyCode)); - $this->error(sprintf('User has a preference for "%s", but this currency does not exist.', $defaultCurrencyCode)); - - return; - } - - /** @var Account $account */ - foreach ($accounts as $account) { - $this->updateAccount($account, $defaultCurrency); - } + app('fireflyconfig')->set(self::CONFIG_NAME, true); } } diff --git a/app/Console/Commands/Upgrade/AppendBudgetLimitPeriods.php b/app/Console/Commands/Upgrade/AppendBudgetLimitPeriods.php index 76375e5677..3af1a62fc5 100644 --- a/app/Console/Commands/Upgrade/AppendBudgetLimitPeriods.php +++ b/app/Console/Commands/Upgrade/AppendBudgetLimitPeriods.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * AppendBudgetLimitPeriods.php @@ -58,7 +79,7 @@ class AppendBudgetLimitPeriods extends Command $this->theresNoLimit(); - $this->markAsExecuted(); + $this->markAsExecuted(); $end = round(microtime(true) - $start, 2); $this->info(sprintf('Fixed budget limits in %s seconds.', $end)); @@ -79,6 +100,44 @@ class AppendBudgetLimitPeriods extends Command return false; // @codeCoverageIgnore } + /** + * + */ + private function theresNoLimit(): void + { + $limits = BudgetLimit::whereNull('period')->get(); + /** @var BudgetLimit $limit */ + foreach ($limits as $limit) { + $this->fixLimit($limit); + } + } + + /** + * @param BudgetLimit $limit + */ + private function fixLimit(BudgetLimit $limit) + { + $period = $this->getLimitPeriod($limit); + + if (null === $period) { + $message = sprintf( + 'Could not guesstimate budget limit #%d (%s - %s) period.', $limit->id, $limit->start_date->format('Y-m-d'), $limit->end_date->format('Y-m-d') + ); + $this->warn($message); + Log::warning($message); + + return; + } + $limit->period = $period; + $limit->save(); + + $msg = sprintf( + 'Budget limit #%d (%s - %s) period is "%s".', $limit->id, $limit->start_date->format('Y-m-d'), $limit->end_date->format('Y-m-d'), $period + ); + Log::debug($msg); + + } + /** * @param BudgetLimit $limit * @@ -139,37 +198,4 @@ class AppendBudgetLimitPeriods extends Command { app('fireflyconfig')->set(self::CONFIG_NAME, true); } - - /** - * - */ - private function theresNoLimit(): void - { - $limits = BudgetLimit::whereNull('period')->get(); - /** @var BudgetLimit $limit */ - foreach ($limits as $limit) { - $this->fixLimit($limit); - } - } - - /** - * @param BudgetLimit $limit - */ - private function fixLimit(BudgetLimit $limit) - { - $period = $this->getLimitPeriod($limit); - - if (null === $period) { - $message = sprintf('Could not guesstimate budget limit #%d (%s - %s) period.', $limit->id, $limit->start_date->format('Y-m-d'), $limit->end_date->format('Y-m-d')); - $this->warn($message); - Log::warning($message); - return; - } - $limit->period = $period; - $limit->save(); - - $msg = sprintf('Budget limit #%d (%s - %s) period is "%s".', $limit->id, $limit->start_date->format('Y-m-d'), $limit->end_date->format('Y-m-d'), $period); - Log::debug($msg); - - } } diff --git a/app/Console/Commands/Upgrade/BackToJournals.php b/app/Console/Commands/Upgrade/BackToJournals.php index 6681fc030f..e2956bd6ef 100644 --- a/app/Console/Commands/Upgrade/BackToJournals.php +++ b/app/Console/Commands/Upgrade/BackToJournals.php @@ -83,43 +83,16 @@ class BackToJournals extends Command } /** - * @return array + * @return bool */ - private function getIdsForBudgets(): array + private function isMigrated(): bool { - $transactions = DB::table('budget_transaction')->distinct()->get(['transaction_id'])->pluck('transaction_id')->toArray(); - $array = []; - $chunks = array_chunk($transactions, 500); - - foreach ($chunks as $chunk) { - $set = DB::table('transactions') - ->whereIn('transactions.id', $chunk) - ->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray(); - /** @noinspection SlowArrayOperationsInLoopInspection */ - $array = array_merge($array, $set); + $configVar = app('fireflyconfig')->get(MigrateToGroups::CONFIG_NAME, false); + if (null !== $configVar) { + return (bool)$configVar->data; } - return $array; - } - - /** - * @return array - */ - private function getIdsForCategories(): array - { - $transactions = DB::table('category_transaction')->distinct()->get(['transaction_id'])->pluck('transaction_id')->toArray(); - $array = []; - $chunks = array_chunk($transactions, 500); - - foreach ($chunks as $chunk) { - $set = DB::table('transactions') - ->whereIn('transactions.id', $chunk) - ->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray(); - /** @noinspection SlowArrayOperationsInLoopInspection */ - $array = array_merge($array, $set); - } - - return $array; + return false; // @codeCoverageIgnore } /** @@ -129,33 +102,12 @@ class BackToJournals extends Command { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); if (null !== $configVar) { - return (bool) $configVar->data; + return (bool)$configVar->data; } return false; // @codeCoverageIgnore } - /** - * @return bool - */ - private function isMigrated(): bool - { - $configVar = app('fireflyconfig')->get(MigrateToGroups::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } - - return false; // @codeCoverageIgnore - } - - /** - * - */ - private function markAsExecuted(): void - { - app('fireflyconfig')->set(self::CONFIG_NAME, true); - } - /** * */ @@ -189,6 +141,26 @@ class BackToJournals extends Command } } + /** + * @return array + */ + private function getIdsForBudgets(): array + { + $transactions = DB::table('budget_transaction')->distinct()->get(['transaction_id'])->pluck('transaction_id')->toArray(); + $array = []; + $chunks = array_chunk($transactions, 500); + + foreach ($chunks as $chunk) { + $set = DB::table('transactions') + ->whereIn('transactions.id', $chunk) + ->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray(); + /** @noinspection SlowArrayOperationsInLoopInspection */ + $array = array_merge($array, $set); + } + + return $array; + } + /** * @param TransactionJournal $journal */ @@ -213,7 +185,7 @@ class BackToJournals extends Command // both have a budget, but they don't match. if (null !== $budget && null !== $journalBudget && $budget->id !== $journalBudget->id) { // sync to journal: - $journal->budgets()->sync([(int) $budget->id]); + $journal->budgets()->sync([(int)$budget->id]); return; } @@ -221,7 +193,7 @@ class BackToJournals extends Command // transaction has a budget, but the journal doesn't. if (null !== $budget && null === $journalBudget) { // sync to journal: - $journal->budgets()->sync([(int) $budget->id]); + $journal->budgets()->sync([(int)$budget->id]); } } @@ -249,6 +221,26 @@ class BackToJournals extends Command } } + /** + * @return array + */ + private function getIdsForCategories(): array + { + $transactions = DB::table('category_transaction')->distinct()->get(['transaction_id'])->pluck('transaction_id')->toArray(); + $array = []; + $chunks = array_chunk($transactions, 500); + + foreach ($chunks as $chunk) { + $set = DB::table('transactions') + ->whereIn('transactions.id', $chunk) + ->get(['transaction_journal_id'])->pluck('transaction_journal_id')->toArray(); + /** @noinspection SlowArrayOperationsInLoopInspection */ + $array = array_merge($array, $set); + } + + return $array; + } + /** * @param TransactionJournal $journal */ @@ -272,12 +264,20 @@ class BackToJournals extends Command // both have a category, but they don't match. if (null !== $category && null !== $journalCategory && $category->id !== $journalCategory->id) { // sync to journal: - $journal->categories()->sync([(int) $category->id]); + $journal->categories()->sync([(int)$category->id]); } // transaction has a category, but the journal doesn't. if (null !== $category && null === $journalCategory) { - $journal->categories()->sync([(int) $category->id]); + $journal->categories()->sync([(int)$category->id]); } } + + /** + * + */ + private function markAsExecuted(): void + { + app('fireflyconfig')->set(self::CONFIG_NAME, true); + } } diff --git a/app/Console/Commands/Upgrade/BudgetLimitCurrency.php b/app/Console/Commands/Upgrade/BudgetLimitCurrency.php index 37ed9ff665..586d9fae96 100644 --- a/app/Console/Commands/Upgrade/BudgetLimitCurrency.php +++ b/app/Console/Commands/Upgrade/BudgetLimitCurrency.php @@ -103,7 +103,7 @@ class BudgetLimitCurrency extends Command { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); if (null !== $configVar) { - return (bool) $configVar->data; + return (bool)$configVar->data; } return false; // @codeCoverageIgnore diff --git a/app/Console/Commands/Upgrade/CCLiabilities.php b/app/Console/Commands/Upgrade/CCLiabilities.php index 36a23a96a8..e8f442f9df 100644 --- a/app/Console/Commands/Upgrade/CCLiabilities.php +++ b/app/Console/Commands/Upgrade/CCLiabilities.php @@ -102,7 +102,7 @@ class CCLiabilities extends Command { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); if (null !== $configVar) { - return (bool) $configVar->data; + return (bool)$configVar->data; } return false; // @codeCoverageIgnore diff --git a/app/Console/Commands/Upgrade/MigrateAttachments.php b/app/Console/Commands/Upgrade/MigrateAttachments.php index e401a12045..a02d72f793 100644 --- a/app/Console/Commands/Upgrade/MigrateAttachments.php +++ b/app/Console/Commands/Upgrade/MigrateAttachments.php @@ -72,7 +72,7 @@ class MigrateAttachments extends Command foreach ($attachments as $att) { // move description: - $attDescription = (string) $att->description; + $attDescription = (string)$att->description; if ('' !== $attDescription) { // find or create note: @@ -112,7 +112,7 @@ class MigrateAttachments extends Command { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); if (null !== $configVar) { - return (bool) $configVar->data; + return (bool)$configVar->data; } return false; // @codeCoverageIgnore diff --git a/app/Console/Commands/Upgrade/MigrateJournalNotes.php b/app/Console/Commands/Upgrade/MigrateJournalNotes.php index 72760471d8..5b1ef28746 100644 --- a/app/Console/Commands/Upgrade/MigrateJournalNotes.php +++ b/app/Console/Commands/Upgrade/MigrateJournalNotes.php @@ -111,7 +111,7 @@ class MigrateJournalNotes extends Command { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); if (null !== $configVar) { - return (bool) $configVar->data; + return (bool)$configVar->data; } return false; // @codeCoverageIgnore diff --git a/app/Console/Commands/Upgrade/MigrateRecurrenceMeta.php b/app/Console/Commands/Upgrade/MigrateRecurrenceMeta.php index e02495bd89..0a35c94c5b 100644 --- a/app/Console/Commands/Upgrade/MigrateRecurrenceMeta.php +++ b/app/Console/Commands/Upgrade/MigrateRecurrenceMeta.php @@ -85,19 +85,26 @@ class MigrateRecurrenceMeta extends Command { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); if (null !== $configVar) { - return (bool) $configVar->data; + return (bool)$configVar->data; } return false; // @codeCoverageIgnore } - /** - * + * @return int */ - private function markAsExecuted(): void + private function migrateMetaData(): int { - app('fireflyconfig')->set(self::CONFIG_NAME, true); + $count = 0; + // get all recurrence meta data: + $collection = RecurrenceMeta::with('recurrence')->get(); + /** @var RecurrenceMeta $meta */ + foreach ($collection as $meta) { + $count += $this->migrateEntry($meta); + } + + return $count; } /** @@ -135,18 +142,10 @@ class MigrateRecurrenceMeta extends Command } /** - * @return int + * */ - private function migrateMetaData(): int + private function markAsExecuted(): void { - $count = 0; - // get all recurrence meta data: - $collection = RecurrenceMeta::with('recurrence')->get(); - /** @var RecurrenceMeta $meta */ - foreach ($collection as $meta) { - $count += $this->migrateEntry($meta); - } - - return $count; + app('fireflyconfig')->set(self::CONFIG_NAME, true); } } diff --git a/app/Console/Commands/Upgrade/MigrateRecurrenceType.php b/app/Console/Commands/Upgrade/MigrateRecurrenceType.php new file mode 100644 index 0000000000..bb86826544 --- /dev/null +++ b/app/Console/Commands/Upgrade/MigrateRecurrenceType.php @@ -0,0 +1,109 @@ +isExecuted() && true !== $this->option('force')) { + $this->warn('This command has already been executed.'); + + return 0; + } + + $this->migrateTypes(); + + $this->markAsExecuted(); + + $end = round(microtime(true) - $start, 2); + $this->info(sprintf('Update recurring transaction types in %s seconds.', $end)); + + return 0; + } + + /** + * @return bool + */ + private function isExecuted(): bool + { + $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + if (null !== $configVar) { + return (bool)$configVar->data; + } + + return false; // @codeCoverageIgnore + } + + /** + * + */ + private function getInvalidType(): TransactionType + { + return TransactionType::whereType(TransactionType::INVALID)->firstOrCreate(['type' => TransactionType::INVALID]); + } + + /** + * + */ + private function migrateTypes(): void + { + $set = Recurrence::get(); + /** @var Recurrence $recurrence */ + foreach ($set as $recurrence) { + if ($recurrence->transactionType->type !== TransactionType::INVALID) { + $this->migrateRecurrence($recurrence); + } + } + } + + /** + * + */ + private function markAsExecuted(): void + { + app('fireflyconfig')->set(self::CONFIG_NAME, true); + } + + private function migrateRecurrence(Recurrence $recurrence): void + { + $originalType = (int)$recurrence->transaction_type_id; + $newType = $this->getInvalidType(); + $recurrence->transaction_type_id = $newType->id; + $recurrence->save(); + /** @var RecurrenceTransaction $transaction */ + foreach ($recurrence->recurrenceTransactions as $transaction) { + $transaction->transaction_type_id = $originalType; + $transaction->save(); + } + $this->line(sprintf('Updated recurrence #%d to new transaction type model.', $recurrence->id)); + } +} diff --git a/app/Console/Commands/Upgrade/MigrateTagLocations.php b/app/Console/Commands/Upgrade/MigrateTagLocations.php index b1693b9f40..b6a9bacbad 100644 --- a/app/Console/Commands/Upgrade/MigrateTagLocations.php +++ b/app/Console/Commands/Upgrade/MigrateTagLocations.php @@ -71,6 +71,30 @@ class MigrateTagLocations extends Command return 0; } + /** + * @return bool + */ + private function isExecuted(): bool + { + $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + if (null !== $configVar) { + return (bool)$configVar->data; + } + + return false; // @codeCoverageIgnore + } + + private function migrateTagLocations(): void + { + $tags = Tag::get(); + /** @var Tag $tag */ + foreach ($tags as $tag) { + if ($this->hasLocationDetails($tag)) { + $this->migrateLocationDetails($tag); + } + } + } + /** * @param Tag $tag * @@ -81,28 +105,6 @@ class MigrateTagLocations extends Command return null !== $tag->latitude && null !== $tag->longitude && null !== $tag->zoomLevel; } - /** - * @return bool - */ - private function isExecuted(): bool - { - $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } - - return false; // @codeCoverageIgnore - } - - - /** - * - */ - private function markAsExecuted(): void - { - app('fireflyconfig')->set(self::CONFIG_NAME, true); - } - /** * @param Tag $tag */ @@ -121,15 +123,12 @@ class MigrateTagLocations extends Command $tag->save(); } - private function migrateTagLocations(): void + /** + * + */ + private function markAsExecuted(): void { - $tags = Tag::get(); - /** @var Tag $tag */ - foreach ($tags as $tag) { - if ($this->hasLocationDetails($tag)) { - $this->migrateLocationDetails($tag); - } - } + app('fireflyconfig')->set(self::CONFIG_NAME, true); } diff --git a/app/Console/Commands/Upgrade/MigrateToGroups.php b/app/Console/Commands/Upgrade/MigrateToGroups.php index 94379c4473..7e12b33577 100644 --- a/app/Console/Commands/Upgrade/MigrateToGroups.php +++ b/app/Console/Commands/Upgrade/MigrateToGroups.php @@ -112,122 +112,19 @@ class MigrateToGroups extends Command } /** - * @param TransactionJournal $journal - * @param Transaction $transaction + * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is + * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should + * be called from the handle method instead of using the constructor to initialize the command. * - * @return Transaction|null + * @codeCoverageIgnore */ - private function findOpposingTransaction(TransactionJournal $journal, Transaction $transaction): ?Transaction + private function stupidLaravel(): void { - $set = $journal->transactions->filter( - static function (Transaction $subject) use ($transaction) { - $amount = (float) $transaction->amount * -1 === (float) $subject->amount; - $identifier = $transaction->identifier === $subject->identifier; - Log::debug(sprintf('Amount the same? %s', var_export($amount, true))); - Log::debug(sprintf('ID the same? %s', var_export($identifier, true))); - - return $amount && $identifier; - } - ); - - return $set->first(); - } - - /** - * @param TransactionJournal $journal - * - * @return Collection - */ - private function getDestinationTransactions(TransactionJournal $journal): Collection - { - return $journal->transactions->filter( - static function (Transaction $transaction) { - return $transaction->amount > 0; - } - ); - } - - /** - * @param Transaction $left - * @param Transaction $right - * - * @return int|null - */ - private function getTransactionBudget(Transaction $left, Transaction $right): ?int - { - Log::debug('Now in getTransactionBudget()'); - - // try to get a budget ID from the left transaction: - /** @var Budget $budget */ - $budget = $left->budgets()->first(); - if (null !== $budget) { - Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $left->id)); - - return (int) $budget->id; - } - - // try to get a budget ID from the right transaction: - /** @var Budget $budget */ - $budget = $right->budgets()->first(); - if (null !== $budget) { - Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $right->id)); - - return (int) $budget->id; - } - Log::debug('Neither left or right have a budget, return NULL'); - - // if all fails, return NULL. - return null; - } - - /** - * @param Transaction $left - * @param Transaction $right - * - * @return int|null - */ - private function getTransactionCategory(Transaction $left, Transaction $right): ?int - { - Log::debug('Now in getTransactionCategory()'); - - // try to get a category ID from the left transaction: - /** @var Category $category */ - $category = $left->categories()->first(); - if (null !== $category) { - Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $left->id)); - - return (int) $category->id; - } - - // try to get a category ID from the left transaction: - /** @var Category $category */ - $category = $right->categories()->first(); - if (null !== $category) { - Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $category->id)); - - return (int) $category->id; - } - Log::debug('Neither left or right have a category, return NULL'); - - // if all fails, return NULL. - return null; - } - - /** - * @param array $array - */ - private function giveGroup(array $array): void - { - $groupId = DB::table('transaction_groups')->insertGetId( - [ - 'created_at' => date('Y-m-d H:i:s'), - 'updated_at' => date('Y-m-d H:i:s'), - 'title' => null, - 'user_id' => $array['user_id'], - ] - ); - DB::table('transaction_journals')->where('id', $array['id'])->update(['transaction_group_id' => $groupId]); - $this->count++; + $this->count = 0; + $this->journalRepository = app(JournalRepositoryInterface::class); + $this->service = app(JournalDestroyService::class); + $this->groupFactory = app(TransactionGroupFactory::class); + $this->cliRepository = app(JournalCLIRepositoryInterface::class); } /** @@ -237,32 +134,12 @@ class MigrateToGroups extends Command { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); if (null !== $configVar) { - return (bool) $configVar->data; + return (bool)$configVar->data; } return false; // @codeCoverageIgnore } - /** - * Gives all journals without a group a group. - */ - private function makeGroupsFromAll(): void - { - $orphanedJournals = $this->cliRepository->getJournalsWithoutGroup(); - $total = count($orphanedJournals); - if ($total > 0) { - Log::debug(sprintf('Going to convert %d transaction journals. Please hold..', $total)); - $this->line(sprintf('Going to convert %d transaction journals. Please hold..', $total)); - /** @var array $journal */ - foreach ($orphanedJournals as $array) { - $this->giveGroup($array); - } - } - if (0 === $total) { - $this->info('No need to convert transaction journals.'); - } - } - /** * @throws Exception */ @@ -427,6 +304,145 @@ class MigrateToGroups extends Command ); } + /** + * @param TransactionJournal $journal + * + * @return Collection + */ + private function getDestinationTransactions(TransactionJournal $journal): Collection + { + return $journal->transactions->filter( + static function (Transaction $transaction) { + return $transaction->amount > 0; + } + ); + } + + /** + * @param TransactionJournal $journal + * @param Transaction $transaction + * + * @return Transaction|null + */ + private function findOpposingTransaction(TransactionJournal $journal, Transaction $transaction): ?Transaction + { + $set = $journal->transactions->filter( + static function (Transaction $subject) use ($transaction) { + $amount = (float)$transaction->amount * -1 === (float)$subject->amount; + $identifier = $transaction->identifier === $subject->identifier; + Log::debug(sprintf('Amount the same? %s', var_export($amount, true))); + Log::debug(sprintf('ID the same? %s', var_export($identifier, true))); + + return $amount && $identifier; + } + ); + + return $set->first(); + } + + /** + * @param Transaction $left + * @param Transaction $right + * + * @return int|null + */ + private function getTransactionBudget(Transaction $left, Transaction $right): ?int + { + Log::debug('Now in getTransactionBudget()'); + + // try to get a budget ID from the left transaction: + /** @var Budget $budget */ + $budget = $left->budgets()->first(); + if (null !== $budget) { + Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $left->id)); + + return (int)$budget->id; + } + + // try to get a budget ID from the right transaction: + /** @var Budget $budget */ + $budget = $right->budgets()->first(); + if (null !== $budget) { + Log::debug(sprintf('Return budget #%d, from transaction #%d', $budget->id, $right->id)); + + return (int)$budget->id; + } + Log::debug('Neither left or right have a budget, return NULL'); + + // if all fails, return NULL. + return null; + } + + /** + * @param Transaction $left + * @param Transaction $right + * + * @return int|null + */ + private function getTransactionCategory(Transaction $left, Transaction $right): ?int + { + Log::debug('Now in getTransactionCategory()'); + + // try to get a category ID from the left transaction: + /** @var Category $category */ + $category = $left->categories()->first(); + if (null !== $category) { + Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $left->id)); + + return (int)$category->id; + } + + // try to get a category ID from the left transaction: + /** @var Category $category */ + $category = $right->categories()->first(); + if (null !== $category) { + Log::debug(sprintf('Return category #%d, from transaction #%d', $category->id, $category->id)); + + return (int)$category->id; + } + Log::debug('Neither left or right have a category, return NULL'); + + // if all fails, return NULL. + return null; + } + + /** + * Gives all journals without a group a group. + */ + private function makeGroupsFromAll(): void + { + $orphanedJournals = $this->cliRepository->getJournalsWithoutGroup(); + $total = count($orphanedJournals); + if ($total > 0) { + Log::debug(sprintf('Going to convert %d transaction journals. Please hold..', $total)); + $this->line(sprintf('Going to convert %d transaction journals. Please hold..', $total)); + /** @var array $journal */ + foreach ($orphanedJournals as $array) { + $this->giveGroup($array); + } + } + if (0 === $total) { + $this->info('No need to convert transaction journals.'); + } + } + + /** + * @param array $array + */ + private function giveGroup(array $array): void + { + $groupId = DB::table('transaction_groups')->insertGetId( + [ + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + 'title' => null, + 'user_id' => $array['user_id'], + ] + ); + DB::table('transaction_journals')->where('id', $array['id'])->update(['transaction_group_id' => $groupId]); + $this->count++; + } + /** * */ @@ -434,20 +450,4 @@ class MigrateToGroups extends Command { app('fireflyconfig')->set(self::CONFIG_NAME, true); } - - /** - * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is - * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should - * be called from the handle method instead of using the constructor to initialize the command. - * - * @codeCoverageIgnore - */ - private function stupidLaravel(): void - { - $this->count = 0; - $this->journalRepository = app(JournalRepositoryInterface::class); - $this->service = app(JournalDestroyService::class); - $this->groupFactory = app(TransactionGroupFactory::class); - $this->cliRepository = app(JournalCLIRepositoryInterface::class); - } } diff --git a/app/Console/Commands/Upgrade/MigrateToRules.php b/app/Console/Commands/Upgrade/MigrateToRules.php index 5029dbd70d..d7b8a1a2b3 100644 --- a/app/Console/Commands/Upgrade/MigrateToRules.php +++ b/app/Console/Commands/Upgrade/MigrateToRules.php @@ -67,8 +67,8 @@ class MigrateToRules extends Command /** * Execute the console command. * - * @throws FireflyException * @return int + * @throws FireflyException */ public function handle(): int { @@ -103,6 +103,22 @@ class MigrateToRules extends Command return 0; } + /** + * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is + * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should + * be called from the handle method instead of using the constructor to initialize the command. + * + * @codeCoverageIgnore + */ + private function stupidLaravel(): void + { + $this->count = 0; + $this->userRepository = app(UserRepositoryInterface::class); + $this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class); + $this->billRepository = app(BillRepositoryInterface::class); + $this->ruleRepository = app(RuleRepositoryInterface::class); + } + /** * @return bool */ @@ -110,18 +126,46 @@ class MigrateToRules extends Command { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); if (null !== $configVar) { - return (bool) $configVar->data; + return (bool)$configVar->data; } return false; // @codeCoverageIgnore } /** + * Migrate bills to new rule structure for a specific user. * + * @param User $user + * + * @throws FireflyException */ - private function markAsExecuted(): void + private function migrateUser(User $user): void { - app('fireflyconfig')->set(self::CONFIG_NAME, true); + $this->ruleGroupRepository->setUser($user); + $this->billRepository->setUser($user); + $this->ruleRepository->setUser($user); + + /** @var Preference $lang */ + $lang = app('preferences')->getForUser($user, 'language', 'en_US'); + $groupTitle = (string)trans('firefly.rulegroup_for_bills_title', [], $lang->data); + $ruleGroup = $this->ruleGroupRepository->findByTitle($groupTitle); + + if (null === $ruleGroup) { + $ruleGroup = $this->ruleGroupRepository->store( + [ + 'title' => (string)trans('firefly.rulegroup_for_bills_title', [], $lang->data), + 'description' => (string)trans('firefly.rulegroup_for_bills_description', [], $lang->data), + 'active' => true, + ] + ); + } + $bills = $this->billRepository->getBills(); + + /** @var Bill $bill */ + foreach ($bills as $bill) { + $this->migrateBill($ruleGroup, $bill, $lang); + } + } /** @@ -142,8 +186,8 @@ class MigrateToRules extends Command 'active' => true, 'strict' => false, 'stop_processing' => false, // field is no longer used. - 'title' => (string) trans('firefly.rule_for_bill_title', ['name' => $bill->name], $language->data), - 'description' => (string) trans('firefly.rule_for_bill_description', ['name' => $bill->name], $language->data), + 'title' => (string)trans('firefly.rule_for_bill_title', ['name' => $bill->name], $language->data), + 'description' => (string)trans('firefly.rule_for_bill_description', ['name' => $bill->name], $language->data), 'trigger' => 'store-journal', 'triggers' => [ [ @@ -196,54 +240,10 @@ class MigrateToRules extends Command } /** - * Migrate bills to new rule structure for a specific user. * - * @param User $user - * - * @throws FireflyException */ - private function migrateUser(User $user): void + private function markAsExecuted(): void { - $this->ruleGroupRepository->setUser($user); - $this->billRepository->setUser($user); - $this->ruleRepository->setUser($user); - - /** @var Preference $lang */ - $lang = app('preferences')->getForUser($user, 'language', 'en_US'); - $groupTitle = (string) trans('firefly.rulegroup_for_bills_title', [], $lang->data); - $ruleGroup = $this->ruleGroupRepository->findByTitle($groupTitle); - - if (null === $ruleGroup) { - $ruleGroup = $this->ruleGroupRepository->store( - [ - 'title' => (string) trans('firefly.rulegroup_for_bills_title', [], $lang->data), - 'description' => (string) trans('firefly.rulegroup_for_bills_description', [], $lang->data), - 'active' => true, - ] - ); - } - $bills = $this->billRepository->getBills(); - - /** @var Bill $bill */ - foreach ($bills as $bill) { - $this->migrateBill($ruleGroup, $bill, $lang); - } - - } - - /** - * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is - * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should - * be called from the handle method instead of using the constructor to initialize the command. - * - * @codeCoverageIgnore - */ - private function stupidLaravel(): void - { - $this->count = 0; - $this->userRepository = app(UserRepositoryInterface::class); - $this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class); - $this->billRepository = app(BillRepositoryInterface::class); - $this->ruleRepository = app(RuleRepositoryInterface::class); + app('fireflyconfig')->set(self::CONFIG_NAME, true); } } diff --git a/app/Console/Commands/Upgrade/OtherCurrenciesCorrections.php b/app/Console/Commands/Upgrade/OtherCurrenciesCorrections.php index ba3999405a..7aee00ec54 100644 --- a/app/Console/Commands/Upgrade/OtherCurrenciesCorrections.php +++ b/app/Console/Commands/Upgrade/OtherCurrenciesCorrections.php @@ -95,68 +95,20 @@ class OtherCurrenciesCorrections extends Command } /** - * @param Account $account + * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is + * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should + * be called from the handle method instead of using the constructor to initialize the command. * - * @return TransactionCurrency|null + * @codeCoverageIgnore */ - private function getCurrency(Account $account): ?TransactionCurrency + private function stupidLaravel(): void { - $accountId = $account->id; - if (array_key_exists($accountId, $this->accountCurrencies) && 0 === $this->accountCurrencies[$accountId]) { - return null; // @codeCoverageIgnore - } - if (array_key_exists($accountId, $this->accountCurrencies) && $this->accountCurrencies[$accountId] instanceof TransactionCurrency) { - return $this->accountCurrencies[$accountId]; // @codeCoverageIgnore - } - $currency = $this->accountRepos->getAccountCurrency($account); - if (null === $currency) { - // @codeCoverageIgnoreStart - $this->accountCurrencies[$accountId] = 0; - - return null; - // @codeCoverageIgnoreEnd - } - $this->accountCurrencies[$accountId] = $currency; - - return $currency; - } - - /** - * Gets the transaction that determines the transaction that "leads" and will determine - * the currency to be used by all transactions, and the journal itself. - * - * @param TransactionJournal $journal - * - * @return Transaction|null - */ - private function getLeadTransaction(TransactionJournal $journal): ?Transaction - { - /** @var Transaction $lead */ - $lead = null; - switch ($journal->transactionType->type) { - default: - break; - case TransactionType::WITHDRAWAL: - $lead = $journal->transactions()->where('amount', '<', 0)->first(); - break; - case TransactionType::DEPOSIT: - $lead = $journal->transactions()->where('amount', '>', 0)->first(); - break; - case TransactionType::OPENING_BALANCE: - // whichever isn't an initial balance account: - $lead = $journal->transactions()->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')->leftJoin( - 'account_types', 'accounts.account_type_id', '=', 'account_types.id' - )->where('account_types.type', '!=', AccountType::INITIAL_BALANCE)->first(['transactions.*']); - break; - case TransactionType::RECONCILIATION: - // whichever isn't the reconciliation account: - $lead = $journal->transactions()->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')->leftJoin( - 'account_types', 'accounts.account_type_id', '=', 'account_types.id' - )->where('account_types.type', '!=', AccountType::RECONCILIATION)->first(['transactions.*']); - break; - } - - return $lead; + $this->count = 0; + $this->accountCurrencies = []; + $this->accountRepos = app(AccountRepositoryInterface::class); + $this->currencyRepos = app(CurrencyRepositoryInterface::class); + $this->journalRepos = app(JournalRepositoryInterface::class); + $this->cliRepos = app(JournalCLIRepositoryInterface::class); } /** @@ -173,28 +125,21 @@ class OtherCurrenciesCorrections extends Command } /** - * + * This routine verifies that withdrawals, deposits and opening balances have the correct currency settings for + * the accounts they are linked to. + * Both source and destination must match the respective currency preference of the related asset account. + * So FF3 must verify all transactions. */ - private function markAsExecuted(): void + private function updateOtherJournalsCurrencies(): void { - app('fireflyconfig')->set(self::CONFIG_NAME, true); - } + $set = $this->cliRepos->getAllJournals( + [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,] + ); - /** - * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is - * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should - * be called from the handle method instead of using the constructor to initialize the command. - * - * @codeCoverageIgnore - */ - private function stupidLaravel(): void - { - $this->count = 0; - $this->accountCurrencies = []; - $this->accountRepos = app(AccountRepositoryInterface::class); - $this->currencyRepos = app(CurrencyRepositoryInterface::class); - $this->journalRepos = app(JournalRepositoryInterface::class); - $this->cliRepos = app(JournalCLIRepositoryInterface::class); + /** @var TransactionJournal $journal */ + foreach ($set as $journal) { + $this->updateJournalCurrency($journal); + } } /** @@ -256,20 +201,75 @@ class OtherCurrenciesCorrections extends Command } /** - * This routine verifies that withdrawals, deposits and opening balances have the correct currency settings for - * the accounts they are linked to. - * Both source and destination must match the respective currency preference of the related asset account. - * So FF3 must verify all transactions. + * Gets the transaction that determines the transaction that "leads" and will determine + * the currency to be used by all transactions, and the journal itself. + * + * @param TransactionJournal $journal + * + * @return Transaction|null */ - private function updateOtherJournalsCurrencies(): void + private function getLeadTransaction(TransactionJournal $journal): ?Transaction { - $set = $this->cliRepos->getAllJournals( - [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,] - ); - - /** @var TransactionJournal $journal */ - foreach ($set as $journal) { - $this->updateJournalCurrency($journal); + /** @var Transaction $lead */ + $lead = null; + switch ($journal->transactionType->type) { + default: + break; + case TransactionType::WITHDRAWAL: + $lead = $journal->transactions()->where('amount', '<', 0)->first(); + break; + case TransactionType::DEPOSIT: + $lead = $journal->transactions()->where('amount', '>', 0)->first(); + break; + case TransactionType::OPENING_BALANCE: + // whichever isn't an initial balance account: + $lead = $journal->transactions()->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')->leftJoin( + 'account_types', 'accounts.account_type_id', '=', 'account_types.id' + )->where('account_types.type', '!=', AccountType::INITIAL_BALANCE)->first(['transactions.*']); + break; + case TransactionType::RECONCILIATION: + // whichever isn't the reconciliation account: + $lead = $journal->transactions()->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')->leftJoin( + 'account_types', 'accounts.account_type_id', '=', 'account_types.id' + )->where('account_types.type', '!=', AccountType::RECONCILIATION)->first(['transactions.*']); + break; } + + return $lead; + } + + /** + * @param Account $account + * + * @return TransactionCurrency|null + */ + private function getCurrency(Account $account): ?TransactionCurrency + { + $accountId = $account->id; + if (array_key_exists($accountId, $this->accountCurrencies) && 0 === $this->accountCurrencies[$accountId]) { + return null; // @codeCoverageIgnore + } + if (array_key_exists($accountId, $this->accountCurrencies) && $this->accountCurrencies[$accountId] instanceof TransactionCurrency) { + return $this->accountCurrencies[$accountId]; // @codeCoverageIgnore + } + $currency = $this->accountRepos->getAccountCurrency($account); + if (null === $currency) { + // @codeCoverageIgnoreStart + $this->accountCurrencies[$accountId] = 0; + + return null; + // @codeCoverageIgnoreEnd + } + $this->accountCurrencies[$accountId] = $currency; + + return $currency; + } + + /** + * + */ + private function markAsExecuted(): void + { + app('fireflyconfig')->set(self::CONFIG_NAME, true); } } diff --git a/app/Console/Commands/Upgrade/RenameAccountMeta.php b/app/Console/Commands/Upgrade/RenameAccountMeta.php index 01aaa1b6fd..d697c17c34 100644 --- a/app/Console/Commands/Upgrade/RenameAccountMeta.php +++ b/app/Console/Commands/Upgrade/RenameAccountMeta.php @@ -102,7 +102,7 @@ class RenameAccountMeta extends Command { $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); if (null !== $configVar) { - return (bool) $configVar->data; + return (bool)$configVar->data; } return false; // @codeCoverageIgnore diff --git a/app/Console/Commands/Upgrade/TransactionIdentifier.php b/app/Console/Commands/Upgrade/TransactionIdentifier.php index d220a8b27e..9ddad9f896 100644 --- a/app/Console/Commands/Upgrade/TransactionIdentifier.php +++ b/app/Console/Commands/Upgrade/TransactionIdentifier.php @@ -105,60 +105,6 @@ class TransactionIdentifier extends Command return 0; } - /** - * @param Transaction $transaction - * @param array $exclude - * - * @return Transaction|null - */ - private function findOpposing(Transaction $transaction, array $exclude): ?Transaction - { - // find opposing: - $amount = bcmul((string) $transaction->amount, '-1'); - - try { - /** @var Transaction $opposing */ - $opposing = Transaction::where('transaction_journal_id', $transaction->transaction_journal_id) - ->where('amount', $amount)->where('identifier', '=', 0) - ->whereNotIn('id', $exclude) - ->first(); - // @codeCoverageIgnoreStart - } catch (QueryException $e) { - Log::error($e->getMessage()); - $this->error('Firefly III could not find the "identifier" field in the "transactions" table.'); - $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.'); - - return null; - } - - // @codeCoverageIgnoreEnd - - return $opposing; - } - - /** - * @return bool - */ - private function isExecuted(): bool - { - $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool) $configVar->data; - } - - return false; // @codeCoverageIgnore - } - - /** - * - */ - private function markAsExecuted(): void - { - app('fireflyconfig')->set(self::CONFIG_NAME, true); - } - /** * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should @@ -173,6 +119,19 @@ class TransactionIdentifier extends Command $this->count = 0; } + /** + * @return bool + */ + private function isExecuted(): bool + { + $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + if (null !== $configVar) { + return (bool)$configVar->data; + } + + return false; // @codeCoverageIgnore + } + /** * Grab all positive transactions from this journal that are not deleted. for each one, grab the negative opposing one * which has 0 as an identifier and give it the same identifier. @@ -202,4 +161,45 @@ class TransactionIdentifier extends Command } } + + /** + * @param Transaction $transaction + * @param array $exclude + * + * @return Transaction|null + */ + private function findOpposing(Transaction $transaction, array $exclude): ?Transaction + { + // find opposing: + $amount = bcmul((string)$transaction->amount, '-1'); + + try { + /** @var Transaction $opposing */ + $opposing = Transaction::where('transaction_journal_id', $transaction->transaction_journal_id) + ->where('amount', $amount)->where('identifier', '=', 0) + ->whereNotIn('id', $exclude) + ->first(); + // @codeCoverageIgnoreStart + } catch (QueryException $e) { + Log::error($e->getMessage()); + $this->error('Firefly III could not find the "identifier" field in the "transactions" table.'); + $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.'); + + return null; + } + + // @codeCoverageIgnoreEnd + + return $opposing; + } + + /** + * + */ + private function markAsExecuted(): void + { + app('fireflyconfig')->set(self::CONFIG_NAME, true); + } } diff --git a/app/Console/Commands/Upgrade/TransferCurrenciesCorrections.php b/app/Console/Commands/Upgrade/TransferCurrenciesCorrections.php index 7565e20473..b23de4ae19 100644 --- a/app/Console/Commands/Upgrade/TransferCurrenciesCorrections.php +++ b/app/Console/Commands/Upgrade/TransferCurrenciesCorrections.php @@ -56,12 +56,12 @@ class TransferCurrenciesCorrections extends Command private JournalCLIRepositoryInterface $cliRepos; private int $count; - private ?Account $destinationAccount; - private ?TransactionCurrency $destinationCurrency; - private ?Transaction $destinationTransaction; - private ?Account $sourceAccount; - private ?TransactionCurrency $sourceCurrency; - private ?Transaction $sourceTransaction; + private ?Account $destinationAccount; + private ?TransactionCurrency $destinationCurrency; + private ?Transaction $destinationTransaction; + private ?Account $sourceAccount; + private ?TransactionCurrency $sourceCurrency; + private ?Transaction $sourceTransaction; /** * Execute the console command. @@ -99,6 +99,313 @@ class TransferCurrenciesCorrections extends Command return 0; } + /** + * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is + * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should + * be called from the handle method instead of using the constructor to initialize the command. + * + * @codeCoverageIgnore + */ + private function stupidLaravel(): void + { + $this->count = 0; + $this->accountRepos = app(AccountRepositoryInterface::class); + $this->cliRepos = app(JournalCLIRepositoryInterface::class); + $this->accountCurrencies = []; + $this->resetInformation(); + } + + /** + * Reset all the class fields for the current transfer. + * + * @codeCoverageIgnore + */ + private function resetInformation(): void + { + $this->sourceTransaction = null; + $this->sourceAccount = null; + $this->sourceCurrency = null; + $this->destinationTransaction = null; + $this->destinationAccount = null; + $this->destinationCurrency = null; + } + + /** + * @return bool + */ + private function isExecuted(): bool + { + $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); + if (null !== $configVar) { + return (bool)$configVar->data; + } + + return false; // @codeCoverageIgnore + } + + /** + * This routine verifies that transfers have the correct currency settings for the accounts they are linked to. + * For transfers, this is can be a destructive routine since we FORCE them into a currency setting whether they + * like it or not. Previous routines MUST have set the currency setting for both accounts for this to work. + * + * Both source and destination must match the respective currency preference. So FF3 must verify ALL + * transactions. + */ + private function startUpdateRoutine(): void + { + $set = $this->cliRepos->getAllJournals([TransactionType::TRANSFER]); + /** @var TransactionJournal $journal */ + foreach ($set as $journal) { + $this->updateTransferCurrency($journal); + } + } + + /** + * @param TransactionJournal $transfer + */ + private function updateTransferCurrency(TransactionJournal $transfer): void + { + $this->resetInformation(); + + // @codeCoverageIgnoreStart + if ($this->isSplitJournal($transfer)) { + $this->line(sprintf(sprintf('Transaction journal #%d is a split journal. Cannot continue.', $transfer->id))); + + return; + } + // @codeCoverageIgnoreEnd + + $this->getSourceInformation($transfer); + $this->getDestinationInformation($transfer); + + // unexpectedly, either one is null: + // @codeCoverageIgnoreStart + if ($this->isEmptyTransactions()) { + $this->error(sprintf('Source or destination information for transaction journal #%d is null. Cannot fix this one.', $transfer->id)); + + return; + } + // @codeCoverageIgnoreEnd + + + // both accounts must have currency preference: + // @codeCoverageIgnoreStart + if ($this->isNoCurrencyPresent()) { + $this->error( + sprintf('Source or destination accounts for transaction journal #%d have no currency information. Cannot fix this one.', $transfer->id) + ); + + return; + } + // @codeCoverageIgnoreEnd + + // fix source transaction having no currency. + $this->fixSourceNoCurrency(); + + // fix source transaction having bad currency. + $this->fixSourceUnmatchedCurrency(); + + // fix destination transaction having no currency. + $this->fixDestNoCurrency(); + + // fix destination transaction having bad currency. + $this->fixDestinationUnmatchedCurrency(); + + // remove foreign currency information if not necessary. + $this->fixInvalidForeignCurrency(); + // correct foreign currency info if necessary. + $this->fixMismatchedForeignCurrency(); + + // restore missing foreign currency amount. + $this->fixSourceNullForeignAmount(); + + $this->fixDestNullForeignAmount(); + + // fix journal itself: + $this->fixTransactionJournalCurrency($transfer); + } + + /** + * Is this a split transaction journal? + * + * @param TransactionJournal $transfer + * + * @return bool + * @codeCoverageIgnore + */ + private function isSplitJournal(TransactionJournal $transfer): bool + { + return $transfer->transactions->count() > 2; + } + + /** + * Extract source transaction, source account + source account currency from the journal. + * + * @param TransactionJournal $journal + * + * @codeCoverageIgnore + */ + private function getSourceInformation(TransactionJournal $journal): void + { + $this->sourceTransaction = $this->getSourceTransaction($journal); + $this->sourceAccount = null === $this->sourceTransaction ? null : $this->sourceTransaction->account; + $this->sourceCurrency = null === $this->sourceAccount ? null : $this->getCurrency($this->sourceAccount); + } + + /** + * @param TransactionJournal $transfer + * + * @return Transaction|null + * @codeCoverageIgnore + */ + private function getSourceTransaction(TransactionJournal $transfer): ?Transaction + { + return $transfer->transactions()->where('amount', '<', 0)->first(); + } + + /** + * @param Account $account + * + * @return TransactionCurrency|null + */ + private function getCurrency(Account $account): ?TransactionCurrency + { + $accountId = $account->id; + if (array_key_exists($accountId, $this->accountCurrencies) && 0 === $this->accountCurrencies[$accountId]) { + return null; // @codeCoverageIgnore + } + if (array_key_exists($accountId, $this->accountCurrencies) && $this->accountCurrencies[$accountId] instanceof TransactionCurrency) { + return $this->accountCurrencies[$accountId]; // @codeCoverageIgnore + } + $currency = $this->accountRepos->getAccountCurrency($account); + if (null === $currency) { + // @codeCoverageIgnoreStart + $this->accountCurrencies[$accountId] = 0; + + return null; + // @codeCoverageIgnoreEnd + } + $this->accountCurrencies[$accountId] = $currency; + + return $currency; + } + + /** + * Extract destination transaction, destination account + destination account currency from the journal. + * + * @param TransactionJournal $journal + * + * @codeCoverageIgnore + */ + private function getDestinationInformation(TransactionJournal $journal): void + { + $this->destinationTransaction = $this->getDestinationTransaction($journal); + $this->destinationAccount = null === $this->destinationTransaction ? null : $this->destinationTransaction->account; + $this->destinationCurrency = null === $this->destinationAccount ? null : $this->getCurrency($this->destinationAccount); + } + + /** + * @param TransactionJournal $transfer + * + * @return Transaction|null + * @codeCoverageIgnore + */ + private function getDestinationTransaction(TransactionJournal $transfer): ?Transaction + { + return $transfer->transactions()->where('amount', '>', 0)->first(); + } + + /** + * Is either the source or destination transaction NULL? + * + * @return bool + * @codeCoverageIgnore + */ + private function isEmptyTransactions(): bool + { + return null === $this->sourceTransaction || null === $this->destinationTransaction + || null === $this->sourceAccount + || null === $this->destinationAccount; + } + + /** + * @return bool + * @codeCoverageIgnore + */ + private function isNoCurrencyPresent(): bool + { + // source account must have a currency preference. + if (null === $this->sourceCurrency) { + $message = sprintf('Account #%d ("%s") must have currency preference but has none.', $this->sourceAccount->id, $this->sourceAccount->name); + Log::error($message); + $this->error($message); + + return true; + } + + // destination account must have a currency preference. + if (null === $this->destinationCurrency) { + $message = sprintf( + 'Account #%d ("%s") must have currency preference but has none.', + $this->destinationAccount->id, + $this->destinationAccount->name + ); + Log::error($message); + $this->error($message); + + return true; + } + + return false; + } + + /** + * The source transaction must have a currency. If not, it will be added by + * taking it from the source account's preference. + */ + private function fixSourceNoCurrency(): void + { + if (null === $this->sourceTransaction->transaction_currency_id && null !== $this->sourceCurrency) { + $this->sourceTransaction + ->transaction_currency_id + = (int)$this->sourceCurrency->id; + $message = sprintf( + 'Transaction #%d has no currency setting, now set to %s.', + $this->sourceTransaction->id, + $this->sourceCurrency->code + ); + Log::debug($message); + $this->line($message); + $this->count++; + $this->sourceTransaction->save(); + } + } + + /** + * The source transaction must have the correct currency. If not, it will be set by + * taking it from the source account's preference. + */ + private function fixSourceUnmatchedCurrency(): void + { + if (null !== $this->sourceCurrency + && null === $this->sourceTransaction->foreign_amount + && (int)$this->sourceTransaction->transaction_currency_id !== (int)$this->sourceCurrency->id + ) { + $message = sprintf( + 'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.', + $this->sourceTransaction->id, + $this->sourceTransaction->transaction_currency_id, + $this->sourceAccount->id, + $this->sourceTransaction->amount + ); + Log::debug($message); + $this->line($message); + $this->count++; + $this->sourceTransaction->transaction_currency_id = (int)$this->sourceCurrency->id; + $this->sourceTransaction->save(); + } + } + /** * The destination transaction must have a currency. If not, it will be added by * taking it from the destination account's preference. @@ -121,26 +428,6 @@ class TransferCurrenciesCorrections extends Command } } - /** - * If the foreign amount of the destination transaction is null, but that of the other isn't, use this piece of code - * to restore it. - */ - private function fixDestNullForeignAmount(): void - { - if (null === $this->destinationTransaction->foreign_amount && null !== $this->sourceTransaction->foreign_amount) { - $this->destinationTransaction->foreign_amount = bcmul((string)$this->sourceTransaction->foreign_amount, '-1'); - $this->destinationTransaction->save(); - $this->count++; - Log::debug( - sprintf( - 'Restored foreign amount of destination transaction #%d to %s', - $this->destinationTransaction->id, - $this->destinationTransaction->foreign_amount - ) - ); - } - } - /** * The destination transaction must have the correct currency. If not, it will be set by * taking it from the destination account's preference. @@ -220,28 +507,6 @@ class TransferCurrenciesCorrections extends Command } } - /** - * The source transaction must have a currency. If not, it will be added by - * taking it from the source account's preference. - */ - private function fixSourceNoCurrency(): void - { - if (null === $this->sourceTransaction->transaction_currency_id && null !== $this->sourceCurrency) { - $this->sourceTransaction - ->transaction_currency_id - = (int)$this->sourceCurrency->id; - $message = sprintf( - 'Transaction #%d has no currency setting, now set to %s.', - $this->sourceTransaction->id, - $this->sourceCurrency->code - ); - Log::debug($message); - $this->line($message); - $this->count++; - $this->sourceTransaction->save(); - } - } - /** * If the foreign amount of the source transaction is null, but that of the other isn't, use this piece of code * to restore it. @@ -263,27 +528,22 @@ class TransferCurrenciesCorrections extends Command } /** - * The source transaction must have the correct currency. If not, it will be set by - * taking it from the source account's preference. + * If the foreign amount of the destination transaction is null, but that of the other isn't, use this piece of code + * to restore it. */ - private function fixSourceUnmatchedCurrency(): void + private function fixDestNullForeignAmount(): void { - if (null !== $this->sourceCurrency - && null === $this->sourceTransaction->foreign_amount - && (int)$this->sourceTransaction->transaction_currency_id !== (int)$this->sourceCurrency->id - ) { - $message = sprintf( - 'Transaction #%d has a currency setting #%d that should be #%d. Amount remains %s, currency is changed.', - $this->sourceTransaction->id, - $this->sourceTransaction->transaction_currency_id, - $this->sourceAccount->id, - $this->sourceTransaction->amount - ); - Log::debug($message); - $this->line($message); + if (null === $this->destinationTransaction->foreign_amount && null !== $this->sourceTransaction->foreign_amount) { + $this->destinationTransaction->foreign_amount = bcmul((string)$this->sourceTransaction->foreign_amount, '-1'); + $this->destinationTransaction->save(); $this->count++; - $this->sourceTransaction->transaction_currency_id = (int)$this->sourceCurrency->id; - $this->sourceTransaction->save(); + Log::debug( + sprintf( + 'Restored foreign amount of destination transaction #%d to %s', + $this->destinationTransaction->id, + $this->destinationTransaction->foreign_amount + ) + ); } } @@ -311,153 +571,6 @@ class TransferCurrenciesCorrections extends Command } } - /** - * @param Account $account - * - * @return TransactionCurrency|null - */ - private function getCurrency(Account $account): ?TransactionCurrency - { - $accountId = $account->id; - if (array_key_exists($accountId, $this->accountCurrencies) && 0 === $this->accountCurrencies[$accountId]) { - return null; // @codeCoverageIgnore - } - if (array_key_exists($accountId, $this->accountCurrencies) && $this->accountCurrencies[$accountId] instanceof TransactionCurrency) { - return $this->accountCurrencies[$accountId]; // @codeCoverageIgnore - } - $currency = $this->accountRepos->getAccountCurrency($account); - if (null === $currency) { - // @codeCoverageIgnoreStart - $this->accountCurrencies[$accountId] = 0; - - return null; - // @codeCoverageIgnoreEnd - } - $this->accountCurrencies[$accountId] = $currency; - - return $currency; - } - - /** - * Extract destination transaction, destination account + destination account currency from the journal. - * - * @param TransactionJournal $journal - * - * @codeCoverageIgnore - */ - private function getDestinationInformation(TransactionJournal $journal): void - { - $this->destinationTransaction = $this->getDestinationTransaction($journal); - $this->destinationAccount = null === $this->destinationTransaction ? null : $this->destinationTransaction->account; - $this->destinationCurrency = null === $this->destinationAccount ? null : $this->getCurrency($this->destinationAccount); - } - - /** - * @param TransactionJournal $transfer - * - * @return Transaction|null - * @codeCoverageIgnore - */ - private function getDestinationTransaction(TransactionJournal $transfer): ?Transaction - { - return $transfer->transactions()->where('amount', '>', 0)->first(); - } - - /** - * Extract source transaction, source account + source account currency from the journal. - * - * @param TransactionJournal $journal - * - * @codeCoverageIgnore - */ - private function getSourceInformation(TransactionJournal $journal): void - { - $this->sourceTransaction = $this->getSourceTransaction($journal); - $this->sourceAccount = null === $this->sourceTransaction ? null : $this->sourceTransaction->account; - $this->sourceCurrency = null === $this->sourceAccount ? null : $this->getCurrency($this->sourceAccount); - } - - /** - * @param TransactionJournal $transfer - * - * @return Transaction|null - * @codeCoverageIgnore - */ - private function getSourceTransaction(TransactionJournal $transfer): ?Transaction - { - return $transfer->transactions()->where('amount', '<', 0)->first(); - } - - /** - * Is either the source or destination transaction NULL? - * - * @return bool - * @codeCoverageIgnore - */ - private function isEmptyTransactions(): bool - { - return null === $this->sourceTransaction || null === $this->destinationTransaction - || null === $this->sourceAccount - || null === $this->destinationAccount; - } - - /** - * @return bool - */ - private function isExecuted(): bool - { - $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); - if (null !== $configVar) { - return (bool)$configVar->data; - } - - return false; // @codeCoverageIgnore - } - - /** - * @return bool - * @codeCoverageIgnore - */ - private function isNoCurrencyPresent(): bool - { - // source account must have a currency preference. - if (null === $this->sourceCurrency) { - $message = sprintf('Account #%d ("%s") must have currency preference but has none.', $this->sourceAccount->id, $this->sourceAccount->name); - Log::error($message); - $this->error($message); - - return true; - } - - // destination account must have a currency preference. - if (null === $this->destinationCurrency) { - $message = sprintf( - 'Account #%d ("%s") must have currency preference but has none.', - $this->destinationAccount->id, - $this->destinationAccount->name - ); - Log::error($message); - $this->error($message); - - return true; - } - - return false; - } - - /** - * Is this a split transaction journal? - * - * @param TransactionJournal $transfer - * - * @return bool - * @codeCoverageIgnore - */ - private function isSplitJournal(TransactionJournal $transfer): bool - { - return $transfer->transactions->count() > 2; - } - /** * */ @@ -465,117 +578,4 @@ class TransferCurrenciesCorrections extends Command { app('fireflyconfig')->set(self::CONFIG_NAME, true); } - - /** - * Reset all the class fields for the current transfer. - * - * @codeCoverageIgnore - */ - private function resetInformation(): void - { - $this->sourceTransaction = null; - $this->sourceAccount = null; - $this->sourceCurrency = null; - $this->destinationTransaction = null; - $this->destinationAccount = null; - $this->destinationCurrency = null; - } - - /** - * This routine verifies that transfers have the correct currency settings for the accounts they are linked to. - * For transfers, this is can be a destructive routine since we FORCE them into a currency setting whether they - * like it or not. Previous routines MUST have set the currency setting for both accounts for this to work. - * - * Both source and destination must match the respective currency preference. So FF3 must verify ALL - * transactions. - */ - private function startUpdateRoutine(): void - { - $set = $this->cliRepos->getAllJournals([TransactionType::TRANSFER]); - /** @var TransactionJournal $journal */ - foreach ($set as $journal) { - $this->updateTransferCurrency($journal); - } - } - - /** - * Laravel will execute ALL __construct() methods for ALL commands whenever a SINGLE command is - * executed. This leads to noticeable slow-downs and class calls. To prevent this, this method should - * be called from the handle method instead of using the constructor to initialize the command. - * - * @codeCoverageIgnore - */ - private function stupidLaravel(): void - { - $this->count = 0; - $this->accountRepos = app(AccountRepositoryInterface::class); - $this->cliRepos = app(JournalCLIRepositoryInterface::class); - $this->accountCurrencies = []; - $this->resetInformation(); - } - - /** - * @param TransactionJournal $transfer - */ - private function updateTransferCurrency(TransactionJournal $transfer): void - { - $this->resetInformation(); - - // @codeCoverageIgnoreStart - if ($this->isSplitJournal($transfer)) { - $this->line(sprintf(sprintf('Transaction journal #%d is a split journal. Cannot continue.', $transfer->id))); - - return; - } - // @codeCoverageIgnoreEnd - - $this->getSourceInformation($transfer); - $this->getDestinationInformation($transfer); - - // unexpectedly, either one is null: - // @codeCoverageIgnoreStart - if ($this->isEmptyTransactions()) { - $this->error(sprintf('Source or destination information for transaction journal #%d is null. Cannot fix this one.', $transfer->id)); - - return; - } - // @codeCoverageIgnoreEnd - - - // both accounts must have currency preference: - // @codeCoverageIgnoreStart - if ($this->isNoCurrencyPresent()) { - $this->error( - sprintf('Source or destination accounts for transaction journal #%d have no currency information. Cannot fix this one.', $transfer->id) - ); - - return; - } - // @codeCoverageIgnoreEnd - - // fix source transaction having no currency. - $this->fixSourceNoCurrency(); - - // fix source transaction having bad currency. - $this->fixSourceUnmatchedCurrency(); - - // fix destination transaction having no currency. - $this->fixDestNoCurrency(); - - // fix destination transaction having bad currency. - $this->fixDestinationUnmatchedCurrency(); - - // remove foreign currency information if not necessary. - $this->fixInvalidForeignCurrency(); - // correct foreign currency info if necessary. - $this->fixMismatchedForeignCurrency(); - - // restore missing foreign currency amount. - $this->fixSourceNullForeignAmount(); - - $this->fixDestNullForeignAmount(); - - // fix journal itself: - $this->fixTransactionJournalCurrency($transfer); - } } diff --git a/app/Console/Commands/Upgrade/UpgradeDatabase.php b/app/Console/Commands/Upgrade/UpgradeDatabase.php index 0167595dad..ca3d4ca158 100644 --- a/app/Console/Commands/Upgrade/UpgradeDatabase.php +++ b/app/Console/Commands/Upgrade/UpgradeDatabase.php @@ -76,6 +76,7 @@ class UpgradeDatabase extends Command 'firefly-iii:rename-account-meta', 'firefly-iii:migrate-recurrence-meta', 'firefly-iii:migrate-tag-locations', + 'firefly-iii:migrate-recurrence-type', // there are 16 verify commands. 'firefly-iii:fix-piggies', @@ -117,9 +118,9 @@ class UpgradeDatabase extends Command echo $result; } // set new DB version. - app('fireflyconfig')->set('db_version', (int) config('firefly.db_version')); + app('fireflyconfig')->set('db_version', (int)config('firefly.db_version')); // index will set FF3 version. - app('fireflyconfig')->set('ff3_version', (string) config('firefly.version')); + app('fireflyconfig')->set('ff3_version', (string)config('firefly.version')); return 0; } diff --git a/app/Events/DetectedNewIPAddress.php b/app/Events/DetectedNewIPAddress.php index 3e5ecb495b..b381200a83 100644 --- a/app/Events/DetectedNewIPAddress.php +++ b/app/Events/DetectedNewIPAddress.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * DetectedNewIPAddress.php diff --git a/app/Events/RequestedSendWebhookMessages.php b/app/Events/RequestedSendWebhookMessages.php index fd9b132688..15ac7b55c2 100644 --- a/app/Events/RequestedSendWebhookMessages.php +++ b/app/Events/RequestedSendWebhookMessages.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * RequestedSendWebhookMessages.php diff --git a/app/Events/StoredTransactionLink.php b/app/Events/StoredTransactionLink.php index c15a335178..f97f22799b 100644 --- a/app/Events/StoredTransactionLink.php +++ b/app/Events/StoredTransactionLink.php @@ -28,6 +28,7 @@ use Illuminate\Queue\SerializesModels; /** * Class StoredTransactionLink + * TODO not used. */ class StoredTransactionLink extends Event { diff --git a/app/Events/UpdatedTransactionLink.php b/app/Events/UpdatedTransactionLink.php index 58a1f1592b..386ea2041c 100644 --- a/app/Events/UpdatedTransactionLink.php +++ b/app/Events/UpdatedTransactionLink.php @@ -28,6 +28,7 @@ use Illuminate\Queue\SerializesModels; /** * Class UpdatedTransactionLink + * TODO unused */ class UpdatedTransactionLink extends Event { diff --git a/app/Exceptions/GracefulNotFoundHandler.php b/app/Exceptions/GracefulNotFoundHandler.php index b46b0585f4..91df0dfd59 100644 --- a/app/Exceptions/GracefulNotFoundHandler.php +++ b/app/Exceptions/GracefulNotFoundHandler.php @@ -102,19 +102,17 @@ class GracefulNotFoundHandler extends ExceptionHandler $request->session()->reflash(); return redirect(route('piggy-banks.index')); - break; case 'recurring.show': + case 'recurring.edit': $request->session()->reflash(); return redirect(route('recurring.index')); - break; case 'tags.show.all': case 'tags.show': case 'tags.edit': $request->session()->reflash(); return redirect(route('tags.index')); - break; case 'categories.show': case 'categories.show.all': $request->session()->reflash(); diff --git a/app/Factory/AccountFactory.php b/app/Factory/AccountFactory.php index 28f9620d84..833cfe9658 100644 --- a/app/Factory/AccountFactory.php +++ b/app/Factory/AccountFactory.php @@ -30,6 +30,7 @@ use FireflyIII\Models\AccountType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Services\Internal\Support\AccountServiceTrait; use FireflyIII\Services\Internal\Support\LocationServiceTrait; +use FireflyIII\Services\Internal\Update\AccountUpdateService; use FireflyIII\User; use Log; @@ -42,12 +43,12 @@ class AccountFactory { use AccountServiceTrait, LocationServiceTrait; - protected AccountRepositoryInterface $accountRepository; - protected array $validAssetFields; - protected array $validCCFields; - protected array $validFields; - private array $canHaveVirtual; - private User $user; + protected AccountRepositoryInterface $accountRepository; + protected array $validAssetFields; + protected array $validCCFields; + protected array $validFields; + private array $canHaveVirtual; + private User $user; /** * AccountFactory constructor. @@ -73,9 +74,10 @@ class AccountFactory public function create(array $data): Account { $type = $this->getAccountType($data['account_type_id'] ?? null, $data['account_type'] ?? null); - if (null === $type) { - throw new FireflyException(sprintf('AccountFactory::create() was unable to find account type #%d ("%s").', $data['account_type_id'] ?? null, $data['account_type'] ?? null)); + throw new FireflyException( + sprintf('AccountFactory::create() was unable to find account type #%d ("%s").', $data['account_type_id'] ?? null, $data['account_type'] ?? null) + ); } $data['iban'] = $this->filterIban($data['iban'] ?? null); @@ -85,8 +87,14 @@ class AccountFactory $return = $this->find($data['name'], $type->type); if (null === $return) { + $this->accountRepository->resetAccountOrder(); + // create it: - $databaseData = ['user_id' => $this->user->id, 'account_type_id' => $type->id, 'name' => $data['name'], 'order' => $data['order'] ?? 0, 'virtual_balance' => $data['virtual_balance'] ?? null, 'active' => true === $data['active'], 'iban' => $data['iban'],]; + $databaseData = ['user_id' => $this->user->id, + 'account_type_id' => $type->id, + 'name' => $data['name'], + 'order' => 25000, + 'virtual_balance' => $data['virtual_balance'] ?? null, 'active' => true === $data['active'], 'iban' => $data['iban'],]; $currency = $this->getCurrency((int)($data['currency_id'] ?? null), (string)($data['currency_code'] ?? null)); unset($data['currency_code']); @@ -118,6 +126,25 @@ class AccountFactory // store location $this->storeNewLocation($return, $data); + + // update order to be correct: + + // set new order: + $maxOrder = $this->accountRepository->maxOrder($type->type); + $order = null; + if (!array_key_exists('order', $data)) { + // take maxOrder + 1 + $order = $maxOrder + 1; + } + if (array_key_exists('order', $data)) { + // limit order + $order = (int)($data['order'] > $maxOrder ? $maxOrder + 1 : $data['order']); + $order = 0 === $order ? $maxOrder + 1 : $order; + } + $updateService = app(AccountUpdateService::class); + $updateService->setUser($return->user); + Log::debug(sprintf('Will set order to %d', $order)); + $return = $updateService->update($return, ['order' => $order]); } return $return; @@ -152,7 +179,10 @@ class AccountFactory if (null === $return) { Log::debug('Found nothing. Will create a new one.'); - $return = $this->create(['user_id' => $this->user->id, 'name' => $accountName, 'account_type_id' => $type->id, 'account_type' => null, 'virtual_balance' => '0', 'iban' => null, 'active' => true,]); + $return = $this->create( + ['user_id' => $this->user->id, 'name' => $accountName, 'account_type_id' => $type->id, 'account_type' => null, 'virtual_balance' => '0', + 'iban' => null, 'active' => true,] + ); } return $return; diff --git a/app/Factory/AttachmentFactory.php b/app/Factory/AttachmentFactory.php index 14b7bd7ea0..98d87967de 100644 --- a/app/Factory/AttachmentFactory.php +++ b/app/Factory/AttachmentFactory.php @@ -46,16 +46,16 @@ class AttachmentFactory public function create(array $data): ?Attachment { // append if necessary. - $model = false === strpos($data['model'], 'FireflyIII') ? sprintf('FireflyIII\\Models\\%s', $data['model']) : $data['model']; + $model = false === strpos($data['attachable_type'], 'FireflyIII') ? sprintf('FireflyIII\\Models\\%s', $data['attachable_type']) : $data['attachable_type']; // get journal instead of transaction. if (Transaction::class === $model) { /** @var Transaction $transaction */ - $transaction = $this->user->transactions()->find((int) $data['model_id']); + $transaction = $this->user->transactions()->find((int) $data['attachable_id']); if (null === $transaction) { throw new FireflyException('Unexpectedly could not find transaction'); // @codeCoverageIgnore } - $data['model_id'] = $transaction->transaction_journal_id; + $data['attachable_id'] = $transaction->transaction_journal_id; $model = TransactionJournal::class; } @@ -63,7 +63,7 @@ class AttachmentFactory $attachment = Attachment::create( [ 'user_id' => $this->user->id, - 'attachable_id' => $data['model_id'], + 'attachable_id' => $data['attachable_id'], 'attachable_type' => $model, 'md5' => '', 'filename' => $data['filename'], diff --git a/app/Factory/BillFactory.php b/app/Factory/BillFactory.php index d68c68b68d..88c2ad614e 100644 --- a/app/Factory/BillFactory.php +++ b/app/Factory/BillFactory.php @@ -26,7 +26,6 @@ namespace FireflyIII\Factory; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Bill; -use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups; use FireflyIII\Services\Internal\Support\BillServiceTrait; use FireflyIII\User; @@ -50,15 +49,14 @@ class BillFactory */ public function create(array $data): ?Bill { - /** @var TransactionCurrencyFactory $factory */ - $factory = app(TransactionCurrencyFactory::class); - /** @var TransactionCurrency $currency */ - $currency = $factory->find((int)($data['currency_id'] ?? null), (string)($data['currency_code'] ?? null)); + Log::debug(sprintf('Now in %s', __METHOD__), $data); + $factory = app(TransactionCurrencyFactory::class); + $currency = $factory->find((int)($data['currency_id'] ?? null), (string)($data['currency_code'] ?? null)) ?? + app('amount')->getDefaultCurrencyByUser($this->user); - if (null === $currency) { - $currency = app('amount')->getDefaultCurrencyByUser($this->user); - } try { + $skip = array_key_exists('skip', $data) ? $data['skip'] : 0; + $active = array_key_exists('active', $data) ? $data['active'] : 0; /** @var Bill $bill */ $bill = Bill::create( [ @@ -70,9 +68,9 @@ class BillFactory 'amount_max' => $data['amount_max'], 'date' => $data['date'], 'repeat_freq' => $data['repeat_freq'], - 'skip' => $data['skip'], + 'skip' => $skip, 'automatch' => true, - 'active' => $data['active'] ?? true, + 'active' => $active, ] ); } catch (QueryException $e) { @@ -84,8 +82,7 @@ class BillFactory if (array_key_exists('notes', $data)) { $this->updateNote($bill, (string)$data['notes']); } - - $objectGroupTitle = $data['object_group'] ?? ''; + $objectGroupTitle = $data['object_group_title'] ?? ''; if ('' !== $objectGroupTitle) { $objectGroup = $this->findOrCreateObjectGroup($objectGroupTitle); if (null !== $objectGroup) { diff --git a/app/Factory/RecurrenceFactory.php b/app/Factory/RecurrenceFactory.php index 6549a23d6a..4ce667d307 100644 --- a/app/Factory/RecurrenceFactory.php +++ b/app/Factory/RecurrenceFactory.php @@ -41,8 +41,9 @@ class RecurrenceFactory { use TransactionTypeTrait, RecurringTransactionTrait; + private MessageBag $errors; - private User $user; + private User $user; /** @@ -58,8 +59,8 @@ class RecurrenceFactory /** * @param array $data * - * @throws FireflyException * @return Recurrence + * @throws FireflyException */ public function create(array $data): Recurrence { @@ -72,26 +73,60 @@ class RecurrenceFactory throw new FireflyException($message); } - /** @var Carbon $firstDate */ - $firstDate = $data['recurrence']['first_date']; + $firstDate = null; + $repeatUntil = null; + $repetitions = 0; + $title = null; + $description = ''; + $applyRules = true; + $active = true; + if (array_key_exists('first_date', $data['recurrence'])) { + /** @var Carbon $firstDate */ + $firstDate = $data['recurrence']['first_date']; + } + if (array_key_exists('nr_of_repetitions', $data['recurrence'])) { + $repetitions = (int)$data['recurrence']['nr_of_repetitions']; + } + if (array_key_exists('repeat_until', $data['recurrence'])) { + $repeatUntil = $data['recurrence']['repeat_until']; + } + if (array_key_exists('title', $data['recurrence'])) { + $title = $data['recurrence']['title']; + } + if (array_key_exists('description', $data['recurrence'])) { + $description = $data['recurrence']['description']; + } + if (array_key_exists('apply_rules', $data['recurrence'])) { + $applyRules = $data['recurrence']['apply_rules']; + } + if (array_key_exists('active', $data['recurrence'])) { + $active = $data['recurrence']['active']; + } + if ($repetitions > 0 && null === $repeatUntil) { + $repeatUntil = Carbon::create()->addyear(); + } - $repetitions = (int) $data['recurrence']['repetitions']; - $recurrence = new Recurrence( + $recurrence = new Recurrence( [ 'user_id' => $this->user->id, 'transaction_type_id' => $type->id, - 'title' => $data['recurrence']['title'], - 'description' => $data['recurrence']['description'], - 'first_date' => $firstDate->format('Y-m-d'), - 'repeat_until' => $repetitions > 0 ? null : $data['recurrence']['repeat_until'], + 'title' => $title, + 'description' => $description, + 'first_date' => $firstDate ? $firstDate->format('Y-m-d') : null, + 'repeat_until' => $repetitions > 0 ? null : $repeatUntil->format('Y-m-d'), 'latest_date' => null, - 'repetitions' => $data['recurrence']['repetitions'], - 'apply_rules' => $data['recurrence']['apply_rules'], - 'active' => $data['recurrence']['active'], + 'repetitions' => $repetitions, + 'apply_rules' => $applyRules, + 'active' => $active, ] ); $recurrence->save(); + if (array_key_exists('notes', $data['recurrence'])) { + $this->updateNote($recurrence, (string)$data['recurrence']['notes']); + + } + $this->createRepetitions($recurrence, $data['repetitions'] ?? []); try { $this->createTransactions($recurrence, $data['transactions'] ?? []); diff --git a/app/Generator/Report/Account/MonthReportGenerator.php b/app/Generator/Report/Account/MonthReportGenerator.php index e8af125c61..76ba7b89d9 100644 --- a/app/Generator/Report/Account/MonthReportGenerator.php +++ b/app/Generator/Report/Account/MonthReportGenerator.php @@ -52,7 +52,7 @@ class MonthReportGenerator implements ReportGeneratorInterface $reportType = 'account'; $preferredPeriod = $this->preferredPeriod(); try { - $result = view('reports.double.report', compact('accountIds', 'reportType', 'doubleIds', 'preferredPeriod')) + $result = prefixView('reports.double.report', compact('accountIds', 'reportType', 'doubleIds', 'preferredPeriod')) ->with('start', $this->start)->with('end', $this->end) ->with('doubles', $this->expense) ->render(); diff --git a/app/Generator/Report/Audit/MonthReportGenerator.php b/app/Generator/Report/Audit/MonthReportGenerator.php index 4414f5718c..d67e8af911 100644 --- a/app/Generator/Report/Audit/MonthReportGenerator.php +++ b/app/Generator/Report/Audit/MonthReportGenerator.php @@ -79,7 +79,7 @@ class MonthReportGenerator implements ReportGeneratorInterface 'due_date', 'payment_date', 'invoice_date', ]; try { - $result = view('reports.audit.report', compact('reportType', 'accountIds', 'auditData', 'hideable', 'defaultShow')) + $result = prefixView('reports.audit.report', compact('reportType', 'accountIds', 'auditData', 'hideable', 'defaultShow')) ->with('start', $this->start)->with('end', $this->end)->with('accounts', $this->accounts) ->render(); } catch (Throwable $e) { diff --git a/app/Generator/Report/Budget/MonthReportGenerator.php b/app/Generator/Report/Budget/MonthReportGenerator.php index e0f43f9dfe..9e4bc6bcbd 100644 --- a/app/Generator/Report/Budget/MonthReportGenerator.php +++ b/app/Generator/Report/Budget/MonthReportGenerator.php @@ -65,7 +65,7 @@ class MonthReportGenerator implements ReportGeneratorInterface $accountIds = implode(',', $this->accounts->pluck('id')->toArray()); $budgetIds = implode(',', $this->budgets->pluck('id')->toArray()); try { - $result = view( + $result = prefixView( 'reports.budget.month', compact('accountIds', 'budgetIds') ) diff --git a/app/Generator/Report/Standard/YearReportGenerator.php b/app/Generator/Report/Standard/YearReportGenerator.php index 587eb741f4..1e80fe6026 100644 --- a/app/Generator/Report/Standard/YearReportGenerator.php +++ b/app/Generator/Report/Standard/YearReportGenerator.php @@ -54,7 +54,7 @@ class YearReportGenerator implements ReportGeneratorInterface $reportType = 'default'; try { - $result = view( + $result = prefixView( 'reports.default.year', compact('accountIds', 'reportType') )->with('start', $this->start)->with('end', $this->end)->render(); diff --git a/app/Generator/Report/Tag/MonthReportGenerator.php b/app/Generator/Report/Tag/MonthReportGenerator.php index c7b6b32a60..a6d6c67b9f 100644 --- a/app/Generator/Report/Tag/MonthReportGenerator.php +++ b/app/Generator/Report/Tag/MonthReportGenerator.php @@ -75,7 +75,7 @@ class MonthReportGenerator implements ReportGeneratorInterface // render! try { - $result = view( + $result = prefixView( 'reports.tag.month', compact('accountIds', 'reportType', 'tagIds') )->with('start', $this->start)->with('end', $this->end)->with('tags', $this->tags)->with('accounts', $this->accounts)->render(); diff --git a/app/Generator/Webhook/MessageGeneratorInterface.php b/app/Generator/Webhook/MessageGeneratorInterface.php index 8487be5833..d86490563d 100644 --- a/app/Generator/Webhook/MessageGeneratorInterface.php +++ b/app/Generator/Webhook/MessageGeneratorInterface.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * MessageGeneratorInterface.php diff --git a/app/Generator/Webhook/StandardMessageGenerator.php b/app/Generator/Webhook/StandardMessageGenerator.php index 4031a5b705..90174354bc 100644 --- a/app/Generator/Webhook/StandardMessageGenerator.php +++ b/app/Generator/Webhook/StandardMessageGenerator.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * WebhookMessageGenerator.php diff --git a/app/Handlers/Events/DestroyedGroupEventHandler.php b/app/Handlers/Events/DestroyedGroupEventHandler.php index 7d5cd667ef..74d58492c6 100644 --- a/app/Handlers/Events/DestroyedGroupEventHandler.php +++ b/app/Handlers/Events/DestroyedGroupEventHandler.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * DestroyedGroupEventHandler.php diff --git a/app/Handlers/Events/StoredGroupEventHandler.php b/app/Handlers/Events/StoredGroupEventHandler.php index 1cb6dc1ef1..751a0422bb 100644 --- a/app/Handlers/Events/StoredGroupEventHandler.php +++ b/app/Handlers/Events/StoredGroupEventHandler.php @@ -62,14 +62,12 @@ class StoredGroupEventHandler Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds)); // collect rules: - $ruleRepository = app(RuleRepositoryInterface::class); $ruleGroupRepository = app(RuleGroupRepositoryInterface::class); - $ruleRepository->setUser($storedGroupEvent->transactionGroup->user); $ruleGroupRepository->setUser($storedGroupEvent->transactionGroup->user); // add the groups to the rule engine. // it should run the rules in the group and cancel the group if necessary. - $groups = $ruleGroupRepository->getRuleGroupsWithRules(); + $groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal'); // create and fire rule engine. $newRuleEngine = app(RuleEngineInterface::class); diff --git a/app/Handlers/Events/UpdatedGroupEventHandler.php b/app/Handlers/Events/UpdatedGroupEventHandler.php index f5b027c2e5..fc281330d9 100644 --- a/app/Handlers/Events/UpdatedGroupEventHandler.php +++ b/app/Handlers/Events/UpdatedGroupEventHandler.php @@ -31,6 +31,7 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Models\Webhook; use FireflyIII\Repositories\Rule\RuleRepositoryInterface; +use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use FireflyIII\TransactionRules\Engine\RuleEngineInterface; use Illuminate\Support\Collection; use Log; @@ -103,15 +104,16 @@ class UpdatedGroupEventHandler Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds)); // collect rules: - $ruleRepository = app(RuleRepositoryInterface::class); - $ruleRepository->setUser($updatedGroupEvent->transactionGroup->user); - $rules = $ruleRepository->getUpdateRules(); + $ruleGroupRepository = app(RuleGroupRepositoryInterface::class); + $ruleGroupRepository->setUser($updatedGroupEvent->transactionGroup->user); + + $groups = $ruleGroupRepository->getRuleGroupsWithRules('update-journal'); // file rule engine. $newRuleEngine = app(RuleEngineInterface::class); $newRuleEngine->setUser($updatedGroupEvent->transactionGroup->user); $newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]); - $newRuleEngine->setRules($rules); + $newRuleEngine->setRuleGroups($groups); $newRuleEngine->fire(); } diff --git a/app/Handlers/Events/WebhookEventHandler.php b/app/Handlers/Events/WebhookEventHandler.php index f6460dac1f..cb6a4ea66e 100644 --- a/app/Handlers/Events/WebhookEventHandler.php +++ b/app/Handlers/Events/WebhookEventHandler.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * WebhookEventHandler.php @@ -40,13 +61,13 @@ class WebhookEventHandler // kick off the job! $messages = WebhookMessage ::where('webhook_messages.sent', 0) - ->where('webhook_messages.errored', 0) + //->where('webhook_messages.errored', 0) ->get(['webhook_messages.*']) ->filter( function (WebhookMessage $message) { return $message->webhookAttempts()->count() <= 2; } - )->splice(0, 3); + )->splice(0, 5); Log::debug(sprintf('Found %d webhook message(s) ready to be send.', $messages->count())); foreach ($messages as $message) { SendWebhookMessage::dispatch($message)->afterResponse(); diff --git a/app/Helpers/Collector/Extensions/AccountCollection.php b/app/Helpers/Collector/Extensions/AccountCollection.php index 8dc522726e..0b4f32361d 100644 --- a/app/Helpers/Collector/Extensions/AccountCollection.php +++ b/app/Helpers/Collector/Extensions/AccountCollection.php @@ -91,7 +91,7 @@ trait AccountCollection $query->orWhereIn('destination.account_id', $accountIds); } ); - app('log')->debug(sprintf('GroupCollector: setAccounts: %s', implode(', ', $accountIds))); + //app('log')->debug(sprintf('GroupCollector: setAccounts: %s', implode(', ', $accountIds))); } return $this; diff --git a/app/Helpers/Collector/Extensions/MetaCollection.php b/app/Helpers/Collector/Extensions/MetaCollection.php index fb09f619b3..c176fc2687 100644 --- a/app/Helpers/Collector/Extensions/MetaCollection.php +++ b/app/Helpers/Collector/Extensions/MetaCollection.php @@ -369,6 +369,18 @@ trait MetaCollection return $this; } + /** + * Limit results to a transactions without a bill. + * + * @return GroupCollectorInterface + */ + public function withoutBill(): GroupCollectorInterface + { + $this->query->whereNull('transaction_journals.bill_id'); + + return $this; + } + /** * Limit results to a transactions without a budget.. * diff --git a/app/Helpers/Collector/GroupCollector.php b/app/Helpers/Collector/GroupCollector.php index 3fa7a843a7..f2339850da 100644 --- a/app/Helpers/Collector/GroupCollector.php +++ b/app/Helpers/Collector/GroupCollector.php @@ -741,7 +741,7 @@ class GroupCollector implements GroupCollectorInterface */ private function startQuery(): void { - app('log')->debug('GroupCollector::startQuery'); + //app('log')->debug('GroupCollector::startQuery'); $this->query = $this->user //->transactionGroups() //->leftJoin('transaction_journals', 'transaction_journals.transaction_group_id', 'transaction_groups.id') diff --git a/app/Helpers/Collector/GroupCollectorInterface.php b/app/Helpers/Collector/GroupCollectorInterface.php index b0dbe45856..43493c59ed 100644 --- a/app/Helpers/Collector/GroupCollectorInterface.php +++ b/app/Helpers/Collector/GroupCollectorInterface.php @@ -501,6 +501,13 @@ interface GroupCollectorInterface */ public function withoutBudget(): GroupCollectorInterface; + /** + * Limit results to a transactions without a bill. + * + * @return GroupCollectorInterface + */ + public function withoutBill(): GroupCollectorInterface; + /** * Limit results to a transactions without a category. * diff --git a/app/Helpers/Webhook/Sha3SignatureGenerator.php b/app/Helpers/Webhook/Sha3SignatureGenerator.php index 43d474622d..8674a2b3bb 100644 --- a/app/Helpers/Webhook/Sha3SignatureGenerator.php +++ b/app/Helpers/Webhook/Sha3SignatureGenerator.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * Sha3SignatureGenerator.php diff --git a/app/Helpers/Webhook/SignatureGeneratorInterface.php b/app/Helpers/Webhook/SignatureGeneratorInterface.php index e4af7c0e65..1c4506c377 100644 --- a/app/Helpers/Webhook/SignatureGeneratorInterface.php +++ b/app/Helpers/Webhook/SignatureGeneratorInterface.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * SignatureGeneratorInterface.php diff --git a/app/Http/Controllers/Account/CreateController.php b/app/Http/Controllers/Account/CreateController.php index ef9c1ba411..fa0610fd7f 100644 --- a/app/Http/Controllers/Account/CreateController.php +++ b/app/Http/Controllers/Account/CreateController.php @@ -122,7 +122,7 @@ class CreateController extends Controller $request->session()->forget('accounts.create.fromStore'); Log::channel('audit')->info('Creating new account.'); - return view('accounts.create', compact('subTitleIcon', 'locations', 'objectType', 'interestPeriods', 'subTitle', 'roles', 'liabilityTypes')); + return prefixView('accounts.create', compact('subTitleIcon', 'locations', 'objectType', 'interestPeriods', 'subTitle', 'roles', 'liabilityTypes')); } /** diff --git a/app/Http/Controllers/Account/DeleteController.php b/app/Http/Controllers/Account/DeleteController.php index 96d99582f6..6e37dbd1f8 100644 --- a/app/Http/Controllers/Account/DeleteController.php +++ b/app/Http/Controllers/Account/DeleteController.php @@ -88,7 +88,7 @@ class DeleteController extends Controller // put previous url in session $this->rememberPreviousUri('accounts.delete.uri'); - return view('accounts.delete', compact('account', 'subTitle', 'accountList', 'objectType')); + return prefixView('accounts.delete', compact('account', 'subTitle', 'accountList', 'objectType')); } /** diff --git a/app/Http/Controllers/Account/EditController.php b/app/Http/Controllers/Account/EditController.php index 85655d0ed1..1d45e1a99e 100644 --- a/app/Http/Controllers/Account/EditController.php +++ b/app/Http/Controllers/Account/EditController.php @@ -150,7 +150,7 @@ class EditController extends Controller $request->session()->flash('preFilled', $preFilled); - return view( + return prefixView( 'accounts.edit', compact( 'account', diff --git a/app/Http/Controllers/Account/IndexController.php b/app/Http/Controllers/Account/IndexController.php index 79061b6a99..eb7dff2ca9 100644 --- a/app/Http/Controllers/Account/IndexController.php +++ b/app/Http/Controllers/Account/IndexController.php @@ -113,7 +113,7 @@ class IndexController extends Controller $accounts = new LengthAwarePaginator($accounts, $total, $pageSize, $page); $accounts->setPath(route('accounts.inactive.index', [$objectType])); - return view('accounts.index', compact('objectType', 'inactivePage', 'subTitleIcon', 'subTitle', 'page', 'accounts')); + return prefixView('accounts.index', compact('objectType', 'inactivePage', 'subTitleIcon', 'subTitle', 'page', 'accounts')); } @@ -136,7 +136,7 @@ class IndexController extends Controller if (1 === random_int(0, 20)) { Log::debug('Will reset order.'); - $this->repository->resetAccountOrder($types); + $this->repository->resetAccountOrder(); } $collection = $this->repository->getActiveAccountsByType($types); @@ -182,7 +182,7 @@ class IndexController extends Controller Log::debug(sprintf('Count of accounts after LAP (1): %d', $accounts->count())); Log::debug(sprintf('Count of accounts after LAP (2): %d', $accounts->getCollection()->count())); - return view('accounts.index', compact('objectType', 'inactiveCount', 'subTitleIcon', 'subTitle', 'page', 'accounts')); + return prefixView('accounts.index', compact('objectType', 'inactiveCount', 'subTitleIcon', 'subTitle', 'page', 'accounts')); } diff --git a/app/Http/Controllers/Account/ReconcileController.php b/app/Http/Controllers/Account/ReconcileController.php index a7665f037d..647fff39ef 100644 --- a/app/Http/Controllers/Account/ReconcileController.php +++ b/app/Http/Controllers/Account/ReconcileController.php @@ -140,7 +140,7 @@ class ReconcileController extends Controller $indexUri = route('accounts.reconcile', [$account->id, '%start%', '%end%']); $objectType = 'asset'; - return view( + return prefixView( 'accounts.reconcile.index', compact( 'account', diff --git a/app/Http/Controllers/Account/ShowController.php b/app/Http/Controllers/Account/ShowController.php index b5bb85941d..0c22048ff8 100644 --- a/app/Http/Controllers/Account/ShowController.php +++ b/app/Http/Controllers/Account/ShowController.php @@ -130,7 +130,7 @@ class ShowController extends Controller $showAll = false; $balance = app('steam')->balance($account, $end); - return view( + return prefixView( 'accounts.show', compact( 'account', @@ -191,7 +191,7 @@ class ShowController extends Controller $showAll = true; $balance = app('steam')->balance($account, $end); - return view( + return prefixView( 'accounts.show', compact( 'account', diff --git a/app/Http/Controllers/Admin/ConfigurationController.php b/app/Http/Controllers/Admin/ConfigurationController.php index c965bd3a0a..8d5517bb05 100644 --- a/app/Http/Controllers/Admin/ConfigurationController.php +++ b/app/Http/Controllers/Admin/ConfigurationController.php @@ -74,7 +74,7 @@ class ConfigurationController extends Controller $isDemoSite = app('fireflyconfig')->get('is_demo_site', config('firefly.configuration.is_demo_site'))->data; $siteOwner = config('firefly.site_owner'); - return view( + return prefixView( 'admin.configuration.index', compact('subTitle', 'subTitleIcon', 'singleUserMode', 'isDemoSite', 'siteOwner') ); diff --git a/app/Http/Controllers/Admin/HomeController.php b/app/Http/Controllers/Admin/HomeController.php index 8e28465697..c29703af67 100644 --- a/app/Http/Controllers/Admin/HomeController.php +++ b/app/Http/Controllers/Admin/HomeController.php @@ -66,7 +66,7 @@ class HomeController extends Controller } Log::debug('Email is ', [$email]); - return view('admin.index', compact('title', 'mainTitleIcon','email')); + return prefixView('admin.index', compact('title', 'mainTitleIcon','email')); } /** diff --git a/app/Http/Controllers/Admin/LinkController.php b/app/Http/Controllers/Admin/LinkController.php index 95c7264c3c..e59d2a3b2c 100644 --- a/app/Http/Controllers/Admin/LinkController.php +++ b/app/Http/Controllers/Admin/LinkController.php @@ -80,7 +80,7 @@ class LinkController extends Controller $this->rememberPreviousUri('link-types.create.uri'); } - return view('admin.link.create', compact('subTitle', 'subTitleIcon')); + return prefixView('admin.link.create', compact('subTitle', 'subTitleIcon')); } /** @@ -116,7 +116,7 @@ class LinkController extends Controller // put previous url in session $this->rememberPreviousUri('link-types.delete.uri'); - return view('admin.link.delete', compact('linkType', 'subTitle', 'moveTo', 'count')); + return prefixView('admin.link.delete', compact('linkType', 'subTitle', 'moveTo', 'count')); } /** @@ -166,7 +166,7 @@ class LinkController extends Controller } $request->session()->forget('link-types.edit.fromUpdate'); - return view('admin.link.edit', compact('subTitle', 'subTitleIcon', 'linkType')); + return prefixView('admin.link.edit', compact('subTitle', 'subTitleIcon', 'linkType')); } /** @@ -187,7 +187,7 @@ class LinkController extends Controller } ); - return view('admin.link.index', compact('subTitle', 'subTitleIcon', 'linkTypes')); + return prefixView('admin.link.index', compact('subTitle', 'subTitleIcon', 'linkTypes')); } /** @@ -205,7 +205,7 @@ class LinkController extends Controller Log::channel('audit')->info(sprintf('User viewing link type #%d', $linkType->id)); - return view('admin.link.show', compact('subTitle', 'subTitleIcon', 'linkType', 'links')); + return prefixView('admin.link.show', compact('subTitle', 'subTitleIcon', 'linkType', 'links')); } /** diff --git a/app/Http/Controllers/Admin/TelemetryController.php b/app/Http/Controllers/Admin/TelemetryController.php index c18643d44f..8aa94a3721 100644 --- a/app/Http/Controllers/Admin/TelemetryController.php +++ b/app/Http/Controllers/Admin/TelemetryController.php @@ -106,7 +106,7 @@ class TelemetryController extends Controller $count = $this->repository->count(); - return view('admin.telemetry.index', compact('version', 'enabled', 'count')); + return prefixView('admin.telemetry.index', compact('version', 'enabled', 'count')); } /** @@ -120,6 +120,6 @@ class TelemetryController extends Controller $size = 100; $records = $this->repository->paginated($size); - return view('admin.telemetry.view', compact('records', 'format')); + return prefixView('admin.telemetry.view', compact('records', 'format')); } } diff --git a/app/Http/Controllers/Admin/UpdateController.php b/app/Http/Controllers/Admin/UpdateController.php index ac35327a2b..64d0fa3244 100644 --- a/app/Http/Controllers/Admin/UpdateController.php +++ b/app/Http/Controllers/Admin/UpdateController.php @@ -85,7 +85,7 @@ class UpdateController extends Controller 'alpha' => (string) trans('firefly.update_channel_alpha'), ]; - return view('admin.update.index', compact('subTitle', 'subTitleIcon', 'selected', 'options', 'channelSelected', 'channelOptions')); + return prefixView('admin.update.index', compact('subTitle', 'subTitleIcon', 'selected', 'options', 'channelSelected', 'channelOptions')); } /** diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 79b7016559..23d1e4e683 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -75,7 +75,7 @@ class UserController extends Controller $subTitle = (string) trans('firefly.delete_user', ['email' => $user->email]); - return view('admin.users.delete', compact('user', 'subTitle')); + return prefixView('admin.users.delete', compact('user', 'subTitle')); } /** @@ -128,7 +128,7 @@ class UserController extends Controller 'email_changed' => (string) trans('firefly.block_code_email_changed'), ]; - return view('admin.users.edit', compact('user', 'canEditDetails', 'subTitle', 'subTitleIcon', 'codes', 'currentUser', 'isAdmin')); + return prefixView('admin.users.edit', compact('user', 'canEditDetails', 'subTitle', 'subTitleIcon', 'codes', 'currentUser', 'isAdmin')); } /** @@ -150,7 +150,7 @@ class UserController extends Controller } ); - return view('admin.users.index', compact('subTitle', 'subTitleIcon', 'users')); + return prefixView('admin.users.index', compact('subTitle', 'subTitleIcon', 'users')); } /** @@ -168,7 +168,7 @@ class UserController extends Controller $subTitleIcon = 'fa-user'; $information = $this->repository->getUserData($user); - return view( + return prefixView( 'admin.users.show', compact( 'title', diff --git a/app/Http/Controllers/AttachmentController.php b/app/Http/Controllers/AttachmentController.php index 211be6430d..bcfda6f939 100644 --- a/app/Http/Controllers/AttachmentController.php +++ b/app/Http/Controllers/AttachmentController.php @@ -74,7 +74,7 @@ class AttachmentController extends Controller // put previous url in session $this->rememberPreviousUri('attachments.delete.uri'); - return view('attachments.delete', compact('attachment', 'subTitle')); + return prefixView('attachments.delete', compact('attachment', 'subTitle')); } /** @@ -153,7 +153,7 @@ class AttachmentController extends Controller ]; $request->session()->flash('preFilled', $preFilled); - return view('attachments.edit', compact('attachment', 'subTitleIcon', 'subTitle')); + return prefixView('attachments.edit', compact('attachment', 'subTitleIcon', 'subTitle')); } /** @@ -173,7 +173,7 @@ class AttachmentController extends Controller ); - return view('attachments.index', compact('set')); + return prefixView('attachments.index', compact('set')); } /** diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php index a4c399ebc8..9842a3eaa1 100644 --- a/app/Http/Controllers/Auth/ForgotPasswordController.php +++ b/app/Http/Controllers/Auth/ForgotPasswordController.php @@ -77,7 +77,7 @@ class ForgotPasswordController extends Controller $message = sprintf('Cannot reset password when authenticating over "%s".', $loginProvider); Log::error($message); - return view('error', compact('message')); + return prefixView('error', compact('message')); } // @codeCoverageIgnoreEnd @@ -118,7 +118,7 @@ class ForgotPasswordController extends Controller if ('eloquent' !== $loginProvider) { $message = sprintf('Cannot reset password when authenticating over "%s".', $loginProvider); - return view('error', compact('message')); + return prefixView('error', compact('message')); } // is allowed to? @@ -130,6 +130,6 @@ class ForgotPasswordController extends Controller $allowRegistration = false; } - return view('auth.passwords.email')->with(compact('allowRegistration', 'pageTitle')); + return prefixView('auth.passwords.email')->with(compact('allowRegistration', 'pageTitle')); } } diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 0b9f4eea05..a77d6a3a88 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -160,7 +160,7 @@ class LoginController extends Controller } - return view('auth.login', compact('allowRegistration', 'email', 'remember', 'allowReset', 'title')); + return prefixView('auth.login', compact('allowRegistration', 'email', 'remember', 'allowReset', 'title')); } /** diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php index 422e57073e..a7152d0b80 100644 --- a/app/Http/Controllers/Auth/RegisterController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -98,7 +98,7 @@ class RegisterController extends Controller if (false === $allowRegistration) { $message = 'Registration is currently not available.'; - return view('error', compact('message')); + return prefixView('error', compact('message')); } $this->validator($request->all())->validate(); @@ -149,12 +149,12 @@ class RegisterController extends Controller if (false === $allowRegistration) { $message = 'Registration is currently not available.'; - return view('error', compact('message')); + return prefixView('error', compact('message')); } $email = $request->old('email'); - return view('auth.register', compact('isDemoSite', 'email', 'pageTitle')); + return prefixView('auth.register', compact('isDemoSite', 'email', 'pageTitle')); } } diff --git a/app/Http/Controllers/Auth/ResetPasswordController.php b/app/Http/Controllers/Auth/ResetPasswordController.php index 53a95040f6..fd0b9c0f07 100644 --- a/app/Http/Controllers/Auth/ResetPasswordController.php +++ b/app/Http/Controllers/Auth/ResetPasswordController.php @@ -85,7 +85,7 @@ class ResetPasswordController extends Controller if ('eloquent' !== $loginProvider) { $message = sprintf('Cannot reset password when authenticating over "%s".', $loginProvider); - return view('error', compact('message')); + return prefixView('error', compact('message')); } $rules = [ @@ -130,7 +130,7 @@ class ResetPasswordController extends Controller if ('eloquent' !== $loginProvider) { $message = sprintf('Cannot reset password when authenticating over "%s".', $loginProvider); - return view('error', compact('message')); + return prefixView('error', compact('message')); } // is allowed to register? @@ -143,7 +143,7 @@ class ResetPasswordController extends Controller } /** @noinspection PhpUndefinedFieldInspection */ - return view('auth.passwords.reset')->with( + return prefixView('auth.passwords.reset')->with( ['token' => $token, 'email' => $request->email, 'allowRegistration' => $allowRegistration, 'pageTitle' => $pageTitle] ); } diff --git a/app/Http/Controllers/Auth/TwoFactorController.php b/app/Http/Controllers/Auth/TwoFactorController.php index a0ed675119..2bf8164b84 100644 --- a/app/Http/Controllers/Auth/TwoFactorController.php +++ b/app/Http/Controllers/Auth/TwoFactorController.php @@ -47,7 +47,7 @@ class TwoFactorController extends Controller $siteOwner = config('firefly.site_owner'); $title = (string) trans('firefly.two_factor_forgot_title'); - return view('auth.lost-two-factor', compact('user', 'siteOwner', 'title')); + return prefixView('auth.lost-two-factor', compact('user', 'siteOwner', 'title')); } /** diff --git a/app/Http/Controllers/Bill/CreateController.php b/app/Http/Controllers/Bill/CreateController.php index c9fd52b080..f39a91c7e7 100644 --- a/app/Http/Controllers/Bill/CreateController.php +++ b/app/Http/Controllers/Bill/CreateController.php @@ -86,7 +86,7 @@ class CreateController extends Controller } $request->session()->forget('bills.create.fromStore'); - return view('bills.create', compact('periods', 'subTitle', 'defaultCurrency')); + return prefixView('bills.create', compact('periods', 'subTitle', 'defaultCurrency')); } /** diff --git a/app/Http/Controllers/Bill/DeleteController.php b/app/Http/Controllers/Bill/DeleteController.php index 8c25043d9d..ee1ef297fb 100644 --- a/app/Http/Controllers/Bill/DeleteController.php +++ b/app/Http/Controllers/Bill/DeleteController.php @@ -75,7 +75,7 @@ class DeleteController extends Controller $this->rememberPreviousUri('bills.delete.uri'); $subTitle = (string) trans('firefly.delete_bill', ['name' => $bill->name]); - return view('bills.delete', compact('bill', 'subTitle')); + return prefixView('bills.delete', compact('bill', 'subTitle')); } /** diff --git a/app/Http/Controllers/Bill/EditController.php b/app/Http/Controllers/Bill/EditController.php index c817f40caf..691d9a7038 100644 --- a/app/Http/Controllers/Bill/EditController.php +++ b/app/Http/Controllers/Bill/EditController.php @@ -107,7 +107,7 @@ class EditController extends Controller $request->session()->flash('preFilled', $preFilled); $request->session()->forget('bills.edit.fromUpdate'); - return view('bills.edit', compact('subTitle', 'periods', 'rules', 'bill', 'defaultCurrency', 'preFilled')); + return prefixView('bills.edit', compact('subTitle', 'periods', 'rules', 'bill', 'defaultCurrency', 'preFilled')); } diff --git a/app/Http/Controllers/Bill/IndexController.php b/app/Http/Controllers/Bill/IndexController.php index ab80335a04..ab5c394c71 100644 --- a/app/Http/Controllers/Bill/IndexController.php +++ b/app/Http/Controllers/Bill/IndexController.php @@ -143,7 +143,7 @@ class IndexController extends Controller $sums = $this->getSums($bills); $totals = $this->getTotals($sums); - return view('bills.index', compact('bills', 'sums', 'total', 'totals')); + return prefixView('bills.index', compact('bills', 'sums', 'total', 'totals')); } @@ -211,8 +211,8 @@ class IndexController extends Controller 'monthly' => '12', 'weekly' => '52.17', ]; - $yearAmount = bcmul($avg, $multiplies[$bill['repeat_freq']]); - Log::debug(sprintf('Amount per year is %s (%s * %s)', $yearAmount, $avg, $multiplies[$bill['repeat_freq']])); + $yearAmount = bcmul($avg, bcdiv($multiplies[$bill['repeat_freq']], (string)($bill['skip'] + 1))); + Log::debug(sprintf('Amount per year is %s (%s * %s / %s)', $yearAmount, $avg, $multiplies[$bill['repeat_freq']], (string)($bill['skip'] + 1))); // per period: $division = [ diff --git a/app/Http/Controllers/Bill/ShowController.php b/app/Http/Controllers/Bill/ShowController.php index 8cee88df51..5197bb6cd4 100644 --- a/app/Http/Controllers/Bill/ShowController.php +++ b/app/Http/Controllers/Bill/ShowController.php @@ -184,7 +184,7 @@ class ShowController extends Controller // @codeCoverageIgnoreEnd - return view('bills.show', compact('attachments', 'groups', 'rules', 'yearAverage', 'overallAverage', 'year', 'object', 'bill', 'subTitle')); + return prefixView('bills.show', compact('attachments', 'groups', 'rules', 'yearAverage', 'overallAverage', 'year', 'object', 'bill', 'subTitle')); } } diff --git a/app/Http/Controllers/Budget/AvailableBudgetController.php b/app/Http/Controllers/Budget/AvailableBudgetController.php index cd00d2b196..72a6239d7a 100644 --- a/app/Http/Controllers/Budget/AvailableBudgetController.php +++ b/app/Http/Controllers/Budget/AvailableBudgetController.php @@ -97,7 +97,7 @@ class AvailableBudgetController extends Controller } $page = (int) ($request->get('page') ?? 1); - return view('budgets.available-budgets.create', compact('start', 'end', 'page', 'currency')); + return prefixView('budgets.available-budgets.create', compact('start', 'end', 'page', 'currency')); } /** @@ -131,7 +131,7 @@ class AvailableBudgetController extends Controller $page = (int) ($request->get('page') ?? 1); - return view('budgets.available-budgets.create-alternative', compact('start', 'end', 'page', 'currencies')); + return prefixView('budgets.available-budgets.create-alternative', compact('start', 'end', 'page', 'currencies')); } /** @@ -158,7 +158,7 @@ class AvailableBudgetController extends Controller public function edit(AvailableBudget $availableBudget, Carbon $start, Carbon $end) { $availableBudget->amount = number_format((float) $availableBudget->amount, $availableBudget->transactionCurrency->decimal_places, '.', ''); - return view('budgets.available-budgets.edit', compact('availableBudget', 'start', 'end')); + return prefixView('budgets.available-budgets.edit', compact('availableBudget', 'start', 'end')); } /** diff --git a/app/Http/Controllers/Budget/BudgetLimitController.php b/app/Http/Controllers/Budget/BudgetLimitController.php index 0647658859..3ace4acb71 100644 --- a/app/Http/Controllers/Budget/BudgetLimitController.php +++ b/app/Http/Controllers/Budget/BudgetLimitController.php @@ -103,7 +103,7 @@ class BudgetLimitController extends Controller } ); - return view('budgets.budget-limits.create', compact('start', 'end', 'currencies', 'budget')); + return prefixView('budgets.budget-limits.create', compact('start', 'end', 'currencies', 'budget')); } /** diff --git a/app/Http/Controllers/Budget/CreateController.php b/app/Http/Controllers/Budget/CreateController.php index d6ef0da956..d908b03ffb 100644 --- a/app/Http/Controllers/Budget/CreateController.php +++ b/app/Http/Controllers/Budget/CreateController.php @@ -106,7 +106,7 @@ class CreateController extends Controller $request->session()->forget('budgets.create.fromStore'); $subTitle = (string) trans('firefly.create_new_budget'); - return view('budgets.create', compact('subTitle', 'autoBudgetTypes', 'autoBudgetPeriods')); + return prefixView('budgets.create', compact('subTitle', 'autoBudgetTypes', 'autoBudgetPeriods')); } diff --git a/app/Http/Controllers/Budget/DeleteController.php b/app/Http/Controllers/Budget/DeleteController.php index 017719053b..d547ced8a0 100644 --- a/app/Http/Controllers/Budget/DeleteController.php +++ b/app/Http/Controllers/Budget/DeleteController.php @@ -77,7 +77,7 @@ class DeleteController extends Controller // put previous url in session $this->rememberPreviousUri('budgets.delete.uri'); - return view('budgets.delete', compact('budget', 'subTitle')); + return prefixView('budgets.delete', compact('budget', 'subTitle')); } /** diff --git a/app/Http/Controllers/Budget/EditController.php b/app/Http/Controllers/Budget/EditController.php index 5b0bb4b4ed..f0a728669d 100644 --- a/app/Http/Controllers/Budget/EditController.php +++ b/app/Http/Controllers/Budget/EditController.php @@ -114,7 +114,7 @@ class EditController extends Controller $request->session()->forget('budgets.edit.fromUpdate'); $request->session()->flash('preFilled', $preFilled); - return view('budgets.edit', compact('budget', 'subTitle', 'autoBudgetTypes', 'autoBudgetPeriods', 'autoBudget')); + return prefixView('budgets.edit', compact('budget', 'subTitle', 'autoBudgetTypes', 'autoBudgetPeriods', 'autoBudget')); } /** diff --git a/app/Http/Controllers/Budget/IndexController.php b/app/Http/Controllers/Budget/IndexController.php index b7de0793ec..8a90506f15 100644 --- a/app/Http/Controllers/Budget/IndexController.php +++ b/app/Http/Controllers/Budget/IndexController.php @@ -133,7 +133,7 @@ class IndexController extends Controller // get all inactive budgets, and simply list them: $inactive = $this->repository->getInactiveBudgets(); - return view( + return prefixView( 'budgets.index', compact( 'availableBudgets', 'budgeted', 'spent', 'prevLoop', 'nextLoop', 'budgets', 'currencies', 'enableAddButton', 'periodTitle', 'defaultCurrency', 'activeDaysPassed', 'activeDaysLeft', 'inactive', 'budgets', 'start', 'end', 'sums' diff --git a/app/Http/Controllers/Budget/ShowController.php b/app/Http/Controllers/Budget/ShowController.php index 45a3bdc178..589a60f9a0 100644 --- a/app/Http/Controllers/Budget/ShowController.php +++ b/app/Http/Controllers/Budget/ShowController.php @@ -46,11 +46,8 @@ use Illuminate\View\View; class ShowController extends Controller { use PeriodOverview, AugumentData; - /** @var JournalRepositoryInterface */ - private $journalRepos; - - /** @var BudgetRepositoryInterface */ - private $repository; + private JournalRepositoryInterface $journalRepos; + private BudgetRepositoryInterface $repository; /** * ShowController constructor. @@ -107,7 +104,7 @@ class ShowController extends Controller $groups = $collector->getPaginatedGroups(); $groups->setPath(route('budgets.no-budget')); - return view('budgets.no-budget', compact('groups', 'subTitle', 'periods', 'start', 'end')); + return prefixView('budgets.no-budget', compact('groups', 'subTitle', 'periods', 'start', 'end')); } /** @@ -134,7 +131,7 @@ class ShowController extends Controller $groups = $collector->getPaginatedGroups(); $groups->setPath(route('budgets.no-budget')); - return view('budgets.no-budget', compact('groups', 'subTitle', 'start', 'end')); + return prefixView('budgets.no-budget', compact('groups', 'subTitle', 'start', 'end')); } @@ -170,7 +167,7 @@ class ShowController extends Controller $subTitle = (string) trans('firefly.all_journals_for_budget', ['name' => $budget->name]); - return view('budgets.show', compact('limits','attachments', 'budget', 'repetition', 'groups', 'subTitle')); + return prefixView('budgets.show', compact('limits','attachments', 'budget', 'repetition', 'groups', 'subTitle')); } /** @@ -215,6 +212,6 @@ class ShowController extends Controller $attachments = $this->repository->getAttachments($budget); $limits = $this->getLimits($budget, $start, $end); - return view('budgets.show', compact('limits','attachments', 'budget', 'budgetLimit', 'groups', 'subTitle')); + return prefixView('budgets.show', compact('limits','attachments', 'budget', 'budgetLimit', 'groups', 'subTitle')); } } diff --git a/app/Http/Controllers/Category/CreateController.php b/app/Http/Controllers/Category/CreateController.php index cecb8c1da5..e96c30ae78 100644 --- a/app/Http/Controllers/Category/CreateController.php +++ b/app/Http/Controllers/Category/CreateController.php @@ -79,7 +79,7 @@ class CreateController extends Controller $request->session()->forget('categories.create.fromStore'); $subTitle = (string) trans('firefly.create_new_category'); - return view('categories.create', compact('subTitle')); + return prefixView('categories.create', compact('subTitle')); } diff --git a/app/Http/Controllers/Category/DeleteController.php b/app/Http/Controllers/Category/DeleteController.php index 59a2a19ecc..d6b406503d 100644 --- a/app/Http/Controllers/Category/DeleteController.php +++ b/app/Http/Controllers/Category/DeleteController.php @@ -75,7 +75,7 @@ class DeleteController extends Controller // put previous url in session $this->rememberPreviousUri('categories.delete.uri'); - return view('categories.delete', compact('category', 'subTitle')); + return prefixView('categories.delete', compact('category', 'subTitle')); } /** diff --git a/app/Http/Controllers/Category/EditController.php b/app/Http/Controllers/Category/EditController.php index 56d9d346fb..c1552813e8 100644 --- a/app/Http/Controllers/Category/EditController.php +++ b/app/Http/Controllers/Category/EditController.php @@ -88,7 +88,7 @@ class EditController extends Controller 'notes' => $request->old('notes') ?? $this->repository->getNoteText($category), ]; - return view('categories.edit', compact('category', 'subTitle', 'preFilled')); + return prefixView('categories.edit', compact('category', 'subTitle', 'preFilled')); } /** diff --git a/app/Http/Controllers/Category/IndexController.php b/app/Http/Controllers/Category/IndexController.php index 633bf11cd0..19c974cfdc 100644 --- a/app/Http/Controllers/Category/IndexController.php +++ b/app/Http/Controllers/Category/IndexController.php @@ -87,7 +87,7 @@ class IndexController extends Controller $categories = new LengthAwarePaginator($collection, $total, $pageSize, $page); $categories->setPath(route('categories.index')); - return view('categories.index', compact('categories')); + return prefixView('categories.index', compact('categories')); } } diff --git a/app/Http/Controllers/Category/NoCategoryController.php b/app/Http/Controllers/Category/NoCategoryController.php index d8af04fb2d..5d3461edf1 100644 --- a/app/Http/Controllers/Category/NoCategoryController.php +++ b/app/Http/Controllers/Category/NoCategoryController.php @@ -103,7 +103,7 @@ class NoCategoryController extends Controller $groups = $collector->getPaginatedGroups(); $groups->setPath(route('categories.no-category')); - return view('categories.no-category', compact('groups', 'subTitle', 'periods', 'start', 'end')); + return prefixView('categories.no-category', compact('groups', 'subTitle', 'periods', 'start', 'end')); } @@ -138,6 +138,6 @@ class NoCategoryController extends Controller $groups = $collector->getPaginatedGroups(); $groups->setPath(route('categories.no-category.all')); - return view('categories.no-category', compact('groups', 'subTitle', 'periods', 'start', 'end')); + return prefixView('categories.no-category', compact('groups', 'subTitle', 'periods', 'start', 'end')); } } diff --git a/app/Http/Controllers/Category/ShowController.php b/app/Http/Controllers/Category/ShowController.php index d26c36a093..4ba3d743b9 100644 --- a/app/Http/Controllers/Category/ShowController.php +++ b/app/Http/Controllers/Category/ShowController.php @@ -105,7 +105,7 @@ class ShowController extends Controller $groups = $collector->getPaginatedGroups(); $groups->setPath($path); - return view('categories.show', compact('category','attachments', 'groups', 'periods', 'subTitle', 'subTitleIcon', 'start', 'end')); + return prefixView('categories.show', compact('category','attachments', 'groups', 'periods', 'subTitle', 'subTitleIcon', 'start', 'end')); } /** @@ -143,6 +143,6 @@ class ShowController extends Controller $groups = $collector->getPaginatedGroups(); $groups->setPath($path); - return view('categories.show', compact('category','attachments', 'groups', 'periods', 'subTitle', 'subTitleIcon', 'start', 'end')); + return prefixView('categories.show', compact('category','attachments', 'groups', 'periods', 'subTitle', 'subTitleIcon', 'start', 'end')); } } diff --git a/app/Http/Controllers/CurrencyController.php b/app/Http/Controllers/CurrencyController.php index dfaf5f38ad..e800be82bf 100644 --- a/app/Http/Controllers/CurrencyController.php +++ b/app/Http/Controllers/CurrencyController.php @@ -97,7 +97,7 @@ class CurrencyController extends Controller Log::channel('audit')->info('Create new currency.'); - return view('currencies.create', compact('subTitleIcon', 'subTitle')); + return prefixView('currencies.create', compact('subTitleIcon', 'subTitle')); } /** @@ -156,7 +156,7 @@ class CurrencyController extends Controller $subTitle = (string) trans('form.delete_currency', ['name' => $currency->name]); Log::channel('audit')->info(sprintf('Visit page to delete currency %s.', $currency->code)); - return view('currencies.delete', compact('currency', 'subTitle')); + return prefixView('currencies.delete', compact('currency', 'subTitle')); } /** @@ -299,7 +299,7 @@ class CurrencyController extends Controller } $request->session()->forget('currencies.edit.fromUpdate'); - return view('currencies.edit', compact('currency', 'subTitle', 'subTitleIcon')); + return prefixView('currencies.edit', compact('currency', 'subTitle', 'subTitleIcon')); } /** @@ -344,7 +344,7 @@ class CurrencyController extends Controller $isOwner = false; } - return view('currencies.index', compact('currencies', 'defaultCurrency', 'isOwner')); + return prefixView('currencies.index', compact('currencies', 'defaultCurrency', 'isOwner')); } diff --git a/app/Http/Controllers/DebugController.php b/app/Http/Controllers/DebugController.php index d8396b1c5b..2be7f99e4b 100644 --- a/app/Http/Controllers/DebugController.php +++ b/app/Http/Controllers/DebugController.php @@ -189,7 +189,7 @@ class DebugController extends Controller $logContent = 'Truncated from this point <----|' . substr($logContent, -8192); } - return view( + return prefixView( 'debug', compact( 'phpVersion', diff --git a/app/Http/Controllers/Export/IndexController.php b/app/Http/Controllers/Export/IndexController.php index 60cd426ac1..30d472d95c 100644 --- a/app/Http/Controllers/Export/IndexController.php +++ b/app/Http/Controllers/Export/IndexController.php @@ -111,7 +111,7 @@ class IndexController extends Controller */ public function index() { - return view('export.index'); + return prefixView('export.index'); } } diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 4146d5972c..b15ec8b399 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -148,6 +148,6 @@ class HomeController extends Controller $user = auth()->user(); event(new RequestedVersionCheckStatus($user)); - return view('index', compact('count', 'subTitle', 'transactions', 'billCount', 'start', 'end', 'today')); + return prefixView('index', compact('count', 'subTitle', 'transactions', 'billCount', 'start', 'end', 'today')); } } diff --git a/app/Http/Controllers/JavascriptController.php b/app/Http/Controllers/JavascriptController.php index c73a82ddc6..eaef5d9ea3 100644 --- a/app/Http/Controllers/JavascriptController.php +++ b/app/Http/Controllers/JavascriptController.php @@ -69,7 +69,7 @@ class JavascriptController extends Controller } return response() - ->view('javascript.accounts', $data) + ->view('v1.javascript.accounts', $data) ->header('Content-Type', 'text/javascript'); } @@ -92,7 +92,7 @@ class JavascriptController extends Controller } return response() - ->view('javascript.currencies', $data) + ->view('v1.javascript.currencies', $data) ->header('Content-Type', 'text/javascript'); } @@ -116,7 +116,7 @@ class JavascriptController extends Controller ]; return response() - ->view('javascript.variables', $data) + ->view('v2.javascript.variables', $data) ->header('Content-Type', 'text/javascript'); } @@ -158,7 +158,7 @@ class JavascriptController extends Controller $request->session()->keep(['two-factor-secret']); return response() - ->view('javascript.variables', $data) + ->view('v1.javascript.variables', $data) ->header('Content-Type', 'text/javascript'); } diff --git a/app/Http/Controllers/Json/FrontpageController.php b/app/Http/Controllers/Json/FrontpageController.php index 6d69ccd1a0..bb79b73df8 100644 --- a/app/Http/Controllers/Json/FrontpageController.php +++ b/app/Http/Controllers/Json/FrontpageController.php @@ -66,7 +66,7 @@ class FrontpageController extends Controller $html = ''; if (!empty($info)) { try { - $html = view('json.piggy-banks', compact('info'))->render(); + $html = prefixView('json.piggy-banks', compact('info'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::error(sprintf('Cannot render json.piggy-banks: %s', $e->getMessage())); diff --git a/app/Http/Controllers/Json/ReconcileController.php b/app/Http/Controllers/Json/ReconcileController.php index 80e4029f09..948b5dc720 100644 --- a/app/Http/Controllers/Json/ReconcileController.php +++ b/app/Http/Controllers/Json/ReconcileController.php @@ -138,7 +138,7 @@ class ReconcileController extends Controller $reconSum = bcadd(bcadd($startBalance, $amount), $clearedAmount); try { - $view = view( + $view = prefixView( 'accounts.reconcile.overview', compact( 'account', @@ -212,7 +212,7 @@ class ReconcileController extends Controller $journals = $this->processTransactions($account, $array); try { - $html = view( + $html = prefixView( 'accounts.reconcile.transactions', compact('account', 'journals', 'currency', 'start', 'end', 'selectionStart', 'selectionEnd') )->render(); diff --git a/app/Http/Controllers/Json/RecurrenceController.php b/app/Http/Controllers/Json/RecurrenceController.php index cbf6aff8da..451c2a7169 100644 --- a/app/Http/Controllers/Json/RecurrenceController.php +++ b/app/Http/Controllers/Json/RecurrenceController.php @@ -37,8 +37,7 @@ use Log; */ class RecurrenceController extends Controller { - /** @var RecurringRepositoryInterface The recurring repository. */ - private $recurring; + private RecurringRepositoryInterface $recurring; /** * RecurrenceController constructor. @@ -97,12 +96,14 @@ class RecurrenceController extends Controller if ('yearly' === $repetitionType) { $repetitionMoment = explode(',', $request->get('type'))[1] ?? '2018-01-01'; } + $actualStart->startOfDay(); $repetition = new RecurrenceRepetition; $repetition->repetition_type = $repetitionType; $repetition->repetition_moment = $repetitionMoment; $repetition->repetition_skip = (int) $request->get('skip'); $repetition->weekend = (int) $request->get('weekend'); $actualEnd = clone $end; + switch ($endsAt) { default: case 'forever': diff --git a/app/Http/Controllers/Json/RuleController.php b/app/Http/Controllers/Json/RuleController.php index 125a4cc9e4..4d24d91fca 100644 --- a/app/Http/Controllers/Json/RuleController.php +++ b/app/Http/Controllers/Json/RuleController.php @@ -51,7 +51,7 @@ class RuleController extends Controller $actions[$key] = (string) trans('firefly.rule_action_' . $key . '_choice'); } try { - $view = view('rules.partials.action', compact('actions', 'count'))->render(); + $view = prefixView('rules.partials.action', compact('actions', 'count'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::error(sprintf('Cannot render rules.partials.action: %s', $e->getMessage())); @@ -84,7 +84,7 @@ class RuleController extends Controller asort($triggers); try { - $view = view('rules.partials.trigger', compact('triggers', 'count'))->render(); + $view = prefixView('rules.partials.trigger', compact('triggers', 'count'))->render(); } catch (Throwable $e) { Log::error(sprintf('Cannot render rules.partials.trigger: %s', $e->getMessage())); $view = 'Could not render view.'; diff --git a/app/Http/Controllers/NewUserController.php b/app/Http/Controllers/NewUserController.php index 9695a1b5dd..8d8af05fa4 100644 --- a/app/Http/Controllers/NewUserController.php +++ b/app/Http/Controllers/NewUserController.php @@ -74,7 +74,7 @@ class NewUserController extends Controller return redirect(route('index')); } - return view('new-user.index', compact('languages')); + return prefixView('new-user.index', compact('languages')); } /** diff --git a/app/Http/Controllers/ObjectGroup/DeleteController.php b/app/Http/Controllers/ObjectGroup/DeleteController.php index fcbd8e7962..1b298fb074 100644 --- a/app/Http/Controllers/ObjectGroup/DeleteController.php +++ b/app/Http/Controllers/ObjectGroup/DeleteController.php @@ -71,7 +71,7 @@ class DeleteController extends Controller // put previous url in session $this->rememberPreviousUri('object-groups.delete.uri'); - return view('object-groups.delete', compact('objectGroup', 'subTitle', 'piggyBanks')); + return prefixView('object-groups.delete', compact('objectGroup', 'subTitle', 'piggyBanks')); } /** diff --git a/app/Http/Controllers/ObjectGroup/EditController.php b/app/Http/Controllers/ObjectGroup/EditController.php index 174be3c4de..aa5b3214f2 100644 --- a/app/Http/Controllers/ObjectGroup/EditController.php +++ b/app/Http/Controllers/ObjectGroup/EditController.php @@ -73,7 +73,7 @@ class EditController extends Controller } session()->forget('object-groups.edit.fromUpdate'); - return view('object-groups.edit', compact('subTitle', 'subTitleIcon', 'objectGroup')); + return prefixView('object-groups.edit', compact('subTitle', 'subTitleIcon', 'objectGroup')); } diff --git a/app/Http/Controllers/ObjectGroup/IndexController.php b/app/Http/Controllers/ObjectGroup/IndexController.php index 6512b73062..1c8b5e7362 100644 --- a/app/Http/Controllers/ObjectGroup/IndexController.php +++ b/app/Http/Controllers/ObjectGroup/IndexController.php @@ -65,11 +65,11 @@ class IndexController extends Controller public function index() { $this->repository->deleteEmpty(); - $this->repository->sort(); + $this->repository->resetOrder(); $subTitle = (string) trans('firefly.object_groups_index'); $objectGroups = $this->repository->get(); - return view('object-groups.index', compact('subTitle', 'objectGroups')); + return prefixView('object-groups.index', compact('subTitle', 'objectGroups')); } /** diff --git a/app/Http/Controllers/PiggyBank/AmountController.php b/app/Http/Controllers/PiggyBank/AmountController.php index dd24347c93..95ced60014 100644 --- a/app/Http/Controllers/PiggyBank/AmountController.php +++ b/app/Http/Controllers/PiggyBank/AmountController.php @@ -82,7 +82,7 @@ class AmountController extends Controller $maxAmount = min($leftOnAccount, $leftToSave); $currency = $this->accountRepos->getAccountCurrency($piggyBank->account) ?? app('amount')->getDefaultCurrency(); - return view('piggy-banks.add', compact('piggyBank', 'maxAmount', 'currency')); + return prefixView('piggy-banks.add', compact('piggyBank', 'maxAmount', 'currency')); } /** @@ -102,7 +102,7 @@ class AmountController extends Controller $maxAmount = min($leftOnAccount, $leftToSave); $currency = $this->accountRepos->getAccountCurrency($piggyBank->account) ?? app('amount')->getDefaultCurrency(); - return view('piggy-banks.add-mobile', compact('piggyBank', 'maxAmount', 'currency')); + return prefixView('piggy-banks.add-mobile', compact('piggyBank', 'maxAmount', 'currency')); } /** @@ -202,7 +202,7 @@ class AmountController extends Controller $repetition = $this->piggyRepos->getRepetition($piggyBank); $currency = $this->accountRepos->getAccountCurrency($piggyBank->account) ?? app('amount')->getDefaultCurrency(); - return view('piggy-banks.remove', compact('piggyBank', 'repetition', 'currency')); + return prefixView('piggy-banks.remove', compact('piggyBank', 'repetition', 'currency')); } /** @@ -217,6 +217,6 @@ class AmountController extends Controller $repetition = $this->piggyRepos->getRepetition($piggyBank); $currency = $this->accountRepos->getAccountCurrency($piggyBank->account) ?? app('amount')->getDefaultCurrency(); - return view('piggy-banks.remove-mobile', compact('piggyBank', 'repetition', 'currency')); + return prefixView('piggy-banks.remove-mobile', compact('piggyBank', 'repetition', 'currency')); } } diff --git a/app/Http/Controllers/PiggyBank/CreateController.php b/app/Http/Controllers/PiggyBank/CreateController.php index 64b0972638..45ff1bc280 100644 --- a/app/Http/Controllers/PiggyBank/CreateController.php +++ b/app/Http/Controllers/PiggyBank/CreateController.php @@ -80,7 +80,7 @@ class CreateController extends Controller } session()->forget('piggy-banks.create.fromStore'); - return view('piggy-banks.create', compact('subTitle', 'subTitleIcon')); + return prefixView('piggy-banks.create', compact('subTitle', 'subTitleIcon')); } diff --git a/app/Http/Controllers/PiggyBank/DeleteController.php b/app/Http/Controllers/PiggyBank/DeleteController.php index 39eefeb451..27803dfbd1 100644 --- a/app/Http/Controllers/PiggyBank/DeleteController.php +++ b/app/Http/Controllers/PiggyBank/DeleteController.php @@ -75,7 +75,7 @@ class DeleteController extends Controller // put previous url in session $this->rememberPreviousUri('piggy-banks.delete.uri'); - return view('piggy-banks.delete', compact('piggyBank', 'subTitle')); + return prefixView('piggy-banks.delete', compact('piggyBank', 'subTitle')); } /** diff --git a/app/Http/Controllers/PiggyBank/EditController.php b/app/Http/Controllers/PiggyBank/EditController.php index 66ceae97e9..6e81b8f793 100644 --- a/app/Http/Controllers/PiggyBank/EditController.php +++ b/app/Http/Controllers/PiggyBank/EditController.php @@ -103,7 +103,7 @@ class EditController extends Controller } session()->forget('piggy-banks.edit.fromUpdate'); - return view('piggy-banks.edit', compact('subTitle', 'subTitleIcon', 'piggyBank', 'preFilled')); + return prefixView('piggy-banks.edit', compact('subTitle', 'subTitleIcon', 'piggyBank', 'preFilled')); } diff --git a/app/Http/Controllers/PiggyBank/IndexController.php b/app/Http/Controllers/PiggyBank/IndexController.php index e7b0c8551e..56df847f53 100644 --- a/app/Http/Controllers/PiggyBank/IndexController.php +++ b/app/Http/Controllers/PiggyBank/IndexController.php @@ -78,7 +78,7 @@ class IndexController extends Controller public function index(Request $request) { $this->cleanupObjectGroups(); - $this->piggyRepos->correctOrder(); + $this->piggyRepos->resetOrder(); $collection = $this->piggyRepos->getPiggyBanks(); $accounts = []; /** @var Carbon $end */ @@ -136,7 +136,7 @@ class IndexController extends Controller ksort($piggyBanks); - return view('piggy-banks.index', compact('piggyBanks', 'accounts')); + return prefixView('piggy-banks.index', compact('piggyBanks', 'accounts')); } /** diff --git a/app/Http/Controllers/PiggyBank/ShowController.php b/app/Http/Controllers/PiggyBank/ShowController.php index 6495b197ca..e28943051d 100644 --- a/app/Http/Controllers/PiggyBank/ShowController.php +++ b/app/Http/Controllers/PiggyBank/ShowController.php @@ -85,6 +85,6 @@ class ShowController extends Controller $subTitle = $piggyBank->name; $attachments = $this->piggyRepos->getAttachments($piggyBank); - return view('piggy-banks.show', compact('piggyBank', 'events', 'subTitle', 'piggy', 'attachments')); + return prefixView('piggy-banks.show', compact('piggyBank', 'events', 'subTitle', 'piggy', 'attachments')); } } diff --git a/app/Http/Controllers/PreferencesController.php b/app/Http/Controllers/PreferencesController.php index c5c8a1b9f8..6953c603dc 100644 --- a/app/Http/Controllers/PreferencesController.php +++ b/app/Http/Controllers/PreferencesController.php @@ -49,7 +49,7 @@ class PreferencesController extends Controller $this->middleware( function ($request, $next) { - app('view')->share('title', (string) trans('firefly.preferences')); + app('view')->share('title', (string)trans('firefly.preferences')); app('view')->share('mainTitleIcon', 'fa-gear'); return $next($request); @@ -111,14 +111,14 @@ class PreferencesController extends Controller Log::error($e->getMessage()); $locales = []; } - $locales = ['equal' => (string) trans('firefly.equal_to_language')] + $locales; + $locales = ['equal' => (string)trans('firefly.equal_to_language')] + $locales; // an important fallback is that the frontPageAccount array gets refilled automatically // when it turns up empty. if (0 === count($frontPageAccounts->data)) { $frontPageAccounts = $accountIds; } - return view( + return prefixView( 'preferences.index', compact( 'language', @@ -151,7 +151,7 @@ class PreferencesController extends Controller $frontPageAccounts = []; if (is_array($request->get('frontPageAccounts')) && count($request->get('frontPageAccounts')) > 0) { foreach ($request->get('frontPageAccounts') as $id) { - $frontPageAccounts[] = (int) $id; + $frontPageAccounts[] = (int)$id; } app('preferences')->set('frontPageAccounts', $frontPageAccounts); } @@ -164,14 +164,14 @@ class PreferencesController extends Controller session()->forget('range'); // custom fiscal year - $customFiscalYear = 1 === (int) $request->get('customFiscalYear'); - $fiscalYearStart = date('m-d', strtotime((string) $request->get('fiscalYearStart'))); + $customFiscalYear = 1 === (int)$request->get('customFiscalYear'); + $fiscalYearStart = date('m-d', strtotime((string)$request->get('fiscalYearStart'))); app('preferences')->set('customFiscalYear', $customFiscalYear); app('preferences')->set('fiscalYearStart', $fiscalYearStart); // save page size: app('preferences')->set('listPageSize', 50); - $listPageSize = (int) $request->get('listPageSize'); + $listPageSize = (int)$request->get('listPageSize'); if ($listPageSize > 0 && $listPageSize < 1337) { app('preferences')->set('listPageSize', $listPageSize); } @@ -207,10 +207,12 @@ class PreferencesController extends Controller 'notes' => isset($setOptions['notes']), 'attachments' => isset($setOptions['attachments']), 'external_uri' => isset($setOptions['external_uri']), + 'location' => isset($setOptions['location']), + 'links' => isset($setOptions['links']), ]; app('preferences')->set('transaction_journal_optional_fields', $optionalTj); - session()->flash('success', (string) trans('firefly.saved_preferences')); + session()->flash('success', (string)trans('firefly.saved_preferences')); app('preferences')->mark(); // telemetry: user language preference + default language. diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 452934e97c..b38955156b 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -98,7 +98,7 @@ class ProfileController extends Controller session()->flash('info', (string) trans('firefly.external_auth_disabled')); return redirect(route('profile.index')); } - return view('profile.logout-other-sessions'); + return prefixView('profile.logout-other-sessions'); } /** @@ -148,7 +148,7 @@ class ProfileController extends Controller $subTitle = (string) trans('firefly.change_your_email'); $subTitleIcon = 'fa-envelope'; - return view('profile.change-email', compact('title', 'subTitle', 'subTitleIcon', 'email')); + return prefixView('profile.change-email', compact('title', 'subTitle', 'subTitleIcon', 'email')); } /** @@ -170,7 +170,7 @@ class ProfileController extends Controller $subTitle = (string) trans('firefly.change_your_password'); $subTitleIcon = 'fa-key'; - return view('profile.change-password', compact('title', 'subTitle', 'subTitleIcon')); + return prefixView('profile.change-password', compact('title', 'subTitle', 'subTitleIcon')); } /** @@ -223,7 +223,7 @@ class ProfileController extends Controller $image = Google2FA::getQRCodeInline($domain, auth()->user()->email, $secret); - return view('profile.code', compact('image', 'secret', 'codes')); + return prefixView('profile.code', compact('image', 'secret', 'codes')); } /** @@ -283,7 +283,7 @@ class ProfileController extends Controller $subTitle = (string) trans('firefly.delete_account'); $subTitleIcon = 'fa-trash'; - return view('profile.delete-account', compact('title', 'subTitle', 'subTitleIcon')); + return prefixView('profile.delete-account', compact('title', 'subTitle', 'subTitleIcon')); } /** @@ -370,7 +370,7 @@ class ProfileController extends Controller $accessToken = app('preferences')->set('access_token', $token); } - return view('profile.index', compact('subTitle', 'mfaBackupCount', 'userId', 'accessToken', 'enabled2FA', 'isInternalAuth','isInternalIdentity')); + return prefixView('profile.index', compact('subTitle', 'mfaBackupCount', 'userId', 'accessToken', 'enabled2FA', 'isInternalAuth','isInternalIdentity')); } /** @@ -396,7 +396,7 @@ class ProfileController extends Controller app('preferences')->set('mfa_recovery', $recoveryCodes); app('preferences')->mark(); - return view('profile.new-backup-codes', compact('codes')); + return prefixView('profile.new-backup-codes', compact('codes')); } /** diff --git a/app/Http/Controllers/Recurring/CreateController.php b/app/Http/Controllers/Recurring/CreateController.php index bc5a42ba09..3c8849be03 100644 --- a/app/Http/Controllers/Recurring/CreateController.php +++ b/app/Http/Controllers/Recurring/CreateController.php @@ -158,7 +158,7 @@ class CreateController extends Controller $request->session()->flash('preFilled', $preFilled); - return view( + return prefixView( 'recurring.create', compact('tomorrow', 'oldRepetitionType', 'weekendResponses', 'preFilled', 'repetitionEnds', 'defaultCurrency', 'budgets') ); @@ -206,7 +206,7 @@ class CreateController extends Controller ]; $request->session()->flash('preFilled', $preFilled); - return view( + return prefixView( 'recurring.create', compact('tomorrow', 'oldRepetitionType', 'weekendResponses', 'preFilled', 'repetitionEnds', 'defaultCurrency', 'budgets') ); diff --git a/app/Http/Controllers/Recurring/DeleteController.php b/app/Http/Controllers/Recurring/DeleteController.php index 1fa3304f5e..b58e940eb6 100644 --- a/app/Http/Controllers/Recurring/DeleteController.php +++ b/app/Http/Controllers/Recurring/DeleteController.php @@ -78,7 +78,7 @@ class DeleteController extends Controller $journalsCreated = $this->recurring->getTransactions($recurrence)->count(); - return view('recurring.delete', compact('recurrence', 'subTitle', 'journalsCreated')); + return prefixView('recurring.delete', compact('recurrence', 'subTitle', 'journalsCreated')); } /** diff --git a/app/Http/Controllers/Recurring/EditController.php b/app/Http/Controllers/Recurring/EditController.php index 2941d6ff18..1d94838b01 100644 --- a/app/Http/Controllers/Recurring/EditController.php +++ b/app/Http/Controllers/Recurring/EditController.php @@ -86,6 +86,12 @@ class EditController extends Controller */ public function edit(Request $request, Recurrence $recurrence) { + // TODO should be in repos + $count = $recurrence->recurrenceTransactions()->count(); + if(0 === $count) { + throw new FireflyException('This recurring transaction has no meta-data. You will have to delete it and recreate it. Sorry!'); + } + /** @var RecurrenceTransformer $transformer */ $transformer = app(RecurrenceTransformer::class); $transformer->setParameters(new ParameterBag); @@ -137,7 +143,7 @@ class EditController extends Controller $array['transactions'][0]['tags'] = implode(',', $array['transactions'][0]['tags'] ?? []); - return view( + return prefixView( 'recurring.edit', compact('recurrence', 'array', 'weekendResponses', 'budgets', 'preFilled', 'currentRepType', 'repetitionEnd', 'repetitionEnds') ); diff --git a/app/Http/Controllers/Recurring/IndexController.php b/app/Http/Controllers/Recurring/IndexController.php index 108c5a06f7..de1404bb9f 100644 --- a/app/Http/Controllers/Recurring/IndexController.php +++ b/app/Http/Controllers/Recurring/IndexController.php @@ -125,7 +125,7 @@ class IndexController extends Controller $this->verifyRecurringCronJob(); - return view('recurring.index', compact('paginator', 'today', 'page', 'pageSize', 'total')); + return prefixView('recurring.index', compact('paginator', 'today', 'page', 'pageSize', 'total')); } } diff --git a/app/Http/Controllers/Recurring/ShowController.php b/app/Http/Controllers/Recurring/ShowController.php index c2fa2e609e..652aaed8d0 100644 --- a/app/Http/Controllers/Recurring/ShowController.php +++ b/app/Http/Controllers/Recurring/ShowController.php @@ -96,6 +96,6 @@ class ShowController extends Controller $subTitle = (string) trans('firefly.overview_for_recurrence', ['title' => $recurrence->title]); - return view('recurring.show', compact('recurrence', 'subTitle', 'array', 'groups','today')); + return prefixView('recurring.show', compact('recurrence', 'subTitle', 'array', 'groups','today')); } } diff --git a/app/Http/Controllers/Report/AccountController.php b/app/Http/Controllers/Report/AccountController.php index ebea357ec1..1fba3e855f 100644 --- a/app/Http/Controllers/Report/AccountController.php +++ b/app/Http/Controllers/Report/AccountController.php @@ -62,7 +62,7 @@ class AccountController extends Controller $accountTasker = app(AccountTaskerInterface::class); $accountReport = $accountTasker->getAccountReport($accounts, $start, $end); try { - $result = view('reports.partials.accounts', compact('accountReport'))->render(); + $result = prefixView('reports.partials.accounts', compact('accountReport'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::debug(sprintf('Could not render reports.partials.accounts: %s', $e->getMessage())); diff --git a/app/Http/Controllers/Report/BalanceController.php b/app/Http/Controllers/Report/BalanceController.php index ed25392830..1d823b8e20 100644 --- a/app/Http/Controllers/Report/BalanceController.php +++ b/app/Http/Controllers/Report/BalanceController.php @@ -139,7 +139,7 @@ class BalanceController extends Controller try { - $result = view('reports.partials.balance', compact('report'))->render(); + $result = prefixView('reports.partials.balance', compact('report'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::debug(sprintf('Could not render reports.partials.balance: %s', $e->getMessage())); diff --git a/app/Http/Controllers/Report/BillController.php b/app/Http/Controllers/Report/BillController.php index 59c3fbafa0..e8cea95237 100644 --- a/app/Http/Controllers/Report/BillController.php +++ b/app/Http/Controllers/Report/BillController.php @@ -60,7 +60,7 @@ class BillController extends Controller try { - $result = view('reports.partials.bills', compact('report'))->render(); + $result = prefixView('reports.partials.bills', compact('report'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::debug(sprintf('Could not render reports.partials.budgets: %s', $e->getMessage())); diff --git a/app/Http/Controllers/Report/BudgetController.php b/app/Http/Controllers/Report/BudgetController.php index 27e9e4f1a8..1a9eeb9a57 100644 --- a/app/Http/Controllers/Report/BudgetController.php +++ b/app/Http/Controllers/Report/BudgetController.php @@ -95,7 +95,7 @@ class BudgetController extends Controller $generator->accountPerBudget(); $report = $generator->getReport(); - return view('reports.budget.partials.account-per-budget', compact('report', 'budgets')); + return prefixView('reports.budget.partials.account-per-budget', compact('report', 'budgets')); } /** @@ -151,7 +151,7 @@ class BudgetController extends Controller } } - return view('reports.budget.partials.accounts', compact('sums', 'report')); + return prefixView('reports.budget.partials.accounts', compact('sums', 'report')); } /** @@ -196,7 +196,7 @@ class BudgetController extends Controller array_multisort($amounts, SORT_ASC, $result); try { - $result = view('reports.budget.partials.avg-expenses', compact('result'))->render(); + $result = prefixView('reports.budget.partials.avg-expenses', compact('result'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); @@ -271,7 +271,7 @@ class BudgetController extends Controller } } - return view('reports.budget.partials.budgets', compact('sums', 'report')); + return prefixView('reports.budget.partials.budgets', compact('sums', 'report')); } /** @@ -296,7 +296,7 @@ class BudgetController extends Controller $generator->general(); $report = $generator->getReport(); - return view('reports.partials.budgets', compact('report'))->render(); + return prefixView('reports.partials.budgets', compact('report'))->render(); } /** @@ -353,7 +353,7 @@ class BudgetController extends Controller } } try { - $result = view('reports.partials.budget-period', compact('report', 'periods'))->render(); + $result = prefixView('reports.partials.budget-period', compact('report', 'periods'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); @@ -405,7 +405,7 @@ class BudgetController extends Controller array_multisort($amounts, SORT_ASC, $result); try { - $result = view('reports.budget.partials.top-expenses', compact('result'))->render(); + $result = prefixView('reports.budget.partials.top-expenses', compact('result'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); diff --git a/app/Http/Controllers/Report/CategoryController.php b/app/Http/Controllers/Report/CategoryController.php index c8c1a28e3f..d5558f4ee6 100644 --- a/app/Http/Controllers/Report/CategoryController.php +++ b/app/Http/Controllers/Report/CategoryController.php @@ -165,7 +165,7 @@ class CategoryController extends Controller } } - return view('reports.category.partials.account-per-category', compact('report', 'categories')); + return prefixView('reports.category.partials.account-per-category', compact('report', 'categories')); } /** @@ -269,7 +269,7 @@ class CategoryController extends Controller } } - return view('reports.category.partials.accounts', compact('sums', 'report')); + return prefixView('reports.category.partials.accounts', compact('sums', 'report')); } /** @@ -314,7 +314,7 @@ class CategoryController extends Controller array_multisort($amounts, SORT_ASC, $result); try { - $result = view('reports.category.partials.avg-expenses', compact('result'))->render(); + $result = prefixView('reports.category.partials.avg-expenses', compact('result'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); @@ -366,7 +366,7 @@ class CategoryController extends Controller array_multisort($amounts, SORT_DESC, $result); try { - $result = view('reports.category.partials.avg-income', compact('result'))->render(); + $result = prefixView('reports.category.partials.avg-income', compact('result'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); @@ -481,7 +481,7 @@ class CategoryController extends Controller } } - return view('reports.category.partials.categories', compact('sums', 'report')); + return prefixView('reports.category.partials.categories', compact('sums', 'report')); } /** @@ -550,7 +550,7 @@ class CategoryController extends Controller $report = $data; try { - $result = view('reports.partials.category-period', compact('report', 'periods'))->render(); + $result = prefixView('reports.partials.category-period', compact('report', 'periods'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::error(sprintf('Could not render category::expenses: %s', $e->getMessage())); @@ -629,7 +629,7 @@ class CategoryController extends Controller $report = $data; try { - $result = view('reports.partials.category-period', compact('report', 'periods'))->render(); + $result = prefixView('reports.partials.category-period', compact('report', 'periods'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::error(sprintf('Could not render category::expenses: %s', $e->getMessage())); @@ -674,7 +674,7 @@ class CategoryController extends Controller // @codeCoverageIgnoreStart try { - $result = view('reports.partials.categories', compact('report'))->render(); + $result = prefixView('reports.partials.categories', compact('report'))->render(); $cache->store($result); } catch (Throwable $e) { Log::error(sprintf('Could not render category::expenses: %s', $e->getMessage())); @@ -726,7 +726,7 @@ class CategoryController extends Controller array_multisort($amounts, SORT_ASC, $result); try { - $result = view('reports.category.partials.top-expenses', compact('result'))->render(); + $result = prefixView('reports.category.partials.top-expenses', compact('result'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); @@ -776,7 +776,7 @@ class CategoryController extends Controller array_multisort($amounts, SORT_DESC, $result); try { - $result = view('reports.category.partials.top-income', compact('result'))->render(); + $result = prefixView('reports.category.partials.top-income', compact('result'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); diff --git a/app/Http/Controllers/Report/DoubleController.php b/app/Http/Controllers/Report/DoubleController.php index c7f9650fb2..5a4a7de4cd 100644 --- a/app/Http/Controllers/Report/DoubleController.php +++ b/app/Http/Controllers/Report/DoubleController.php @@ -110,7 +110,7 @@ class DoubleController extends Controller array_multisort($amounts, SORT_ASC, $result); try { - $result = view('reports.double.partials.avg-expenses', compact('result'))->render(); + $result = prefixView('reports.double.partials.avg-expenses', compact('result'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); @@ -162,7 +162,7 @@ class DoubleController extends Controller array_multisort($amounts, SORT_DESC, $result); try { - $result = view('reports.double.partials.avg-income', compact('result'))->render(); + $result = prefixView('reports.double.partials.avg-income', compact('result'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); @@ -288,7 +288,7 @@ class DoubleController extends Controller } } - return view('reports.double.partials.accounts', compact('sums', 'report')); + return prefixView('reports.double.partials.accounts', compact('sums', 'report')); } /** @@ -388,7 +388,7 @@ class DoubleController extends Controller } } - return view('reports.double.partials.accounts-per-asset', compact('sums', 'report')); + return prefixView('reports.double.partials.accounts-per-asset', compact('sums', 'report')); } /** @@ -431,7 +431,7 @@ class DoubleController extends Controller array_multisort($amounts, SORT_ASC, $result); try { - $result = view('reports.double.partials.top-expenses', compact('result'))->render(); + $result = prefixView('reports.double.partials.top-expenses', compact('result'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); @@ -481,7 +481,7 @@ class DoubleController extends Controller array_multisort($amounts, SORT_DESC, $result); try { - $result = view('reports.double.partials.top-income', compact('result'))->render(); + $result = prefixView('reports.double.partials.top-income', compact('result'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); diff --git a/app/Http/Controllers/Report/OperationsController.php b/app/Http/Controllers/Report/OperationsController.php index ae52958745..04ee93f96e 100644 --- a/app/Http/Controllers/Report/OperationsController.php +++ b/app/Http/Controllers/Report/OperationsController.php @@ -82,7 +82,7 @@ class OperationsController extends Controller $report = $this->tasker->getExpenseReport($start, $end, $accounts); $type = 'expense-entry'; try { - $result = view('reports.partials.income-expenses', compact('report', 'type'))->render(); + $result = prefixView('reports.partials.income-expenses', compact('report', 'type'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::debug(sprintf('Could not render reports.partials.income-expense: %s', $e->getMessage())); @@ -117,7 +117,7 @@ class OperationsController extends Controller $report = $this->tasker->getIncomeReport($start, $end, $accounts); $type = 'income-entry'; try { - $result = view('reports.partials.income-expenses', compact('report', 'type'))->render(); + $result = prefixView('reports.partials.income-expenses', compact('report', 'type'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::debug(sprintf('Could not render reports.partials.income-expenses: %s', $e->getMessage())); @@ -172,7 +172,7 @@ class OperationsController extends Controller } try { - $result = view('reports.partials.operations', compact('sums'))->render(); + $result = prefixView('reports.partials.operations', compact('sums'))->render(); } catch (Throwable $e) { Log::debug(sprintf('Could not render reports.partials.operations: %s', $e->getMessage())); $result = 'Could not render view.'; diff --git a/app/Http/Controllers/Report/TagController.php b/app/Http/Controllers/Report/TagController.php index 606eeed1a8..50edfafe84 100644 --- a/app/Http/Controllers/Report/TagController.php +++ b/app/Http/Controllers/Report/TagController.php @@ -159,7 +159,7 @@ class TagController extends Controller } } - return view('reports.tag.partials.account-per-tag', compact('report', 'tags')); + return prefixView('reports.tag.partials.account-per-tag', compact('report', 'tags')); } /** @@ -263,7 +263,7 @@ class TagController extends Controller } } - return view('reports.tag.partials.accounts', compact('sums', 'report')); + return prefixView('reports.tag.partials.accounts', compact('sums', 'report')); } /** @@ -308,7 +308,7 @@ class TagController extends Controller array_multisort($amounts, SORT_ASC, $result); try { - $result = view('reports.tag.partials.avg-expenses', compact('result'))->render(); + $result = prefixView('reports.tag.partials.avg-expenses', compact('result'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); @@ -360,7 +360,7 @@ class TagController extends Controller array_multisort($amounts, SORT_DESC, $result); try { - $result = view('reports.tag.partials.avg-income', compact('result'))->render(); + $result = prefixView('reports.tag.partials.avg-income', compact('result'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); @@ -475,7 +475,7 @@ class TagController extends Controller } } - return view('reports.tag.partials.tags', compact('sums', 'report')); + return prefixView('reports.tag.partials.tags', compact('sums', 'report')); } /** @@ -518,7 +518,7 @@ class TagController extends Controller array_multisort($amounts, SORT_ASC, $result); try { - $result = view('reports.tag.partials.top-expenses', compact('result'))->render(); + $result = prefixView('reports.tag.partials.top-expenses', compact('result'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); @@ -568,7 +568,7 @@ class TagController extends Controller array_multisort($amounts, SORT_DESC, $result); try { - $result = view('reports.tag.partials.top-income', compact('result'))->render(); + $result = prefixView('reports.tag.partials.top-income', compact('result'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index 6bbcfa583c..8dc4988fe1 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -88,7 +88,7 @@ class ReportController extends Controller public function auditReport(Collection $accounts, Carbon $start, Carbon $end) { if ($end < $start) { - return view('error')->with('message', (string) trans('firefly.end_after_start_date')); // @codeCoverageIgnore + return prefixView('error')->with('message', (string) trans('firefly.end_after_start_date')); // @codeCoverageIgnore } $this->repository->cleanupBudgets(); @@ -124,7 +124,7 @@ class ReportController extends Controller public function budgetReport(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end) { if ($end < $start) { - return view('error')->with('message', (string) trans('firefly.end_after_start_date')); // @codeCoverageIgnore + return prefixView('error')->with('message', (string) trans('firefly.end_after_start_date')); // @codeCoverageIgnore } $this->repository->cleanupBudgets(); @@ -161,7 +161,7 @@ class ReportController extends Controller public function categoryReport(Collection $accounts, Collection $categories, Carbon $start, Carbon $end) { if ($end < $start) { - return view('error')->with('message', (string) trans('firefly.end_after_start_date')); // @codeCoverageIgnore + return prefixView('error')->with('message', (string) trans('firefly.end_after_start_date')); // @codeCoverageIgnore } $this->repository->cleanupBudgets(); @@ -197,7 +197,7 @@ class ReportController extends Controller public function defaultReport(Collection $accounts, Carbon $start, Carbon $end) { if ($end < $start) { - return view('error')->with('message', (string) trans('firefly.end_after_start_date')); + return prefixView('error')->with('message', (string) trans('firefly.end_after_start_date')); } $this->repository->cleanupBudgets(); @@ -291,7 +291,7 @@ class ReportController extends Controller $accountList = implode(',', $accounts->pluck('id')->toArray()); $this->repository->cleanupBudgets(); - return view('reports.index', compact('months', 'accounts', 'start', 'accountList', 'groupedAccounts', 'customFiscalYear')); + return prefixView('reports.index', compact('months', 'accounts', 'start', 'accountList', 'groupedAccounts', 'customFiscalYear')); } /** @@ -379,7 +379,7 @@ class ReportController extends Controller } if ($request->getEndDate() < $request->getStartDate()) { - return view('error')->with('message', (string) trans('firefly.end_after_start_date')); + return prefixView('error')->with('message', (string) trans('firefly.end_after_start_date')); } switch ($reportType) { @@ -422,7 +422,7 @@ class ReportController extends Controller public function tagReport(Collection $accounts, Collection $tags, Carbon $start, Carbon $end) { if ($end < $start) { - return view('error')->with('message', (string) trans('firefly.end_after_start_date')); // @codeCoverageIgnore + return prefixView('error')->with('message', (string) trans('firefly.end_after_start_date')); // @codeCoverageIgnore } $this->repository->cleanupBudgets(); diff --git a/app/Http/Controllers/Rule/CreateController.php b/app/Http/Controllers/Rule/CreateController.php index f12449f0f0..880bcc7838 100644 --- a/app/Http/Controllers/Rule/CreateController.php +++ b/app/Http/Controllers/Rule/CreateController.php @@ -126,7 +126,7 @@ class CreateController extends Controller } session()->forget('rules.create.fromStore'); - return view( + return prefixView( 'rules.rule.create', compact('subTitleIcon', 'oldTriggers', 'preFilled', 'oldActions', 'triggerCount', 'actionCount', 'ruleGroup', 'subTitle') ); @@ -180,7 +180,7 @@ class CreateController extends Controller } session()->forget('rules.create.fromStore'); - return view( + return prefixView( 'rules.rule.create', compact('subTitleIcon', 'oldTriggers', 'preFilled', 'oldActions', 'triggerCount', 'actionCount', 'subTitle') ); @@ -229,7 +229,7 @@ class CreateController extends Controller } session()->forget('rules.create.fromStore'); - return view( + return prefixView( 'rules.rule.create', compact('subTitleIcon', 'oldTriggers', 'preFilled', 'oldActions', 'triggerCount', 'actionCount', 'subTitle') ); diff --git a/app/Http/Controllers/Rule/DeleteController.php b/app/Http/Controllers/Rule/DeleteController.php index 212631438d..a80ec188a1 100644 --- a/app/Http/Controllers/Rule/DeleteController.php +++ b/app/Http/Controllers/Rule/DeleteController.php @@ -74,7 +74,7 @@ class DeleteController extends Controller // put previous url in session $this->rememberPreviousUri('rules.delete.uri'); - return view('rules.rule.delete', compact('rule', 'subTitle')); + return prefixView('rules.rule.delete', compact('rule', 'subTitle')); } /** diff --git a/app/Http/Controllers/Rule/EditController.php b/app/Http/Controllers/Rule/EditController.php index 0891804279..bd3ad98ad2 100644 --- a/app/Http/Controllers/Rule/EditController.php +++ b/app/Http/Controllers/Rule/EditController.php @@ -136,7 +136,7 @@ class EditController extends Controller $request->session()->flash('preFilled', $preFilled); - return view('rules.rule.edit', compact('rule', 'subTitle', 'primaryTrigger', 'oldTriggers', 'oldActions', 'triggerCount', 'actionCount')); + return prefixView('rules.rule.edit', compact('rule', 'subTitle', 'primaryTrigger', 'oldTriggers', 'oldActions', 'triggerCount', 'actionCount')); } /** @@ -187,7 +187,7 @@ class EditController extends Controller $index = 0; foreach ($submittedOperators as $operator) { try { - $renderedEntries[] = view( + $renderedEntries[] = prefixView( 'rules.partials.trigger', [ 'oldTrigger' => OperatorQuerySearch::getRootOperator($operator['type']), diff --git a/app/Http/Controllers/Rule/IndexController.php b/app/Http/Controllers/Rule/IndexController.php index b068004aeb..793451ee07 100644 --- a/app/Http/Controllers/Rule/IndexController.php +++ b/app/Http/Controllers/Rule/IndexController.php @@ -77,9 +77,9 @@ class IndexController extends Controller $this->createDefaultRuleGroup(); $this->createDefaultRule(); $this->ruleGroupRepos->resetRuleGroupOrder(); - $ruleGroups = $this->ruleGroupRepos->getRuleGroupsWithRules($user); + $ruleGroups = $this->ruleGroupRepos->getRuleGroupsWithRules(null); - return view('rules.index', compact('ruleGroups')); + return prefixView('rules.index', compact('ruleGroups')); } /** diff --git a/app/Http/Controllers/Rule/SelectController.php b/app/Http/Controllers/Rule/SelectController.php index 0490704432..ecab505702 100644 --- a/app/Http/Controllers/Rule/SelectController.php +++ b/app/Http/Controllers/Rule/SelectController.php @@ -60,7 +60,7 @@ class SelectController extends Controller $this->middleware( function ($request, $next) { - app('view')->share('title', (string) trans('firefly.rules')); + app('view')->share('title', (string)trans('firefly.rules')); app('view')->share('mainTitleIcon', 'fa-random'); return $next($request); @@ -97,9 +97,9 @@ class SelectController extends Controller // set rules: $newRuleEngine->setRules(new Collection([$rule])); $newRuleEngine->fire(); + $resultCount = $newRuleEngine->getResults(); - // Tell the user that the job is queued - session()->flash('success', (string) trans('firefly.applied_rule_selection', ['title' => $rule->title])); + session()->flash('success', (string)trans_choice('firefly.applied_rule_selection', $resultCount, ['title' => $rule->title])); return redirect()->route('rules.index'); } @@ -116,14 +116,15 @@ class SelectController extends Controller { if (false === $rule->active) { session()->flash('warning', trans('firefly.cannot_fire_inactive_rules')); + return redirect(route('rules.index')); } // does the user have shared accounts? $first = session('first', Carbon::now()->subYear())->format('Y-m-d'); $today = Carbon::now()->format('Y-m-d'); - $subTitle = (string) trans('firefly.apply_rule_selection', ['title' => $rule->title]); + $subTitle = (string)trans('firefly.apply_rule_selection', ['title' => $rule->title]); - return view('rules.rule.select-transactions', compact('first', 'today', 'rule', 'subTitle')); + return prefixView('rules.rule.select-transactions', compact('first', 'today', 'rule', 'subTitle')); } /** @@ -147,7 +148,7 @@ class SelectController extends Controller // warn if nothing. if (0 === count($textTriggers)) { - return response()->json(['html' => '', 'warning' => (string) trans('firefly.warning_no_valid_triggers')]); // @codeCoverageIgnore + return response()->json(['html' => '', 'warning' => (string)trans('firefly.warning_no_valid_triggers')]); // @codeCoverageIgnore } foreach ($textTriggers as $textTrigger) { @@ -170,13 +171,13 @@ class SelectController extends Controller // Warn the user if only a subset of transactions is returned $warning = ''; if (0 === count($collection)) { - $warning = (string) trans('firefly.warning_no_matching_transactions'); // @codeCoverageIgnore + $warning = (string)trans('firefly.warning_no_matching_transactions'); // @codeCoverageIgnore } // Return json response $view = 'ERROR, see logs.'; try { - $view = view('list.journals-array-tiny', ['groups' => $collection])->render(); + $view = prefixView('list.journals-array-tiny', ['groups' => $collection])->render(); // @codeCoverageIgnoreStart } catch (Throwable $exception) { Log::error(sprintf('Could not render view in testTriggers(): %s', $exception->getMessage())); @@ -203,7 +204,7 @@ class SelectController extends Controller $triggers = $rule->ruleTriggers; if (0 === count($triggers)) { - return response()->json(['html' => '', 'warning' => (string) trans('firefly.warning_no_valid_triggers')]); // @codeCoverageIgnore + return response()->json(['html' => '', 'warning' => (string)trans('firefly.warning_no_valid_triggers')]); // @codeCoverageIgnore } @@ -217,13 +218,13 @@ class SelectController extends Controller $warning = ''; if (0 === count($collection)) { - $warning = (string) trans('firefly.warning_no_matching_transactions'); // @codeCoverageIgnore + $warning = (string)trans('firefly.warning_no_matching_transactions'); // @codeCoverageIgnore } // Return json response $view = 'ERROR, see logs.'; try { - $view = view('list.journals-array-tiny', ['groups' => $collection])->render(); + $view = prefixView('list.journals-array-tiny', ['groups' => $collection])->render(); // @codeCoverageIgnoreStart } catch (Throwable $exception) { Log::error(sprintf('Could not render view in testTriggersByRule(): %s', $exception->getMessage())); diff --git a/app/Http/Controllers/RuleGroup/CreateController.php b/app/Http/Controllers/RuleGroup/CreateController.php index d2edd4df2c..31e7969d72 100644 --- a/app/Http/Controllers/RuleGroup/CreateController.php +++ b/app/Http/Controllers/RuleGroup/CreateController.php @@ -77,7 +77,7 @@ class CreateController extends Controller } session()->forget('rule-groups.create.fromStore'); - return view('rules.rule-group.create', compact('subTitleIcon', 'subTitle')); + return prefixView('rules.rule-group.create', compact('subTitleIcon', 'subTitle')); } /** diff --git a/app/Http/Controllers/RuleGroup/DeleteController.php b/app/Http/Controllers/RuleGroup/DeleteController.php index a06fadf159..f83f73d9ec 100644 --- a/app/Http/Controllers/RuleGroup/DeleteController.php +++ b/app/Http/Controllers/RuleGroup/DeleteController.php @@ -77,7 +77,7 @@ class DeleteController extends Controller // put previous url in session $this->rememberPreviousUri('rule-groups.delete.uri'); - return view('rules.rule-group.delete', compact('ruleGroup', 'subTitle')); + return prefixView('rules.rule-group.delete', compact('ruleGroup', 'subTitle')); } /** diff --git a/app/Http/Controllers/RuleGroup/EditController.php b/app/Http/Controllers/RuleGroup/EditController.php index 09dc3889bd..a40f25f2be 100644 --- a/app/Http/Controllers/RuleGroup/EditController.php +++ b/app/Http/Controllers/RuleGroup/EditController.php @@ -101,7 +101,7 @@ class EditController extends Controller session()->forget('rule-groups.edit.fromUpdate'); session()->flash('preFilled', $preFilled); - return view('rules.rule-group.edit', compact('ruleGroup', 'subTitle')); + return prefixView('rules.rule-group.edit', compact('ruleGroup', 'subTitle')); } /** diff --git a/app/Http/Controllers/RuleGroup/ExecutionController.php b/app/Http/Controllers/RuleGroup/ExecutionController.php index e46b4bdfc3..bb57a6cb72 100644 --- a/app/Http/Controllers/RuleGroup/ExecutionController.php +++ b/app/Http/Controllers/RuleGroup/ExecutionController.php @@ -115,7 +115,7 @@ class ExecutionController extends Controller $today = Carbon::now()->format('Y-m-d'); $subTitle = (string) trans('firefly.apply_rule_group_selection', ['title' => $ruleGroup->title]); - return view('rules.rule-group.select-transactions', compact('first', 'today', 'ruleGroup', 'subTitle')); + return prefixView('rules.rule-group.select-transactions', compact('first', 'today', 'ruleGroup', 'subTitle')); } diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index ce52d7ad05..aa55db88ba 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -88,7 +88,7 @@ class SearchController extends Controller $subTitle = (string) trans('breadcrumbs.search_result', ['query' => $fullQuery]); - return view('search.index', compact('query', 'operators', 'page', 'rule', 'fullQuery', 'subTitle', 'ruleId', 'ruleChanged')); + return prefixView('search.index', compact('query', 'operators', 'page', 'rule', 'fullQuery', 'subTitle', 'ruleId', 'ruleChanged')); } /** @@ -115,7 +115,7 @@ class SearchController extends Controller $groups->setPath($url); try { - $html = view('search.search', compact('groups', 'hasPages', 'searchTime'))->render(); + $html = prefixView('search.search', compact('groups', 'hasPages', 'searchTime'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::error(sprintf('Cannot render search.search: %s', $e->getMessage())); diff --git a/app/Http/Controllers/System/InstallController.php b/app/Http/Controllers/System/InstallController.php index f953050c63..e540f432f7 100644 --- a/app/Http/Controllers/System/InstallController.php +++ b/app/Http/Controllers/System/InstallController.php @@ -83,6 +83,7 @@ class InstallController extends Controller 'firefly-iii:rename-account-meta' => [], 'firefly-iii:migrate-recurrence-meta' => [], 'firefly-iii:migrate-tag-locations' => [], + 'firefly-iii:migrate-recurrence-type' => [], // verify commands 'firefly-iii:fix-piggies' => [], @@ -120,12 +121,12 @@ class InstallController extends Controller public function index() { // index will set FF3 version. - app('fireflyconfig')->set('ff3_version', (string) config('firefly.version')); + app('fireflyconfig')->set('ff3_version', (string)config('firefly.version')); // set new DB version. - app('fireflyconfig')->set('db_version', (int) config('firefly.db_version')); + app('fireflyconfig')->set('db_version', (int)config('firefly.db_version')); - return view('install.index'); + return prefixView('install.index'); } /** @@ -156,7 +157,7 @@ class InstallController extends Controller */ public function runCommand(Request $request): JsonResponse { - $requestIndex = (int) $request->get('index'); + $requestIndex = (int)$request->get('index'); $response = [ 'hasNextCommand' => false, 'done' => true, @@ -183,6 +184,7 @@ class InstallController extends Controller if (false === $result) { $response['errorMessage'] = $this->lastError; $response['error'] = true; + return response()->json($response); } $index++; @@ -197,6 +199,7 @@ class InstallController extends Controller /** * @param string $command * @param array $args + * * @return bool */ private function executeCommand(string $command, array $args): bool @@ -215,15 +218,17 @@ class InstallController extends Controller Log::error($e->getTraceAsString()); if (strpos($e->getMessage(), 'open_basedir restriction in effect')) { $this->lastError = self::BASEDIR_ERROR; + return false; } $this->lastError = sprintf('%s %s', self::OTHER_ERROR, $e->getMessage()); + return false; } // clear cache as well. Cache::clear(); Preferences::mark(); - + return true; } } diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index a5bdb76b63..70433f63c1 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -94,7 +94,7 @@ class TagController extends Controller } session()->forget('tags.create.fromStore'); - return view('tags.create', compact('subTitle', 'subTitleIcon', 'locations')); + return prefixView('tags.create', compact('subTitle', 'subTitleIcon', 'locations')); } /** @@ -111,7 +111,7 @@ class TagController extends Controller // put previous url in session $this->rememberPreviousUri('tags.delete.uri'); - return view('tags.delete', compact('tag', 'subTitle')); + return prefixView('tags.delete', compact('tag', 'subTitle')); } /** @@ -164,7 +164,7 @@ class TagController extends Controller } session()->forget('tags.edit.fromUpdate'); - return view('tags.edit', compact('tag', 'subTitle', 'subTitleIcon', 'locations')); + return prefixView('tags.edit', compact('tag', 'subTitle', 'subTitleIcon', 'locations')); } /** @@ -192,7 +192,7 @@ class TagController extends Controller } $count = $repository->count(); - return view('tags.index', compact('tags', 'count')); + return prefixView('tags.index', compact('tags', 'count')); } /** @@ -262,7 +262,7 @@ class TagController extends Controller $groups->setPath($path); $sums = $this->repository->sumsOfTag($tag, $start, $end); - return view('tags.show', compact('tag', 'attachments', 'sums', 'periods', 'subTitle', 'subTitleIcon', 'groups', 'start', 'end', 'location')); + return prefixView('tags.show', compact('tag', 'attachments', 'sums', 'periods', 'subTitle', 'subTitleIcon', 'groups', 'start', 'end', 'location')); } /** @@ -295,7 +295,7 @@ class TagController extends Controller $groups->setPath($path); $sums = $this->repository->sumsOfTag($tag, $start, $end); - return view('tags.show', compact('tag', 'attachments', 'sums', 'periods', 'subTitle', 'subTitleIcon', 'groups', 'start', 'end', 'location')); + return prefixView('tags.show', compact('tag', 'attachments', 'sums', 'periods', 'subTitle', 'subTitleIcon', 'groups', 'start', 'end', 'location')); } /** diff --git a/app/Http/Controllers/Transaction/BulkController.php b/app/Http/Controllers/Transaction/BulkController.php index 9f20c61659..f630f3ec71 100644 --- a/app/Http/Controllers/Transaction/BulkController.php +++ b/app/Http/Controllers/Transaction/BulkController.php @@ -84,7 +84,7 @@ class BulkController extends Controller $budgetRepos = app(BudgetRepositoryInterface::class); $budgetList = app('expandedform')->makeSelectListWithEmpty($budgetRepos->getActiveBudgets()); - return view('transactions.bulk.edit', compact('journals', 'subTitle', 'budgetList')); + return prefixView('transactions.bulk.edit', compact('journals', 'subTitle', 'budgetList')); } diff --git a/app/Http/Controllers/Transaction/ConvertController.php b/app/Http/Controllers/Transaction/ConvertController.php index 9556096bd0..6e75b311fb 100644 --- a/app/Http/Controllers/Transaction/ConvertController.php +++ b/app/Http/Controllers/Transaction/ConvertController.php @@ -124,7 +124,7 @@ class ConvertController extends Controller return redirect(route('transactions.show', [$group->id])); } - return view( + return prefixView( 'transactions.convert', compact( 'sourceType', diff --git a/app/Http/Controllers/Transaction/CreateController.php b/app/Http/Controllers/Transaction/CreateController.php index 4af431b334..c8f3c8c6d5 100644 --- a/app/Http/Controllers/Transaction/CreateController.php +++ b/app/Http/Controllers/Transaction/CreateController.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Transaction; +use FireflyIII\Events\StoredTransactionGroup; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\TransactionGroup; use FireflyIII\Repositories\Account\AccountRepositoryInterface; @@ -68,6 +69,9 @@ class CreateController extends Controller $service = app(GroupCloneService::class); $newGroup = $service->cloneGroup($group); + // event! + event(new StoredTransactionGroup($newGroup, true)); + app('preferences')->mark(); $title = $newGroup->title ?? $newGroup->transactionJournals->first()->description; @@ -110,7 +114,7 @@ class CreateController extends Controller session()->put('preFilled', $preFilled); - return view( + return prefixView( 'transactions.create', compact( 'subTitleIcon', diff --git a/app/Http/Controllers/Transaction/DeleteController.php b/app/Http/Controllers/Transaction/DeleteController.php index 87e3dfc429..7846e1f830 100644 --- a/app/Http/Controllers/Transaction/DeleteController.php +++ b/app/Http/Controllers/Transaction/DeleteController.php @@ -91,7 +91,7 @@ class DeleteController extends Controller Log::debug('Will try to remember previous URI'); $this->rememberPreviousUri('transactions.delete.uri'); - return view('transactions.delete', compact('group', 'journal', 'subTitle', 'objectType', 'previous')); + return prefixView('transactions.delete', compact('group', 'journal', 'subTitle', 'objectType', 'previous')); } /** diff --git a/app/Http/Controllers/Transaction/EditController.php b/app/Http/Controllers/Transaction/EditController.php index fc4fdea4dc..6dc9475688 100644 --- a/app/Http/Controllers/Transaction/EditController.php +++ b/app/Http/Controllers/Transaction/EditController.php @@ -89,7 +89,7 @@ class EditController extends Controller $previousUri = str_replace($search, '', $previousUri); - return view( + return prefixView( 'transactions.edit', compact( 'cash', 'allowedSourceDests', 'expectedSourceTypes', 'transactionGroup', 'allowedOpposingTypes', 'accountToTypes', 'defaultCurrency', diff --git a/app/Http/Controllers/Transaction/IndexController.php b/app/Http/Controllers/Transaction/IndexController.php index 95f3b7767f..c82cbeedd9 100644 --- a/app/Http/Controllers/Transaction/IndexController.php +++ b/app/Http/Controllers/Transaction/IndexController.php @@ -117,7 +117,7 @@ class IndexController extends Controller $groups = $collector->getPaginatedGroups(); $groups->setPath($path); - return view('transactions.index', compact('subTitle', 'objectType', 'subTitleIcon', 'groups', 'periods', 'start', 'end')); + return prefixView('transactions.index', compact('subTitle', 'objectType', 'subTitleIcon', 'groups', 'periods', 'start', 'end')); } /** @@ -156,6 +156,6 @@ class IndexController extends Controller $groups = $collector->getPaginatedGroups(); $groups->setPath($path); - return view('transactions.index', compact('subTitle', 'objectType', 'subTitleIcon', 'groups', 'start', 'end')); + return prefixView('transactions.index', compact('subTitle', 'objectType', 'subTitleIcon', 'groups', 'start', 'end')); } } diff --git a/app/Http/Controllers/Transaction/LinkController.php b/app/Http/Controllers/Transaction/LinkController.php index 24895c1815..ee6063405d 100644 --- a/app/Http/Controllers/Transaction/LinkController.php +++ b/app/Http/Controllers/Transaction/LinkController.php @@ -78,7 +78,7 @@ class LinkController extends Controller $subTitle = (string) trans('breadcrumbs.delete_journal_link'); $this->rememberPreviousUri('journal_links.delete.uri'); - return view('transactions.links.delete', compact('link', 'subTitle', 'subTitleIcon')); + return prefixView('transactions.links.delete', compact('link', 'subTitle', 'subTitleIcon')); } /** @@ -107,7 +107,7 @@ class LinkController extends Controller { $linkTypes = $this->repository->get(); - return view('transactions.links.modal', compact('journal', 'linkTypes')); + return prefixView('transactions.links.modal', compact('journal', 'linkTypes')); } /** diff --git a/app/Http/Controllers/Transaction/MassController.php b/app/Http/Controllers/Transaction/MassController.php index 9260f72c22..96ecbc05b8 100644 --- a/app/Http/Controllers/Transaction/MassController.php +++ b/app/Http/Controllers/Transaction/MassController.php @@ -83,7 +83,7 @@ class MassController extends Controller // put previous url in session $this->rememberPreviousUri('transactions.mass-delete.uri'); - return view('transactions.mass.delete', compact('journals', 'subTitle')); + return prefixView('transactions.mass.delete', compact('journals', 'subTitle')); } /** @@ -154,7 +154,7 @@ class MassController extends Controller $this->rememberPreviousUri('transactions.mass-edit.uri'); - return view('transactions.mass.edit', compact('journals', 'subTitle', 'withdrawalSources', 'depositDestinations', 'budgets')); + return prefixView('transactions.mass.edit', compact('journals', 'subTitle', 'withdrawalSources', 'depositDestinations', 'budgets')); } /** diff --git a/app/Http/Controllers/Transaction/ShowController.php b/app/Http/Controllers/Transaction/ShowController.php index 9f3d1fba6f..2afcf381fb 100644 --- a/app/Http/Controllers/Transaction/ShowController.php +++ b/app/Http/Controllers/Transaction/ShowController.php @@ -109,7 +109,7 @@ class ShowController extends Controller $attachments = $this->repository->getAttachments($transactionGroup); $links = $this->repository->getLinks($transactionGroup); - return view( + return prefixView( 'transactions.show', compact( 'transactionGroup', diff --git a/app/Jobs/SendWebhookMessage.php b/app/Jobs/SendWebhookMessage.php index 3aa23faefa..e0e9b94f41 100644 --- a/app/Jobs/SendWebhookMessage.php +++ b/app/Jobs/SendWebhookMessage.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); namespace FireflyIII\Jobs; diff --git a/app/Mail/AccessTokenCreatedMail.php b/app/Mail/AccessTokenCreatedMail.php index 3837003be1..abc626f4ae 100644 --- a/app/Mail/AccessTokenCreatedMail.php +++ b/app/Mail/AccessTokenCreatedMail.php @@ -62,7 +62,7 @@ class AccessTokenCreatedMail extends Mailable */ public function build(): self { - return $this->view('emails.access-token-created-html')->text('emails.access-token-created-text') + return $this->view('v1.emails.access-token-created-html')->text('v1.emails.access-token-created-text') ->subject((string) trans('email.access_token_created_subject')); } } diff --git a/app/Mail/AdminTestMail.php b/app/Mail/AdminTestMail.php index 565672cde3..406e4cb88d 100644 --- a/app/Mail/AdminTestMail.php +++ b/app/Mail/AdminTestMail.php @@ -61,7 +61,7 @@ class AdminTestMail extends Mailable */ public function build(): self { - return $this->view('emails.admin-test-html')->text('emails.admin-test-text') + return $this->view('v1.emails.admin-test-html')->text('v1.emails.admin-test-text') ->subject((string) trans('email.admin_test_subject')); } } diff --git a/app/Mail/ConfirmEmailChangeMail.php b/app/Mail/ConfirmEmailChangeMail.php index 21f60197ca..7f8694da23 100644 --- a/app/Mail/ConfirmEmailChangeMail.php +++ b/app/Mail/ConfirmEmailChangeMail.php @@ -69,7 +69,7 @@ class ConfirmEmailChangeMail extends Mailable */ public function build(): self { - return $this->view('emails.confirm-email-change-html')->text('emails.confirm-email-change-text') + return $this->view('v1.emails.confirm-email-change-html')->text('v1.emails.confirm-email-change-text') ->subject((string) trans('email.email_change_subject')); } } diff --git a/app/Mail/NewIPAddressWarningMail.php b/app/Mail/NewIPAddressWarningMail.php index 12fee2fa47..58cf46620e 100644 --- a/app/Mail/NewIPAddressWarningMail.php +++ b/app/Mail/NewIPAddressWarningMail.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * NewIPAddressWarningMail.php @@ -62,7 +83,7 @@ class NewIPAddressWarningMail extends Mailable $this->host = $hostName; } - return $this->view('emails.new-ip-html')->text('emails.new-ip-text') + return $this->view('v1.emails.new-ip-html')->text('v1.emails.new-ip-text') ->subject((string) trans('email.login_from_new_ip')); } } diff --git a/app/Mail/OAuthTokenCreatedMail.php b/app/Mail/OAuthTokenCreatedMail.php index 1d2d4cced8..dd7b169574 100644 --- a/app/Mail/OAuthTokenCreatedMail.php +++ b/app/Mail/OAuthTokenCreatedMail.php @@ -66,7 +66,7 @@ class OAuthTokenCreatedMail extends Mailable */ public function build(): self { - return $this->view('emails.oauth-client-created-html')->text('emails.oauth-client-created-text') + return $this->view('v1.emails.oauth-client-created-html')->text('v1.emails.oauth-client-created-text') ->subject((string) trans('email.oauth_created_subject')); } } diff --git a/app/Mail/RegisteredUser.php b/app/Mail/RegisteredUser.php index 1abd4d6caa..d56180f336 100644 --- a/app/Mail/RegisteredUser.php +++ b/app/Mail/RegisteredUser.php @@ -61,6 +61,6 @@ class RegisteredUser extends Mailable */ public function build(): self { - return $this->view('emails.registered-html')->text('emails.registered-text')->subject((string) trans('email.registered_subject')); + return $this->view('v1.emails.registered-html')->text('v1.emails.registered-text')->subject((string) trans('email.registered_subject')); } } diff --git a/app/Mail/ReportNewJournalsMail.php b/app/Mail/ReportNewJournalsMail.php index 3956773a3c..36b1c3fe1c 100644 --- a/app/Mail/ReportNewJournalsMail.php +++ b/app/Mail/ReportNewJournalsMail.php @@ -40,14 +40,10 @@ class ReportNewJournalsMail extends Mailable { use Queueable, SerializesModels; - /** @var string Email address of the user */ - public $email; - /** @var Collection A collection of groups */ - public $groups; - /** @var string IP address of user (if known) */ - public $ipAddress; - /** @var array All groups, transformed to array. */ - public $transformed; + public string $email; + public Collection $groups; + public string $ipAddress; + public array $transformed; /** * ConfirmEmailChangeMail constructor. @@ -72,7 +68,7 @@ class ReportNewJournalsMail extends Mailable { $this->transform(); - return $this->view('emails.report-new-journals-html')->text('emails.report-new-journals-text') + return $this->view('v1.emails.report-new-journals-html')->text('v1.emails.report-new-journals-text') ->subject((string) trans_choice('email.new_journals_subject', $this->groups->count())); } diff --git a/app/Mail/RequestedNewPassword.php b/app/Mail/RequestedNewPassword.php index a3d2a83a9a..19833e195d 100644 --- a/app/Mail/RequestedNewPassword.php +++ b/app/Mail/RequestedNewPassword.php @@ -60,6 +60,6 @@ class RequestedNewPassword extends Mailable */ public function build(): self { - return $this->view('emails.password-html')->text('emails.password-text')->subject((string) trans('email.reset_pw_subject')); + return $this->view('v1.emails.password-html')->text('v1.emails.password-text')->subject((string) trans('email.reset_pw_subject')); } } diff --git a/app/Mail/UndoEmailChangeMail.php b/app/Mail/UndoEmailChangeMail.php index 93cc2979c4..3cdfdca396 100644 --- a/app/Mail/UndoEmailChangeMail.php +++ b/app/Mail/UndoEmailChangeMail.php @@ -67,7 +67,7 @@ class UndoEmailChangeMail extends Mailable */ public function build(): self { - return $this->view('emails.undo-email-change-html')->text('emails.undo-email-change-text') + return $this->view('v1.emails.undo-email-change-html')->text('v1.emails.undo-email-change-text') ->subject((string) trans('email.email_change_subject')); } } diff --git a/app/Models/Attachment.php b/app/Models/Attachment.php index e55d58b6d7..263f050b5b 100644 --- a/app/Models/Attachment.php +++ b/app/Models/Attachment.php @@ -52,7 +52,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property int $size * @property bool $uploaded * @property-read Model|\Eloquent $attachable - * @property-read Collection|\FireflyIII\Models\Note[] $notes + * @property Collection|\FireflyIII\Models\Note[] $notes * @property-read int|null $notes_count * @property-read User $user * @method static \Illuminate\Database\Eloquent\Builder|Attachment newModelQuery() diff --git a/app/Models/RecurrenceTransaction.php b/app/Models/RecurrenceTransaction.php index b1551f5bea..dcb1c804b0 100644 --- a/app/Models/RecurrenceTransaction.php +++ b/app/Models/RecurrenceTransaction.php @@ -74,6 +74,8 @@ use Illuminate\Support\Collection; * @method static Builder|RecurrenceTransaction withTrashed() * @method static Builder|RecurrenceTransaction withoutTrashed() * @mixin Eloquent + * @property int|null $transaction_type_id + * @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransaction whereTransactionTypeId($value) */ class RecurrenceTransaction extends Model { diff --git a/app/Models/RuleGroup.php b/app/Models/RuleGroup.php index 06bde1ba19..6bc281100d 100644 --- a/app/Models/RuleGroup.php +++ b/app/Models/RuleGroup.php @@ -47,7 +47,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property int $order * @property bool $active * @property bool $stop_processing - * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Rule[] $rules + * @property \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Rule[] $rules * @property-read int|null $rules_count * @property-read User $user * @method static \Illuminate\Database\Eloquent\Builder|RuleGroup newModelQuery() diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index da8ef8d017..5bbdd544cf 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -113,6 +113,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Query\Builder|TransactionJournal withTrashed() * @method static \Illuminate\Database\Query\Builder|TransactionJournal withoutTrashed() * @mixin Eloquent + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Location[] $locations + * @property-read int|null $locations_count */ class TransactionJournal extends Model { @@ -213,6 +215,15 @@ class TransactionJournal extends Model return $this->belongsTo(Bill::class); } + /** + * @codeCoverageIgnore + * @return MorphMany + */ + public function locations(): MorphMany + { + return $this->morphMany(Location::class, 'locatable'); + } + /** * @codeCoverageIgnore * @return BelongsToMany diff --git a/app/Models/TransactionType.php b/app/Models/TransactionType.php index 1d159ca24a..7f3f9f5c23 100644 --- a/app/Models/TransactionType.php +++ b/app/Models/TransactionType.php @@ -57,39 +57,20 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class TransactionType extends Model { use SoftDeletes; - - /** - * - */ public const WITHDRAWAL = 'Withdrawal'; - /** - * - */ public const DEPOSIT = 'Deposit'; - /** - * - */ public const TRANSFER = 'Transfer'; - /** - * - */ public const OPENING_BALANCE = 'Opening balance'; - /** - * - */ public const RECONCILIATION = 'Reconciliation'; - /** - * The attributes that should be casted to native types. - * - * @var array - */ + public const INVALID = 'Invalid'; + /** @var string[] */ protected $casts = [ 'created_at' => 'datetime', 'updated_at' => 'datetime', 'deleted_at' => 'datetime', ]; - /** @var array Fields that can be filled */ + /** @var string[] */ protected $fillable = ['type']; /** diff --git a/app/Models/Webhook.php b/app/Models/Webhook.php index 2281de76c5..18705d949d 100644 --- a/app/Models/Webhook.php +++ b/app/Models/Webhook.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * Webhook.php @@ -63,6 +84,10 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Query\Builder|Webhook withTrashed() * @method static \Illuminate\Database\Query\Builder|Webhook withoutTrashed() * @mixin \Eloquent + * @property string $title + * @property string $secret + * @method static \Illuminate\Database\Eloquent\Builder|Webhook whereSecret($value) + * @method static \Illuminate\Database\Eloquent\Builder|Webhook whereTitle($value) */ class Webhook extends Model { @@ -103,11 +128,11 @@ class Webhook extends Model public static function routeBinder(string $value): Webhook { if (auth()->check()) { - $budgetId = (int)$value; + $webhookId = (int)$value; /** @var User $user */ $user = auth()->user(); /** @var Webhook $webhook */ - $webhook = $user->webhooks()->find($budgetId); + $webhook = $user->webhooks()->find($webhookId); if (null !== $webhook) { return $webhook; } diff --git a/app/Models/WebhookAttempt.php b/app/Models/WebhookAttempt.php index d4523dc0e6..2fc8420963 100644 --- a/app/Models/WebhookAttempt.php +++ b/app/Models/WebhookAttempt.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * WebhookAttempt.php @@ -23,14 +44,43 @@ declare(strict_types=1); namespace FireflyIII\Models; +use FireflyIII\User; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\SoftDeletes; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class WebhookAttempt + * + * @property int $id + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property string|null $deleted_at + * @property int $webhook_message_id + * @property int $status_code + * @property string|null $logs + * @property string|null $response + * @property-read \FireflyIII\Models\WebhookMessage $webhookMessage + * @method static \Illuminate\Database\Eloquent\Builder|WebhookAttempt newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|WebhookAttempt newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|WebhookAttempt query() + * @method static \Illuminate\Database\Eloquent\Builder|WebhookAttempt whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|WebhookAttempt whereDeletedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|WebhookAttempt whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|WebhookAttempt whereLogs($value) + * @method static \Illuminate\Database\Eloquent\Builder|WebhookAttempt whereResponse($value) + * @method static \Illuminate\Database\Eloquent\Builder|WebhookAttempt whereStatusCode($value) + * @method static \Illuminate\Database\Eloquent\Builder|WebhookAttempt whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|WebhookAttempt whereWebhookMessageId($value) + * @mixin \Eloquent + * @method static \Illuminate\Database\Query\Builder|WebhookAttempt onlyTrashed() + * @method static \Illuminate\Database\Query\Builder|WebhookAttempt withTrashed() + * @method static \Illuminate\Database\Query\Builder|WebhookAttempt withoutTrashed() */ class WebhookAttempt extends Model { + use SoftDeletes; /** * @codeCoverageIgnore * @return BelongsTo @@ -39,4 +89,29 @@ class WebhookAttempt extends Model { return $this->belongsTo(WebhookMessage::class); } + + /** + * Route binder. Converts the key in the URL to the specified object (or throw 404). + * + * @param string $value + * + * @return WebhookAttempt + * @throws NotFoundHttpException + */ + public static function routeBinder(string $value): WebhookAttempt + { + if (auth()->check()) { + $attemptId = (int)$value; + /** @var User $user */ + $user = auth()->user(); + /** @var WebhookAttempt $attempt */ + $attempt = self::find($attemptId); + if (null !== $attempt) { + if($attempt->webhookMessage->webhook->user_id === $user->id) { + return $attempt; + } + } + } + throw new NotFoundHttpException; + } } diff --git a/app/Models/WebhookMessage.php b/app/Models/WebhookMessage.php index 0b3704cd21..a05653866c 100644 --- a/app/Models/WebhookMessage.php +++ b/app/Models/WebhookMessage.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * WebhookMessage.php @@ -23,9 +44,11 @@ declare(strict_types=1); namespace FireflyIII\Models; +use FireflyIII\User; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * FireflyIII\Models\WebhookMessage @@ -57,6 +80,8 @@ use Illuminate\Database\Eloquent\Relations\HasMany; * @method static \Illuminate\Database\Eloquent\Builder|WebhookMessage whereUuid($value) * @method static \Illuminate\Database\Eloquent\Builder|WebhookMessage whereWebhookId($value) * @mixin \Eloquent + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\WebhookAttempt[] $webhookAttempts + * @property-read int|null $webhook_attempts_count */ class WebhookMessage extends Model { @@ -70,6 +95,31 @@ class WebhookMessage extends Model 'logs' => 'json', ]; + /** + * Route binder. Converts the key in the URL to the specified object (or throw 404). + * + * @param string $value + * + * @return WebhookMessage + * @throws NotFoundHttpException + */ + public static function routeBinder(string $value): WebhookMessage + { + if (auth()->check()) { + $messageId = (int)$value; + /** @var User $user */ + $user = auth()->user(); + /** @var WebhookMessage $message */ + $message = self::find($messageId); + if (null !== $message) { + if($message->webhook->user_id === $user->id) { + return $message; + } + } + } + throw new NotFoundHttpException; + } + /** * @codeCoverageIgnore * @return BelongsTo diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 9ac1181564..e1e2fd2456 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -32,10 +32,8 @@ use FireflyIII\Events\RequestedReportOnJournals; use FireflyIII\Events\RequestedSendWebhookMessages; use FireflyIII\Events\RequestedVersionCheckStatus; use FireflyIII\Events\StoredTransactionGroup; -use FireflyIII\Events\StoredWebhookMessage; use FireflyIII\Events\UpdatedTransactionGroup; use FireflyIII\Events\UserChangedEmail; -use FireflyIII\Handlers\Events\SendEmailVerificationNotification; use FireflyIII\Mail\OAuthTokenCreatedMail; use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBankRepetition; diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index 666315aa6d..e3e7741cc4 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -37,8 +37,8 @@ use FireflyIII\Models\TransactionType; use FireflyIII\Services\Internal\Destroy\AccountDestroyService; use FireflyIII\Services\Internal\Update\AccountUpdateService; use FireflyIII\User; +use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Relations\HasMany; -use \Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Support\Collection; use Log; use Storage; @@ -193,7 +193,7 @@ class AccountRepository implements AccountRepositoryInterface */ public function getAccountCurrency(Account $account): ?TransactionCurrency { - $currencyId = (int) $this->getMetaValue($account, 'currency_id'); + $currencyId = (int)$this->getMetaValue($account, 'currency_id'); if ($currencyId > 0) { return TransactionCurrency::find($currencyId); } @@ -245,7 +245,10 @@ class AccountRepository implements AccountRepositoryInterface if (!empty($types)) { $query->accountTypeIn($types); } - $query->orderBy('accounts.order', 'ASC'); + $res = array_intersect([AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT], $types); + if (0 !== count($res)) { + $query->orderBy('accounts.order', 'ASC'); + } $query->orderBy('accounts.active', 'DESC'); $query->orderBy('accounts.name', 'ASC'); @@ -277,6 +280,27 @@ class AccountRepository implements AccountRepositoryInterface return $query->get(['accounts.*']); } + /** + * @inheritDoc + */ + public function getAttachments(Account $account): Collection + { + $set = $account->attachments()->get(); + + /** @var Storage $disk */ + $disk = Storage::disk('upload'); + + return $set->each( + static function (Attachment $attachment) use ($disk) { + $notes = $attachment->notes()->first(); + $attachment->file_exists = $disk->exists($attachment->fileName()); + $attachment->notes = $notes ? $notes->text : ''; + + return $attachment; + } + ); + } + /** * @return Account * @@ -293,6 +317,38 @@ class AccountRepository implements AccountRepositoryInterface return $factory->findOrCreate('Cash account', $type->type); } + /** + * @param array $types + * + * @return Collection + */ + public function getInactiveAccountsByType(array $types): Collection + { + /** @var Collection $result */ + $query = $this->user->accounts()->with( + ['accountmeta' => function (HasMany $query) { + $query->where('name', 'account_role'); + }] + ); + if (!empty($types)) { + $query->accountTypeIn($types); + } + $query->where('active', 0); + $query->orderBy('accounts.account_type_id', 'ASC'); + $query->orderBy('accounts.order', 'ASC'); + $query->orderBy('accounts.name', 'ASC'); + + return $query->get(['accounts.*']); + } + + /** + * @inheritDoc + */ + public function getLocation(Account $account): ?Location + { + return $account->locations()->first(); + } + /** * Return meta value for account. Null if not found. * @@ -303,15 +359,18 @@ class AccountRepository implements AccountRepositoryInterface */ public function getMetaValue(Account $account, string $field): ?string { - $result = $account->accountMeta->filter(function (AccountMeta $meta) use ($field) { - return strtolower($meta->name) === strtolower($field); - }); + $result = $account->accountMeta->filter( + function (AccountMeta $meta) use ($field) { + return strtolower($meta->name) === strtolower($field); + } + ); if (0 === $result->count()) { return null; } if (1 === $result->count()) { - return (string) $result->first()->data; + return (string)$result->first()->data; } + return null; } @@ -369,7 +428,7 @@ class AccountRepository implements AccountRepositoryInterface return null; } - return (string) $transaction->amount; + return (string)$transaction->amount; } /** @@ -460,6 +519,22 @@ class AccountRepository implements AccountRepositoryInterface return $factory->create($data); } + /** + * @inheritDoc + */ + public function getUsedCurrencies(Account $account): Collection + { + $info = $account->transactions()->get(['transaction_currency_id', 'foreign_currency_id'])->toArray(); + $currencyIds = []; + foreach ($info as $entry) { + $currencyIds[] = (int)$entry['transaction_currency_id']; + $currencyIds[] = (int)$entry['foreign_currency_id']; + } + $currencyIds = array_unique($currencyIds); + + return TransactionCurrency::whereIn('id', $currencyIds)->get(); + } + /** * @param Account $account * @@ -487,7 +562,7 @@ class AccountRepository implements AccountRepositoryInterface ->orderBy('transaction_journals.id', 'ASC') ->first(['transaction_journals.id']); if (null !== $first) { - return TransactionJournal::find((int) $first->id); + return TransactionJournal::find((int)$first->id); } return null; @@ -511,6 +586,33 @@ class AccountRepository implements AccountRepositoryInterface return $result; } + /** + * @inheritDoc + */ + public function resetAccountOrder(): void + { + $sets = [ + [AccountType::DEFAULT, AccountType::ASSET], + //[AccountType::EXPENSE, AccountType::BENEFICIARY], + //[AccountType::REVENUE], + [AccountType::LOAN, AccountType::DEBT, AccountType::CREDITCARD, AccountType::MORTGAGE], + //[AccountType::CASH, AccountType::INITIAL_BALANCE, AccountType::IMPORT, AccountType::RECONCILIATION], + ]; + foreach ($sets as $set) { + Log::debug('Now in resetAccountOrder', $set); + $list = $this->getAccountsByType($set); + $index = 1; + foreach ($list as $account) { + if ($index !== (int)$account->order) { + Log::debug(sprintf('Account #%d ("%s"): order should %d be but is %d.', $account->id, $account->name, $index, $account->order)); + $account->order = $index; + $account->save(); + } + $index++; + } + } + } + /** * @param string $query * @param array $types @@ -543,6 +645,44 @@ class AccountRepository implements AccountRepositoryInterface return $dbQuery->take($limit)->get(['accounts.*']); } + /** + * @inheritDoc + */ + public function searchAccountNr(string $query, array $types, int $limit): Collection + { + $dbQuery = $this->user->accounts()->distinct() + ->leftJoin('account_meta', 'accounts.id', 'account_meta.account_id') + ->where('accounts.active', 1) + ->orderBy('accounts.order', 'ASC') + ->orderBy('accounts.account_type_id', 'ASC') + ->orderBy('accounts.name', 'ASC') + ->with(['accountType', 'accountMeta']); + if ('' !== $query) { + // split query on spaces just in case: + $parts = explode(' ', $query); + foreach ($parts as $part) { + $search = sprintf('%%%s%%', $part); + $dbQuery->where( + function (EloquentBuilder $q1) use ($search) { + $q1->where('accounts.iban', 'LIKE', $search); + $q1->orWhere( + function (EloquentBuilder $q2) use ($search) { + $q2->where('account_meta.name', '=', 'account_number'); + $q2->where('account_meta.data', 'LIKE', $search); + } + ); + } + ); + } + } + if (!empty($types)) { + $dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); + $dbQuery->whereIn('account_types.type', $types); + } + + return $dbQuery->take($limit)->get(['accounts.*']); + } + /** * @param User $user */ @@ -577,125 +717,34 @@ class AccountRepository implements AccountRepositoryInterface { /** @var AccountUpdateService $service */ $service = app(AccountUpdateService::class); + return $service->update($account, $data); } - /** - * @param array $types - * - * @return Collection - */ - public function getInactiveAccountsByType(array $types): Collection - { - /** @var Collection $result */ - $query = $this->user->accounts()->with( - ['accountmeta' => function (HasMany $query) { - $query->where('name', 'account_role'); - }] - ); - if (!empty($types)) { - $query->accountTypeIn($types); - } - $query->where('active', 0); - $query->orderBy('accounts.account_type_id', 'ASC'); - $query->orderBy('accounts.order', 'ASC'); - $query->orderBy('accounts.name', 'ASC'); - - return $query->get(['accounts.*']); - } - /** * @inheritDoc */ - public function getLocation(Account $account): ?Location + public function maxOrder(string $type): int { - return $account->locations()->first(); - } + $sets = [ + AccountType::ASSET => [AccountType::DEFAULT, AccountType::ASSET], + AccountType::EXPENSE => [AccountType::EXPENSE, AccountType::BENEFICIARY], + AccountType::REVENUE => [AccountType::REVENUE], + AccountType::LOAN => [AccountType::LOAN, AccountType::DEBT, AccountType::CREDITCARD, AccountType::MORTGAGE], + AccountType::DEBT => [AccountType::LOAN, AccountType::DEBT, AccountType::CREDITCARD, AccountType::MORTGAGE], + AccountType::MORTGAGE => [AccountType::LOAN, AccountType::DEBT, AccountType::CREDITCARD, AccountType::MORTGAGE], + ]; + if (array_key_exists(ucfirst($type), $sets)) { + $order = (int)$this->getAccountsByType($sets[ucfirst($type)])->max('order'); + Log::debug(sprintf('Return max order of "%s" set: %d', $type, $order)); - /** - * @inheritDoc - */ - public function getAttachments(Account $account): Collection - { - $set = $account->attachments()->get(); - - /** @var Storage $disk */ - $disk = Storage::disk('upload'); - - return $set->each( - static function (Attachment $attachment) use ($disk) { - $notes = $attachment->notes()->first(); - $attachment->file_exists = $disk->exists($attachment->fileName()); - $attachment->notes = $notes ? $notes->text : ''; - - return $attachment; - } - ); - } - - /** - * @inheritDoc - */ - public function getUsedCurrencies(Account $account): Collection - { - $info = $account->transactions()->get(['transaction_currency_id', 'foreign_currency_id'])->toArray(); - $currencyIds = []; - foreach ($info as $entry) { - $currencyIds[] = (int) $entry['transaction_currency_id']; - $currencyIds[] = (int) $entry['foreign_currency_id']; + return $order; } - $currencyIds = array_unique($currencyIds); + $specials = [AccountType::CASH, AccountType::INITIAL_BALANCE, AccountType::IMPORT, AccountType::RECONCILIATION]; - return TransactionCurrency::whereIn('id', $currencyIds)->get(); - } + $order = (int)$this->getAccountsByType($specials)->max('order'); + Log::debug(sprintf('Return max order of "%s" set (specials!): %d', $type, $order)); - /** - * @inheritDoc - */ - public function resetAccountOrder(array $types): void - { - $list = $this->getAccountsByType($types); - /** - * @var int $index - * @var Account $account - */ - foreach ($list as $index => $account) { - $account->order = $index + 1; - $account->save(); - } - } - - /** - * @inheritDoc - */ - public function searchAccountNr(string $query, array $types, int $limit): Collection - { - $dbQuery = $this->user->accounts()->distinct() - ->leftJoin('account_meta', 'accounts.id', 'account_meta.account_id') - ->where('accounts.active', 1) - ->orderBy('accounts.order', 'ASC') - ->orderBy('accounts.account_type_id', 'ASC') - ->orderBy('accounts.name', 'ASC') - ->with(['accountType', 'accountMeta']); - if ('' !== $query) { - // split query on spaces just in case: - $parts = explode(' ', $query); - foreach ($parts as $part) { - $search = sprintf('%%%s%%', $part); - $dbQuery->where(function (EloquentBuilder $q1) use ($search) { - $q1->where('accounts.iban', 'LIKE', $search); - $q1->orWhere(function (EloquentBuilder $q2) use ($search) { - $q2->where('account_meta.name', '=', 'account_number'); - $q2->where('account_meta.data', 'LIKE', $search); - }); - }); - } - } - if (!empty($types)) { - $dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); - $dbQuery->whereIn('account_types.type', $types); - } - - return $dbQuery->take($limit)->get(['accounts.*']); + return $order; } } diff --git a/app/Repositories/Account/AccountRepositoryInterface.php b/app/Repositories/Account/AccountRepositoryInterface.php index 0befebe3de..a26e9a1afe 100644 --- a/app/Repositories/Account/AccountRepositoryInterface.php +++ b/app/Repositories/Account/AccountRepositoryInterface.php @@ -48,39 +48,16 @@ interface AccountRepositoryInterface public function count(array $types): int; /** - * Reset order types of the mentioned accounts. + * @param string $type * - * @param array $types + * @return int */ - public function resetAccountOrder(array $types): void; - - /** - * @param Account $account - * - * @return Collection - */ - public function getUsedCurrencies(Account $account): Collection; - - /** - * @param Account $account - * - * @return Collection - */ - public function getAttachments(Account $account): Collection; - - /** - * Get account location, if any. - * - * @param Account $account - * - * @return Location|null - */ - public function getLocation(Account $account): ?Location; + public function maxOrder(string $type): int; /** * Moved here from account CRUD. * - * @param Account $account + * @param Account $account * @param Account|null $moveTo * * @return bool @@ -98,7 +75,7 @@ interface AccountRepositoryInterface /** * @param string $iban - * @param array $types + * @param array $types * * @return Account|null */ @@ -106,7 +83,7 @@ interface AccountRepositoryInterface /** * @param string $name - * @param array $types + * @param array $types * * @return Account|null */ @@ -157,22 +134,38 @@ interface AccountRepositoryInterface public function getActiveAccountsByType(array $types): Collection; /** - * @param array $types + * @param Account $account * * @return Collection */ - public function getInactiveAccountsByType(array $types): Collection; + public function getAttachments(Account $account): Collection; /** * @return Account */ public function getCashAccount(): Account; + /** + * @param array $types + * + * @return Collection + */ + public function getInactiveAccountsByType(array $types): Collection; + + /** + * Get account location, if any. + * + * @param Account $account + * + * @return Location|null + */ + public function getLocation(Account $account): ?Location; + /** * Return meta value for account. Null if not found. * * @param Account $account - * @param string $field + * @param string $field * * @return null|string */ @@ -236,6 +229,12 @@ interface AccountRepositoryInterface */ public function getReconciliation(Account $account): ?Account; + /** + * @param Account $account + * + * @return Collection + */ + public function getUsedCurrencies(Account $account): Collection; /** * @param Account $account @@ -244,7 +243,6 @@ interface AccountRepositoryInterface */ public function isLiability(Account $account): bool; - /** * Returns the date of the very first transaction in this account. * @@ -263,10 +261,15 @@ interface AccountRepositoryInterface */ public function oldestJournalDate(Account $account): ?Carbon; + /** + * Reset order types of the mentioned accounts. + */ + public function resetAccountOrder(): void; + /** * @param string $query - * @param array $types - * @param int $limit + * @param array $types + * @param int $limit * * @return Collection */ @@ -274,8 +277,8 @@ interface AccountRepositoryInterface /** * @param string $query - * @param array $types - * @param int $limit + * @param array $types + * @param int $limit * * @return Collection */ @@ -295,7 +298,7 @@ interface AccountRepositoryInterface /** * @param Account $account - * @param array $data + * @param array $data * * @return Account */ diff --git a/app/Repositories/Account/AccountTaskerInterface.php b/app/Repositories/Account/AccountTaskerInterface.php index 4e96ddf417..cc6f68eed7 100644 --- a/app/Repositories/Account/AccountTaskerInterface.php +++ b/app/Repositories/Account/AccountTaskerInterface.php @@ -33,16 +33,16 @@ interface AccountTaskerInterface { /** * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end + * @param Carbon $start + * @param Carbon $end * * @return array */ public function getAccountReport(Collection $accounts, Carbon $start, Carbon $end): array; /** - * @param Carbon $start - * @param Carbon $end + * @param Carbon $start + * @param Carbon $end * @param Collection $accounts * * @return array @@ -50,8 +50,8 @@ interface AccountTaskerInterface public function getExpenseReport(Carbon $start, Carbon $end, Collection $accounts): array; /** - * @param Carbon $start - * @param Carbon $end + * @param Carbon $start + * @param Carbon $end * @param Collection $accounts * * @return array diff --git a/app/Repositories/Account/OperationsRepository.php b/app/Repositories/Account/OperationsRepository.php index 1c39fe31d5..13359b22b4 100644 --- a/app/Repositories/Account/OperationsRepository.php +++ b/app/Repositories/Account/OperationsRepository.php @@ -24,12 +24,11 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Account; use Carbon\Carbon; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; +use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionType; use FireflyIII\User; use Illuminate\Support\Collection; -use Log; /** * @@ -37,8 +36,7 @@ use Log; */ class OperationsRepository implements OperationsRepositoryInterface { - /** @var User */ - private $user; + private User $user; /** * This method returns a list of all the withdrawal transaction journals (as arrays) set in that period @@ -161,30 +159,224 @@ class OperationsRepository implements OperationsRepositoryInterface } /** - * Sum of withdrawal journals in period for a set of accounts, grouped per currency. Amounts are always negative. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * - * @return array + * @inheritDoc */ - public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null): array - { - throw new FireflyException(sprintf('%s is not yet implemented', __METHOD__)); + public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $expense = null, ?TransactionCurrency $currency = null + ): array { + $start->startOfDay(); + $end->endOfDay(); + + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector + ->setUser($this->user) + ->setRange($start, $end) + ->setTypes([TransactionType::WITHDRAWAL]); + + if (null !== $accounts) { + $collector->setSourceAccounts($accounts); + } + if (null !== $expense) { + $collector->setDestinationAccounts($expense); + } + if (null !== $currency) { + $collector->setCurrency($currency); + } + $journals = $collector->getExtractedJournals(); + + // same but for foreign currencies: + if (null !== $currency) { + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]) + ->setForeignCurrency($currency); + + if (null !== $accounts) { + $collector->setSourceAccounts($accounts); + } + if (null !== $expense) { + $collector->setDestinationAccounts($expense); + } + + $result = $collector->getExtractedJournals(); + + // do not use array_merge because you want keys to overwrite (otherwise you get double results): + $journals = $result + $journals; + } + $array = []; + + foreach ($journals as $journal) { + $currencyId = (int)$journal['currency_id']; + $array[$currencyId] = $array[$currencyId] ?? [ + 'sum' => '0', + 'currency_id' => $currencyId, + 'currency_name' => $journal['currency_name'], + 'currency_symbol' => $journal['currency_symbol'], + 'currency_code' => $journal['currency_code'], + 'currency_decimal_places' => $journal['currency_decimal_places'], + ]; + $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->negative($journal['amount'])); + + // also do foreign amount: + $foreignId = (int)$journal['foreign_currency_id']; + if (0 !== $foreignId) { + $array[$foreignId] = $array[$foreignId] ?? [ + 'sum' => '0', + 'currency_id' => $foreignId, + 'currency_name' => $journal['foreign_currency_name'], + 'currency_symbol' => $journal['foreign_currency_symbol'], + 'currency_code' => $journal['foreign_currency_code'], + 'currency_decimal_places' => $journal['foreign_currency_decimal_places'], + ]; + $array[$foreignId]['sum'] = bcadd($array[$foreignId]['sum'], app('steam')->negative($journal['foreign_amount'])); + } + } + + return $array; } /** - * Sum of income journals in period for a set of accounts, grouped per currency. Amounts are always positive. + * // TODO same as income but copied. * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * - * @return array + * @inheritDoc */ - public function sumIncome(Carbon $start, Carbon $end, ?Collection $accounts = null): array + public function sumIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $revenue = null, ?TransactionCurrency $currency = null + ): array { + $start->startOfDay(); + $end->endOfDay(); + + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector + ->setUser($this->user) + ->setRange($start, $end) + ->setTypes([TransactionType::DEPOSIT]); + + if (null !== $accounts) { + $collector->setDestinationAccounts($accounts); + } + if (null !== $revenue) { + $collector->setSourceAccounts($revenue); + } + if (null !== $currency) { + $collector->setCurrency($currency); + } + $journals = $collector->getExtractedJournals(); + + // same but for foreign currencies: + if (null !== $currency) { + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]) + ->setForeignCurrency($currency); + + if (null !== $accounts) { + $collector->setDestinationAccounts($accounts); + } + if (null !== $revenue) { + $collector->setSourceAccounts($revenue); + } + $result = $collector->getExtractedJournals(); + + // do not use array_merge because you want keys to overwrite (otherwise you get double results): + $journals = $result + $journals; + } + $array = []; + + foreach ($journals as $journal) { + $currencyId = (int)$journal['currency_id']; + $array[$currencyId] = $array[$currencyId] ?? [ + 'sum' => '0', + 'currency_id' => $currencyId, + 'currency_name' => $journal['currency_name'], + 'currency_symbol' => $journal['currency_symbol'], + 'currency_code' => $journal['currency_code'], + 'currency_decimal_places' => $journal['currency_decimal_places'], + ]; + $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->positive($journal['amount'])); + + // also do foreign amount: + $foreignId = (int)$journal['foreign_currency_id']; + if (0 !== $foreignId) { + $array[$foreignId] = $array[$foreignId] ?? [ + 'sum' => '0', + 'currency_id' => $foreignId, + 'currency_name' => $journal['foreign_currency_name'], + 'currency_symbol' => $journal['foreign_currency_symbol'], + 'currency_code' => $journal['foreign_currency_code'], + 'currency_decimal_places' => $journal['foreign_currency_decimal_places'], + ]; + $array[$foreignId]['sum'] = bcadd($array[$foreignId]['sum'], app('steam')->positive($journal['foreign_amount'])); + } + } + + return $array; + } + + /** + * @inheritDoc + */ + public function sumTransfers(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array { - throw new FireflyException(sprintf('%s is not yet implemented', __METHOD__)); + $start->startOfDay(); + $end->endOfDay(); + + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::TRANSFER]); + + if (null !== $accounts) { + $collector->setAccounts($accounts); + } + if (null !== $currency) { + $collector->setCurrency($currency); + } + $journals = $collector->getExtractedJournals(); + + // same but for foreign currencies: + if (null !== $currency) { + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]) + ->setForeignCurrency($currency); + + if (null !== $accounts) { + $collector->setAccounts($accounts); + } + $result = $collector->getExtractedJournals(); + + // do not use array_merge because you want keys to overwrite (otherwise you get double results): + $journals = $result + $journals; + } + $array = []; + + foreach ($journals as $journal) { + $currencyId = (int)$journal['currency_id']; + $array[$currencyId] = $array[$currencyId] ?? [ + 'sum' => '0', + 'currency_id' => $currencyId, + 'currency_name' => $journal['currency_name'], + 'currency_symbol' => $journal['currency_symbol'], + 'currency_code' => $journal['currency_code'], + 'currency_decimal_places' => $journal['currency_decimal_places'], + ]; + $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->positive($journal['amount'])); + + // also do foreign amount: + $foreignId = (int)$journal['foreign_currency_id']; + if (0 !== $foreignId) { + $array[$foreignId] = $array[$foreignId] ?? [ + 'sum' => '0', + 'currency_id' => $foreignId, + 'currency_name' => $journal['foreign_currency_name'], + 'currency_symbol' => $journal['foreign_currency_symbol'], + 'currency_code' => $journal['foreign_currency_code'], + 'currency_decimal_places' => $journal['foreign_currency_decimal_places'], + ]; + $array[$foreignId]['sum'] = bcadd($array[$foreignId]['sum'], app('steam')->positive($journal['foreign_amount'])); + } + } + + return $array; } } diff --git a/app/Repositories/Account/OperationsRepositoryInterface.php b/app/Repositories/Account/OperationsRepositoryInterface.php index 04a5c214f9..6e4f0d94a4 100644 --- a/app/Repositories/Account/OperationsRepositoryInterface.php +++ b/app/Repositories/Account/OperationsRepositoryInterface.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Account; use Carbon\Carbon; +use FireflyIII\Models\TransactionCurrency; use FireflyIII\User; use Illuminate\Support\Collection; @@ -37,8 +38,8 @@ interface OperationsRepositoryInterface * which have the specified accounts. It's grouped per currency, with as few details in the array * as possible. Amounts are always negative. * - * @param Carbon $start - * @param Carbon $end + * @param Carbon $start + * @param Carbon $end * @param Collection $accounts * * @return array @@ -66,22 +67,40 @@ interface OperationsRepositoryInterface /** * Sum of withdrawal journals in period for a set of accounts, grouped per currency. Amounts are always negative. * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts + * @param Carbon $start + * @param Carbon $end + * @param Collection|null $accounts + * @param Collection|null $expense + * @param TransactionCurrency|null $currency * * @return array */ - public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null): array; + public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $expense = null, ?TransactionCurrency $currency = null + ): array; /** * Sum of income journals in period for a set of accounts, grouped per currency. Amounts are always positive. * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts + * @param Carbon $start + * @param Carbon $end + * @param Collection|null $accounts + * @param Collection|null $revenue + * @param TransactionCurrency|null $currency * * @return array */ - public function sumIncome(Carbon $start, Carbon $end, ?Collection $accounts = null): array; + public function sumIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $revenue = null, ?TransactionCurrency $currency = null + ): array; + + /** + * Sum of transfers in period for a set of accounts, grouped per currency. Amounts are always positive. + * + * @param Carbon $start + * @param Carbon $end + * @param Collection|null $accounts + * @param TransactionCurrency|null $currency + * + * @return array + */ + public function sumTransfers(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array; } diff --git a/app/Repositories/Attachment/AttachmentRepository.php b/app/Repositories/Attachment/AttachmentRepository.php index 1518bdaa6c..5b791637ed 100644 --- a/app/Repositories/Attachment/AttachmentRepository.php +++ b/app/Repositories/Attachment/AttachmentRepository.php @@ -49,7 +49,7 @@ class AttachmentRepository implements AttachmentRepositoryInterface * @param Attachment $attachment * * @return bool - * @throws \Exception + * @throws Exception */ public function destroy(Attachment $attachment): bool { @@ -111,7 +111,7 @@ class AttachmentRepository implements AttachmentRepositoryInterface try { $unencryptedContent = Crypt::decrypt($encryptedContent); // verified } catch (DecryptException $e) { - $unencryptedContent = $encryptedContent; + $unencryptedContent = $encryptedContent; } } @@ -170,14 +170,19 @@ class AttachmentRepository implements AttachmentRepositoryInterface */ public function update(Attachment $attachment, array $data): Attachment { - $attachment->title = $data['title']; + if (array_key_exists('title', $data)) { + $attachment->title = $data['title']; + } - // update filename, if present and different: - if (isset($data['filename']) && '' !== $data['filename'] && $data['filename'] !== $attachment->filename) { - $attachment->filename = $data['filename']; + if (array_key_exists('filename', $data)) { + if ('' !== (string)$data['filename'] && $data['filename'] !== $attachment->filename) { + $attachment->filename = $data['filename']; + } } $attachment->save(); - $this->updateNote($attachment, $data['notes'] ?? ''); + if (array_key_exists('notes', $data)) { + $this->updateNote($attachment, (string)$data['notes']); + } return $attachment; } diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index 9d15e78752..fd6b073fb5 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -53,6 +53,22 @@ class BillRepository implements BillRepositoryInterface private User $user; + /** + * Correct order of piggies in case of issues. + */ + public function correctOrder(): void + { + $set = $this->user->bills()->orderBy('order', 'ASC')->get(); + $current = 1; + foreach ($set as $bill) { + if ((int)$bill->order !== $current) { + $bill->order = $current; + $bill->save(); + } + $current++; + } + } + /** * @param Bill $bill * @@ -69,6 +85,14 @@ class BillRepository implements BillRepositoryInterface return true; } + /** + * @inheritDoc + */ + public function destroyAll(): void + { + $this->user->bills()->delete(); + } + /** * Find a bill by ID. * @@ -439,7 +463,7 @@ class BillRepository implements BillRepositoryInterface */ public function getPaidDatesInRange(Bill $bill, Carbon $start, Carbon $end): Collection { - Log::debug('Now in getPaidDatesInRange()'); + //Log::debug('Now in getPaidDatesInRange()'); return $bill->transactionJournals() ->before($end)->after($start)->get( @@ -463,24 +487,21 @@ class BillRepository implements BillRepositoryInterface { $set = new Collection; $currentStart = clone $start; - Log::debug(sprintf('Now at bill "%s" (%s)', $bill->name, $bill->repeat_freq)); - Log::debug(sprintf('First currentstart is %s', $currentStart->format('Y-m-d'))); + //Log::debug(sprintf('Now at bill "%s" (%s)', $bill->name, $bill->repeat_freq)); + //Log::debug(sprintf('First currentstart is %s', $currentStart->format('Y-m-d'))); while ($currentStart <= $end) { Log::debug(sprintf('Currentstart is now %s.', $currentStart->format('Y-m-d'))); $nextExpectedMatch = $this->nextDateMatch($bill, $currentStart); - Log::debug(sprintf('Next Date match after %s is %s', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d'))); + //Log::debug(sprintf('Next Date match after %s is %s', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d'))); if ($nextExpectedMatch > $end) {// If nextExpectedMatch is after end, we continue - Log::debug( - sprintf('nextExpectedMatch %s is after %s, so we skip this bill now.', $nextExpectedMatch->format('Y-m-d'), $end->format('Y-m-d')) - ); break; } $set->push(clone $nextExpectedMatch); - Log::debug(sprintf('Now %d dates in set.', $set->count())); + //Log::debug(sprintf('Now %d dates in set.', $set->count())); $nextExpectedMatch->addDay(); - Log::debug(sprintf('Currentstart (%s) has become %s.', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d'))); + //Log::debug(sprintf('Currentstart (%s) has become %s.', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d'))); $currentStart = clone $nextExpectedMatch; } @@ -489,7 +510,8 @@ class BillRepository implements BillRepositoryInterface return $date->format('Y-m-d'); } ); - Log::debug(sprintf('Found dates between %s and %s:', $start->format('Y-m-d'), $end->format('Y-m-d')), $simple->toArray()); + + //Log::debug(sprintf('Found dates between %s and %s:', $start->format('Y-m-d'), $end->format('Y-m-d')), $simple->toArray()); return $set; } @@ -617,7 +639,7 @@ class BillRepository implements BillRepositoryInterface * @param Bill $bill * @param Carbon $date * - * @return \Carbon\Carbon + * @return Carbon */ public function nextDateMatch(Bill $bill, Carbon $date): Carbon { @@ -637,8 +659,8 @@ class BillRepository implements BillRepositoryInterface $end = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip); - Log::debug('nextDateMatch: Final start is ' . $start->format('Y-m-d')); - Log::debug('nextDateMatch: Matching end is ' . $end->format('Y-m-d')); + //Log::debug('nextDateMatch: Final start is ' . $start->format('Y-m-d')); + //Log::debug('nextDateMatch: Matching end is ' . $end->format('Y-m-d')); $cache->store($start); @@ -691,6 +713,16 @@ class BillRepository implements BillRepositoryInterface return $start; } + /** + * @inheritDoc + */ + public function removeObjectGroup(Bill $bill): Bill + { + $bill->objectGroups()->sync([]); + + return $bill; + } + /** * @param string $query * @param int $limit @@ -704,6 +736,28 @@ class BillRepository implements BillRepositoryInterface return $this->user->bills()->where('name', 'LIKE', $query)->take($limit)->get(); } + /** + * @inheritDoc + */ + public function setObjectGroup(Bill $bill, string $objectGroupTitle): Bill + { + $objectGroup = $this->findOrCreateObjectGroup($objectGroupTitle); + if (null !== $objectGroup) { + $bill->objectGroups()->sync([$objectGroup->id]); + } + + return $bill; + } + + /** + * @inheritDoc + */ + public function setOrder(Bill $bill, int $order): void + { + $bill->order = $order; + $bill->save(); + } + /** * @param User $user */ @@ -727,6 +781,14 @@ class BillRepository implements BillRepositoryInterface return $factory->create($data); } + /** + * @param Bill $bill + */ + public function unlinkAll(Bill $bill): void + { + $this->user->transactionJournals()->where('bill_id', $bill->id)->update(['bill_id' => null]); + } + /** * @param Bill $bill * @param array $data @@ -740,68 +802,4 @@ class BillRepository implements BillRepositoryInterface return $service->update($bill, $data); } - - /** - * @param Bill $bill - */ - public function unlinkAll(Bill $bill): void - { - $this->user->transactionJournals()->where('bill_id', $bill->id)->update(['bill_id' => null]); - } - - /** - * Correct order of piggies in case of issues. - */ - public function correctOrder(): void - { - $set = $this->user->bills()->orderBy('order', 'ASC')->get(); - $current = 1; - foreach ($set as $bill) { - if ((int)$bill->order !== $current) { - $bill->order = $current; - $bill->save(); - } - $current++; - } - } - - /** - * @inheritDoc - */ - public function setObjectGroup(Bill $bill, string $objectGroupTitle): Bill - { - $objectGroup = $this->findOrCreateObjectGroup($objectGroupTitle); - if (null !== $objectGroup) { - $bill->objectGroups()->sync([$objectGroup->id]); - } - - return $bill; - } - - /** - * @inheritDoc - */ - public function removeObjectGroup(Bill $bill): Bill - { - $bill->objectGroups()->sync([]); - - return $bill; - } - - /** - * @inheritDoc - */ - public function setOrder(Bill $bill, int $order): void - { - $bill->order = $order; - $bill->save(); - } - - /** - * @inheritDoc - */ - public function destroyAll(): void - { - $this->user->bills()->delete(); - } } diff --git a/app/Repositories/Bill/BillRepositoryInterface.php b/app/Repositories/Bill/BillRepositoryInterface.php index ab2884b508..030cb479c1 100644 --- a/app/Repositories/Bill/BillRepositoryInterface.php +++ b/app/Repositories/Bill/BillRepositoryInterface.php @@ -35,44 +35,11 @@ use Illuminate\Support\Collection; interface BillRepositoryInterface { - /** - * @param Bill $bill - * @param string $objectGroupTitle - * - * @return Bill - */ - public function setObjectGroup(Bill $bill, string $objectGroupTitle): Bill; - - /** - * - */ - public function destroyAll(): void; - - /** - * @param Bill $bill - * - * @return Bill - */ - public function removeObjectGroup(Bill $bill): Bill; - - /** - * @param Bill $bill - */ - public function unlinkAll(Bill $bill): void; - /** * Add correct order to bills. */ public function correctOrder(): void; - /** - * Set specific piggy bank to specific order. - * - * @param Bill $bill - * @param int $order - */ - public function setOrder(Bill $bill, int $order): void; - /** * @param Bill $bill * @@ -80,6 +47,11 @@ interface BillRepositoryInterface */ public function destroy(Bill $bill): bool; + /** + * + */ + public function destroyAll(): void; + /** * Find a bill by ID. * @@ -273,7 +245,7 @@ interface BillRepositoryInterface * @param Bill $bill * @param Carbon $date * - * @return \Carbon\Carbon + * @return Carbon */ public function nextDateMatch(Bill $bill, Carbon $date): Carbon; @@ -281,10 +253,17 @@ interface BillRepositoryInterface * @param Bill $bill * @param Carbon $date * - * @return \Carbon\Carbon + * @return Carbon */ public function nextExpectedMatch(Bill $bill, Carbon $date): Carbon; + /** + * @param Bill $bill + * + * @return Bill + */ + public function removeObjectGroup(Bill $bill): Bill; + /** * @param string $query * @param int $limit @@ -293,6 +272,22 @@ interface BillRepositoryInterface */ public function searchBill(string $query, int $limit): Collection; + /** + * @param Bill $bill + * @param string $objectGroupTitle + * + * @return Bill + */ + public function setObjectGroup(Bill $bill, string $objectGroupTitle): Bill; + + /** + * Set specific piggy bank to specific order. + * + * @param Bill $bill + * @param int $order + */ + public function setOrder(Bill $bill, int $order): void; + /** * @param User $user */ @@ -306,6 +301,11 @@ interface BillRepositoryInterface */ public function store(array $data): Bill; + /** + * @param Bill $bill + */ + public function unlinkAll(Bill $bill): void; + /** * @param Bill $bill * @param array $data diff --git a/app/Repositories/Budget/AvailableBudgetRepository.php b/app/Repositories/Budget/AvailableBudgetRepository.php index 1ea1f8022c..64854c7c29 100644 --- a/app/Repositories/Budget/AvailableBudgetRepository.php +++ b/app/Repositories/Budget/AvailableBudgetRepository.php @@ -25,7 +25,6 @@ namespace FireflyIII\Repositories\Budget; use Carbon\Carbon; use Exception; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\AvailableBudget; use FireflyIII\Models\TransactionCurrency; use FireflyIII\User; @@ -41,6 +40,14 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface { private User $user; + /** + * Delete all available budgets. + */ + public function destroyAll(): void + { + $this->user->availableBudgets()->delete(); + } + /** * @param AvailableBudget $availableBudget */ @@ -171,6 +178,18 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface return $query->get(); } + /** + * @inheritDoc + */ + public function getByCurrencyDate(Carbon $start, Carbon $end, TransactionCurrency $currency): ?AvailableBudget + { + return $this->user + ->availableBudgets() + ->where('transaction_currency_id', $currency->id) + ->where('start_date', $start->format('Y-m-d')) + ->where('end_date', $end->format('Y-m-d'))->first(); + } + /** * @param TransactionCurrency $currency * @param Carbon $start @@ -215,17 +234,18 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface public function store(array $data): ?AvailableBudget { $start = $data['start']; - if($start instanceof Carbon) { + if ($start instanceof Carbon) { $start = $data['start']->startOfDay(); } $end = $data['end']; - if($end instanceof Carbon) { + if ($end instanceof Carbon) { $end = $data['end']->endOfDay(); } + return AvailableBudget::create( [ 'user_id' => $this->user->id, - 'transaction_currency_id' => $data['currency']->id, + 'transaction_currency_id' => $data['currency_id'], 'amount' => $data['amount'], 'start_date' => $start, 'end_date' => $end, @@ -255,57 +275,36 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface * @param array $data * * @return AvailableBudget - * @throws FireflyException */ public function updateAvailableBudget(AvailableBudget $availableBudget, array $data): AvailableBudget { - $existing = $this->user->availableBudgets() - ->where('transaction_currency_id', $data['currency_id']) - ->where('start_date', $data['start']->format('Y-m-d')) - ->where('end_date', $data['end']->format('Y-m-d')) - ->where('id', '!=', $availableBudget->id) - ->first(); - - if (null !== $existing) { - throw new FireflyException(sprintf('An entry already exists for these parameters: available budget object with ID #%d', $existing->id)); + if (array_key_exists('start', $data)) { + $start = $data['start']; + if ($start instanceof Carbon) { + $start = $data['start']->startOfDay(); + $availableBudget->start_date = $start; + $availableBudget->save(); + } } - $start = $data['start']; - if($start instanceof Carbon) { - $start = $data['start']->startOfDay(); + if (array_key_exists('end', $data)) { + $end = $data['end']; + if ($end instanceof Carbon) { + $end = $data['end']->endOfDay(); + $availableBudget->end_date = $end; + $availableBudget->save(); + } } - $end = $data['end']; - if($end instanceof Carbon) { - $end = $data['end']->endOfDay(); + if (array_key_exists('currency_id', $data)) { + $availableBudget->transaction_currency_id = $data['currency_id']; + $availableBudget->save(); + } + if (array_key_exists('amount', $data)) { + $availableBudget->amount = $data['amount']; + $availableBudget->save(); } - - $availableBudget->transaction_currency_id = $data['currency_id']; - $availableBudget->start_date = $start; - $availableBudget->end_date = $end; - $availableBudget->amount = $data['amount']; - $availableBudget->save(); return $availableBudget; } - - /** - * Delete all available budgets. - */ - public function destroyAll(): void - { - $this->user->availableBudgets()->delete(); - } - - /** - * @inheritDoc - */ - public function getByCurrencyDate(Carbon $start, Carbon $end, TransactionCurrency $currency): ?AvailableBudget - { - return $this->user - ->availableBudgets() - ->where('transaction_currency_id', $currency->id) - ->where('start_date', $start->format('Y-m-d')) - ->where('end_date', $end->format('Y-m-d'))->first(); - } } diff --git a/app/Repositories/Budget/AvailableBudgetRepositoryInterface.php b/app/Repositories/Budget/AvailableBudgetRepositoryInterface.php index d631233669..1120dfba3e 100644 --- a/app/Repositories/Budget/AvailableBudgetRepositoryInterface.php +++ b/app/Repositories/Budget/AvailableBudgetRepositoryInterface.php @@ -66,17 +66,6 @@ interface AvailableBudgetRepositoryInterface */ public function get(?Carbon $start = null, ?Carbon $end = null): Collection; - /** - * Get by transaction currency and date. Should always result in one entry or NULL. - * - * @param Carbon $start - * @param Carbon $end - * @param TransactionCurrency $currency - * - * @return null|AvailableBudget - */ - public function getByCurrencyDate(Carbon $start, Carbon $end, TransactionCurrency $currency): ?AvailableBudget; - /** * @param TransactionCurrency $currency * @param Carbon $start @@ -115,6 +104,17 @@ interface AvailableBudgetRepositoryInterface */ public function getAvailableBudgetsByDate(?Carbon $start, ?Carbon $end): Collection; + /** + * Get by transaction currency and date. Should always result in one entry or NULL. + * + * @param Carbon $start + * @param Carbon $end + * @param TransactionCurrency $currency + * + * @return null|AvailableBudget + */ + public function getByCurrencyDate(Carbon $start, Carbon $end, TransactionCurrency $currency): ?AvailableBudget; + /** * @param TransactionCurrency $currency * @param Carbon $start diff --git a/app/Repositories/Budget/BudgetLimitRepository.php b/app/Repositories/Budget/BudgetLimitRepository.php index 1007bfe02e..81cf81138b 100644 --- a/app/Repositories/Budget/BudgetLimitRepository.php +++ b/app/Repositories/Budget/BudgetLimitRepository.php @@ -88,8 +88,6 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface ); } ) - - ->where('budget_limits.transaction_currency_id', $currency->id) ->whereNull('budgets.deleted_at') ->where('budgets.user_id', $this->user->id); @@ -107,6 +105,18 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface return $result; } + /** + * Destroy all budget limits. + */ + public function destroyAll(): void + { + $budgets = $this->user->budgets()->get(); + /** @var Budget $budget */ + foreach ($budgets as $budget) { + $budget->budgetlimits()->delete(); + } + } + /** * Destroy a budget limit. * @@ -168,8 +178,10 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface // start date must be after $start. $query->where('start_date', '>=', $start->format('Y-m-d 00:00:00')); } + return $query->get(['budget_limits.*']); } + // neither are NULL: return BudgetLimit::leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') ->with(['budget']) @@ -244,6 +256,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface // start date must be after $start. $query->where('start_date', '>=', $start->format('Y-m-d 00:00:00')); } + return $query->get(['budget_limits.*']); } @@ -307,7 +320,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface $currency->save(); // find the budget: - $budget = $this->user->budgets()->find((int) $data['budget_id']); + $budget = $this->user->budgets()->find((int)$data['budget_id']); if (null === $budget) { throw new FireflyException('200004: Budget does not exist.'); // @codeCoverageIgnore } @@ -344,22 +357,22 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface */ public function update(BudgetLimit $budgetLimit, array $data): BudgetLimit { - $budgetLimit->amount = array_key_exists('amount',$data) ? $data['amount'] : $budgetLimit->amount; + $budgetLimit->amount = array_key_exists('amount', $data) ? $data['amount'] : $budgetLimit->amount; $budgetLimit->budget_id = array_key_exists('budget_id', $data) ? $data['budget_id'] : $budgetLimit->budget_id; - $budgetLimit->start_date = array_key_exists('start_date', $data) ? $data['start_date']->format('Y-m-d 00:00:00') : $budgetLimit->start_date; - $budgetLimit->end_date = array_key_exists('end_date', $data) ? $data['end_date']->format('Y-m-d 23:59:59') : $budgetLimit->end_date; + $budgetLimit->start_date = array_key_exists('start', $data) ? $data['start']->format('Y-m-d 00:00:00') : $budgetLimit->start_date; + $budgetLimit->end_date = array_key_exists('end', $data) ? $data['end']->format('Y-m-d 23:59:59') : $budgetLimit->end_date; // if no currency has been provided, use the user's default currency: $currency = null; // update if relevant: - if(array_key_exists('currency_id', $data) || array_key_exists('currency_code', $data)) { + if (array_key_exists('currency_id', $data) || array_key_exists('currency_code', $data)) { /** @var TransactionCurrencyFactory $factory */ $factory = app(TransactionCurrencyFactory::class); $currency = $factory->find($data['currency_id'] ?? null, $data['currency_code'] ?? null); } // catch unexpected null: - if(null === $currency) { + if (null === $currency) { $currency = $budgetLimit->transactionCurrency ?? app('amount')->getDefaultCurrencyByUser($this->user); } $currency->enabled = true; @@ -368,8 +381,6 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface $budgetLimit->transaction_currency_id = $currency->id; $budgetLimit->save(); - Log::debug(sprintf('Updated budget limit with ID #%d and amount %s', $budgetLimit->id, $data['amount'])); - return $budgetLimit; } @@ -441,16 +452,4 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface return $limit; } - - /** - * Destroy all budget limits. - */ - public function destroyAll(): void - { - $budgets = $this->user->budgets()->get(); - /** @var Budget $budget */ - foreach ($budgets as $budget) { - $budget->budgetlimits()->delete(); - } - } } diff --git a/app/Repositories/Budget/BudgetLimitRepositoryInterface.php b/app/Repositories/Budget/BudgetLimitRepositoryInterface.php index 4fc293f5d5..686519d990 100644 --- a/app/Repositories/Budget/BudgetLimitRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetLimitRepositoryInterface.php @@ -36,11 +36,6 @@ use Illuminate\Support\Collection; interface BudgetLimitRepositoryInterface { - /** - * Destroy all budget limits. - */ - public function destroyAll(): void; - /** * Tells you which amount has been budgeted (for the given budgets) * in the selected query. Returns a positive amount as a string. @@ -54,6 +49,11 @@ interface BudgetLimitRepositoryInterface */ public function budgeted(Carbon $start, Carbon $end, TransactionCurrency $currency, ?Collection $budgets = null): string; + /** + * Destroy all budget limits. + */ + public function destroyAll(): void; + /** * Destroy a budget limit. * diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 548b6a229f..ea0d2675c5 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -90,6 +90,33 @@ class BudgetRepository implements BudgetRepositoryInterface return true; } + /** + * Destroy all budgets. + */ + public function destroyAll(): void + { + $budgets = $this->getBudgets(); + /** @var Budget $budget */ + foreach ($budgets as $budget) { + DB::table('budget_transaction')->where('budget_id', $budget->id)->delete(); + DB::table('budget_transaction_journal')->where('budget_id', $budget->id)->delete(); + RecurrenceTransactionMeta::where('name', 'budget_id')->where('value', $budget->id)->delete(); + RuleAction::where('action_type', 'set_budget')->where('action_value', $budget->id)->delete(); + $budget->delete(); + } + } + + /** + * @inheritDoc + */ + public function destroyAutoBudget(Budget $budget): void + { + /** @var AutoBudget $autoBudget */ + foreach ($budget->autoBudgets()->get() as $autoBudget) { + $autoBudget->delete(); + } + } + /** * @param int|null $budgetId * @param string|null $budgetName @@ -176,6 +203,35 @@ class BudgetRepository implements BudgetRepositoryInterface ->get(); } + /** + * @inheritDoc + */ + public function getAttachments(Budget $budget): Collection + { + $set = $budget->attachments()->get(); + + /** @var Storage $disk */ + $disk = Storage::disk('upload'); + + return $set->each( + static function (Attachment $attachment) use ($disk) { + $notes = $attachment->notes()->first(); + $attachment->file_exists = $disk->exists($attachment->fileName()); + $attachment->notes = $notes ? $notes->text : ''; + + return $attachment; + } + ); + } + + /** + * @inheritDoc + */ + public function getAutoBudget(Budget $budget): ?AutoBudget + { + return $budget->autoBudgets()->first(); + } + /** * @return Collection */ @@ -207,6 +263,11 @@ class BudgetRepository implements BudgetRepositoryInterface ->orderBy('name', 'ASC')->where('active', 0)->get(); } + public function getMaxOrder(): int + { + return (int)$this->user->budgets()->max('order'); + } + /** * @param string $query * @param int $limit @@ -259,6 +320,7 @@ class BudgetRepository implements BudgetRepositoryInterface 'user_id' => $this->user->id, 'name' => $data['name'], 'order' => $order + 1, + 'active' => array_key_exists('active', $data) ? $data['active'] : true, ] ); } catch (QueryException $e) { @@ -266,25 +328,27 @@ class BudgetRepository implements BudgetRepositoryInterface Log::error($e->getTraceAsString()); throw new FireflyException('400002: Could not store budget.'); } - - // try to create associated auto budget: - $type = $data['auto_budget_type'] ?? 0; - if (0 === $type) { + if (!array_key_exists('auto_budget_type', $data)) { return $newBudget; } + $type = $data['auto_budget_type']; + if ('none' === $type) { + return $newBudget; + } + if ('reset' === $type) { $type = AutoBudget::AUTO_BUDGET_RESET; } if ('rollover' === $type) { $type = AutoBudget::AUTO_BUDGET_ROLLOVER; } - $repos = app(CurrencyRepositoryInterface::class); - $currencyId = (int)($data['transaction_currency_id'] ?? 0); - $currencyCode = (string)($data['transaction_currency_code'] ?? ''); - $currency = $repos->findNull($currencyId); - if (null === $currency) { - $currency = $repos->findByCodeNull($currencyCode); + $repos = app(CurrencyRepositoryInterface::class); + if (array_key_exists('currency_id', $data)) { + $currency = $repos->findNull((int)$data['currency_id']); + } + if (array_key_exists('currency_code', $data)) { + $currency = $repos->findByCode((string)$data['currency_code']); } if (null === $currency) { $currency = app('amount')->getDefaultCurrencyByUser($this->user); @@ -326,81 +390,72 @@ class BudgetRepository implements BudgetRepositoryInterface */ public function update(Budget $budget, array $data): Budget { - $oldName = $budget->name; - $budget->name = $data['name']; - $budget->active = $data['active']; + $oldName = $budget->name; + if (array_key_exists('name', $data)) { + $budget->name = $data['name']; + } + if (array_key_exists('active', $data)) { + $budget->active = $data['active']; + } $budget->save(); // update or create auto-budget: - $autoBudgetType = $data['auto_budget_type'] ?? 0; - if ('reset' === $autoBudgetType) { - $autoBudgetType = AutoBudget::AUTO_BUDGET_RESET; - } - if ('rollover' === $autoBudgetType) { - $autoBudgetType = AutoBudget::AUTO_BUDGET_ROLLOVER; - } - if ('none' === $autoBudgetType) { - $autoBudgetType = 0; - } - if (0 !== $autoBudgetType) { - $autoBudget = $this->getAutoBudget($budget); - if (null === $autoBudget) { - $autoBudget = new AutoBudget; - $autoBudget->budget()->associate($budget); - } + $autoBudget = $this->getAutoBudget($budget); + // get currency: + $currency = null; + if (array_key_exists('currency_id', $data) || array_key_exists('currency_code', $data)) { $repos = app(CurrencyRepositoryInterface::class); - $currencyId = (int)($data['transaction_currency_id'] ?? 0); - $currencyCode = (string)($data['transaction_currency_code'] ?? ''); - - $currency = $repos->findNull($currencyId); + $currencyId = (int)($data['currency_id'] ?? 0); + $currencyCode = (string)($data['currency_code'] ?? ''); + $currency = $repos->findNull($currencyId); if (null === $currency) { $currency = $repos->findByCodeNull($currencyCode); } - if (null === $currency) { - $currency = app('amount')->getDefaultCurrencyByUser($this->user); - } + } + if (null === $currency) { + $currency = app('amount')->getDefaultCurrencyByUser($this->user); + } + if (null === $autoBudget + && array_key_exists('auto_budget_type', $data) + && array_key_exists('auto_budget_amount', $data) + ) { + // only create if all are here: + $autoBudget = new AutoBudget; + $autoBudget->budget_id = $budget->id; $autoBudget->transaction_currency_id = $currency->id; - $autoBudget->auto_budget_type = $autoBudgetType; - $autoBudget->amount = $data['auto_budget_amount'] ?? '0'; - $autoBudget->period = $data['auto_budget_period'] ?? 'monthly'; + } + + // update existing type + if (array_key_exists('auto_budget_type', $data)) { + $autoBudgetType = $data['auto_budget_type']; + if ('reset' === $autoBudgetType) { + $autoBudget->auto_budget_type = AutoBudget::AUTO_BUDGET_RESET; + } + if ('rollover' === $autoBudgetType) { + $autoBudget->auto_budget_type = AutoBudget::AUTO_BUDGET_ROLLOVER; + } + if ('none' === $autoBudgetType && null !== $autoBudget->id) { + $autoBudget->delete(); + + return $budget; + } + } + if (array_key_exists('auto_budget_amount', $data)) { + $autoBudget->amount = $data['auto_budget_amount']; + } + if (array_key_exists('auto_budget_period', $data)) { + $autoBudget->period = $data['auto_budget_period']; + } + if (null !== $autoBudget) { $autoBudget->save(); } - if (0 === $autoBudgetType) { - $autoBudget = $this->getAutoBudget($budget); - if (null !== $autoBudget) { - $this->destroyAutoBudget($budget); - } - } - $this->updateRuleTriggers($oldName, $data['name']); - $this->updateRuleActions($oldName, $data['name']); - app('preferences')->mark(); + return $budget; } - /** - * @param string $oldName - * @param string $newName - */ - private function updateRuleActions(string $oldName, string $newName): void - { - $types = ['set_budget',]; - $actions = RuleAction::leftJoin('rules', 'rules.id', '=', 'rule_actions.rule_id') - ->where('rules.user_id', $this->user->id) - ->whereIn('rule_actions.action_type', $types) - ->where('rule_actions.action_value', $oldName) - ->get(['rule_actions.*']); - Log::debug(sprintf('Found %d actions to update.', $actions->count())); - /** @var RuleAction $action */ - foreach ($actions as $action) { - $action->action_value = $newName; - $action->save(); - Log::debug(sprintf('Updated action %d: %s', $action->id, $action->action_value)); - } - } - /** * @param string $oldName * @param string $newName @@ -423,63 +478,23 @@ class BudgetRepository implements BudgetRepositoryInterface } /** - * Destroy all budgets. + * @param string $oldName + * @param string $newName */ - public function destroyAll(): void + private function updateRuleActions(string $oldName, string $newName): void { - $budgets = $this->getBudgets(); - /** @var Budget $budget */ - foreach ($budgets as $budget) { - DB::table('budget_transaction')->where('budget_id', $budget->id)->delete(); - DB::table('budget_transaction_journal')->where('budget_id', $budget->id)->delete(); - RecurrenceTransactionMeta::where('name', 'budget_id')->where('value', $budget->id)->delete(); - RuleAction::where('action_type', 'set_budget')->where('action_value', $budget->id)->delete(); - $budget->delete(); + $types = ['set_budget',]; + $actions = RuleAction::leftJoin('rules', 'rules.id', '=', 'rule_actions.rule_id') + ->where('rules.user_id', $this->user->id) + ->whereIn('rule_actions.action_type', $types) + ->where('rule_actions.action_value', $oldName) + ->get(['rule_actions.*']); + Log::debug(sprintf('Found %d actions to update.', $actions->count())); + /** @var RuleAction $action */ + foreach ($actions as $action) { + $action->action_value = $newName; + $action->save(); + Log::debug(sprintf('Updated action %d: %s', $action->id, $action->action_value)); } } - - /** - * @inheritDoc - */ - public function getAutoBudget(Budget $budget): ?AutoBudget - { - return $budget->autoBudgets()->first(); - } - - /** - * @inheritDoc - */ - public function destroyAutoBudget(Budget $budget): void - { - /** @var AutoBudget $autoBudget */ - foreach ($budget->autoBudgets()->get() as $autoBudget) { - $autoBudget->delete(); - } - } - - /** - * @inheritDoc - */ - public function getAttachments(Budget $budget): Collection - { - $set = $budget->attachments()->get(); - - /** @var Storage $disk */ - $disk = Storage::disk('upload'); - - return $set->each( - static function (Attachment $attachment) use ($disk) { - $notes = $attachment->notes()->first(); - $attachment->file_exists = $disk->exists($attachment->fileName()); - $attachment->notes = $notes ? $notes->text : ''; - - return $attachment; - } - ); - } - - public function getMaxOrder(): int - { - return (int)$this->user->budgets()->max('order'); - } } diff --git a/app/Repositories/Budget/BudgetRepositoryInterface.php b/app/Repositories/Budget/BudgetRepositoryInterface.php index bf58a3f739..301697faf9 100644 --- a/app/Repositories/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetRepositoryInterface.php @@ -23,8 +23,8 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Budget; use Carbon\Carbon; -use FireflyIII\Models\AutoBudget; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\AutoBudget; use FireflyIII\Models\Budget; use FireflyIII\User; use Illuminate\Support\Collection; @@ -34,36 +34,6 @@ use Illuminate\Support\Collection; */ interface BudgetRepositoryInterface { - /** - * Destroy all budgets. - */ - public function destroyAll(): void; - - /** - * @param Budget $budget - * - * @return Collection - */ - public function getAttachments(Budget $budget): Collection; - - /** - * @param Budget $budget - * - * @return AutoBudget|null - */ - public function getAutoBudget(Budget $budget): ?AutoBudget; - - /** - * @param Budget $budget - */ - public function destroyAutoBudget(Budget $budget): void; - - /** - * @return int - */ - public function getMaxOrder(): int; - - /** * @return bool */ @@ -76,6 +46,16 @@ interface BudgetRepositoryInterface */ public function destroy(Budget $budget): bool; + /** + * Destroy all budgets. + */ + public function destroyAll(): void; + + /** + * @param Budget $budget + */ + public function destroyAutoBudget(Budget $budget): void; + /** * @param int|null $budgetId * @param string|null $budgetName @@ -117,6 +97,20 @@ interface BudgetRepositoryInterface */ public function getActiveBudgets(): Collection; + /** + * @param Budget $budget + * + * @return Collection + */ + public function getAttachments(Budget $budget): Collection; + + /** + * @param Budget $budget + * + * @return AutoBudget|null + */ + public function getAutoBudget(Budget $budget): ?AutoBudget; + /** * @return Collection */ @@ -136,9 +130,15 @@ interface BudgetRepositoryInterface */ public function getInactiveBudgets(): Collection; + /** + * @return int + */ + public function getMaxOrder(): int; + /** * @param string $query - * @param int $limit + * @param int $limit + * * @return Collection */ public function searchBudget(string $query, int $limit): Collection; diff --git a/app/Repositories/Budget/NoBudgetRepository.php b/app/Repositories/Budget/NoBudgetRepository.php index 7229a49420..8b3362e135 100644 --- a/app/Repositories/Budget/NoBudgetRepository.php +++ b/app/Repositories/Budget/NoBudgetRepository.php @@ -30,7 +30,6 @@ use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionType; use FireflyIII\User; use Illuminate\Support\Collection; -use Log; /** * @@ -135,12 +134,12 @@ class NoBudgetRepository implements NoBudgetRepositoryInterface /** @var TransactionCurrency $currency */ $currency = $currencies[$code]; $return[] = [ - 'currency_id' => $currency['id'], + 'currency_id' => (string)$currency['id'], 'currency_code' => $code, 'currency_name' => $currency['name'], 'currency_symbol' => $currency['symbol'], 'currency_decimal_places' => $currency['decimal_places'], - 'amount' => number_format((float)$spent,$currency['decimal_places'], '.',''), + 'amount' => number_format((float)$spent, $currency['decimal_places'], '.', ''), ]; } diff --git a/app/Repositories/Budget/OperationsRepository.php b/app/Repositories/Budget/OperationsRepository.php index 150bcca996..9b3fcd2c64 100644 --- a/app/Repositories/Budget/OperationsRepository.php +++ b/app/Repositories/Budget/OperationsRepository.php @@ -25,12 +25,9 @@ namespace FireflyIII\Repositories\Budget; use Carbon\Carbon; use FireflyIII\Helpers\Collector\GroupCollectorInterface; -use FireflyIII\Models\AccountType; use FireflyIII\Models\Budget; -use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionType; -use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\User; use Illuminate\Support\Collection; use Log; @@ -59,15 +56,15 @@ class OperationsRepository implements OperationsRepositoryInterface foreach ($budget->budgetlimits as $limit) { $diff = $limit->start_date->diffInDays($limit->end_date); $diff = 0 === $diff ? 1 : $diff; - $amount = (string) $limit->amount; - $perDay = bcdiv($amount, (string) $diff); + $amount = (string)$limit->amount; + $perDay = bcdiv($amount, (string)$diff); $total = bcadd($total, $perDay); $count++; Log::debug(sprintf('Found %d budget limits. Per day is %s, total is %s', $count, $perDay, $total)); } $avg = $total; if ($count > 0) { - $avg = bcdiv($total, (string) $count); + $avg = bcdiv($total, (string)$count); } Log::debug(sprintf('%s / %d = %s = average.', $total, $count, $avg)); @@ -103,9 +100,9 @@ class OperationsRepository implements OperationsRepositoryInterface /** @var array $journal */ foreach ($journals as $journal) { // prep data array for currency: - $budgetId = (int) $journal['budget_id']; + $budgetId = (int)$journal['budget_id']; $budgetName = $journal['budget_name']; - $currencyId = (int) $journal['currency_id']; + $currencyId = (int)$journal['currency_id']; $key = sprintf('%d-%d', $budgetId, $currencyId); $data[$key] = $data[$key] ?? [ @@ -157,9 +154,9 @@ class OperationsRepository implements OperationsRepositoryInterface $array = []; foreach ($journals as $journal) { - $currencyId = (int) $journal['currency_id']; - $budgetId = (int) $journal['budget_id']; - $budgetName = (string) $journal['budget_name']; + $currencyId = (int)$journal['currency_id']; + $budgetId = (int)$journal['budget_id']; + $budgetName = (string)$journal['budget_name']; // catch "no category" entries. if (0 === $budgetId) { @@ -185,7 +182,7 @@ class OperationsRepository implements OperationsRepositoryInterface // add journal to array: // only a subset of the fields. - $journalId = (int) $journal['transaction_journal_id']; + $journalId = (int)$journal['transaction_journal_id']; $array[$currencyId]['budgets'][$budgetId]['transaction_journals'][$journalId] = [ @@ -264,12 +261,12 @@ class OperationsRepository implements OperationsRepositoryInterface /** @var TransactionCurrency $currency */ $currency = $currencies[$code]; $return[] = [ - 'currency_id' => $currency['id'], + 'currency_id' => (string)$currency['id'], 'currency_code' => $code, 'currency_name' => $currency['name'], 'currency_symbol' => $currency['symbol'], 'currency_decimal_places' => $currency['decimal_places'], - 'amount' => number_format((float) $spent, $currency['decimal_places'], '.', ''), + 'amount' => number_format((float)$spent, $currency['decimal_places'], '.', ''), ]; } @@ -286,8 +283,7 @@ class OperationsRepository implements OperationsRepositoryInterface * @return array */ public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null, ?TransactionCurrency $currency = null - ): array - { + ): array { Log::debug(sprintf('Now in %s', __METHOD__)); $start->startOfDay(); $end->endOfDay(); @@ -326,7 +322,7 @@ class OperationsRepository implements OperationsRepositoryInterface $array = []; foreach ($journals as $journal) { - $currencyId = (int) $journal['currency_id']; + $currencyId = (int)$journal['currency_id']; $array[$currencyId] = $array[$currencyId] ?? [ 'sum' => '0', 'currency_id' => $currencyId, @@ -338,8 +334,8 @@ class OperationsRepository implements OperationsRepositoryInterface $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->negative($journal['amount'])); // also do foreign amount: - $foreignId = (int) $journal['foreign_currency_id']; - if(0 !== $foreignId) { + $foreignId = (int)$journal['foreign_currency_id']; + if (0 !== $foreignId) { $array[$foreignId] = $array[$foreignId] ?? [ 'sum' => '0', 'currency_id' => $foreignId, @@ -356,6 +352,17 @@ class OperationsRepository implements OperationsRepositoryInterface return $array; } + /** + * @return Collection + */ + private function getBudgets(): Collection + { + /** @var BudgetRepositoryInterface $repos */ + $repos = app(BudgetRepositoryInterface::class); + + return $repos->getActiveBudgets(); + } + /** * For now, simply refer to whichever repository holds this function. * TODO might be done better in the future. @@ -373,15 +380,4 @@ class OperationsRepository implements OperationsRepositoryInterface return $blRepository->getBudgetLimits($budget, $start, $end); } - - /** - * @return Collection - */ - private function getBudgets(): Collection - { - /** @var BudgetRepositoryInterface $repos */ - $repos = app(BudgetRepositoryInterface::class); - - return $repos->getActiveBudgets(); - } } diff --git a/app/Repositories/Budget/OperationsRepositoryInterface.php b/app/Repositories/Budget/OperationsRepositoryInterface.php index 4723743664..4296d13179 100644 --- a/app/Repositories/Budget/OperationsRepositoryInterface.php +++ b/app/Repositories/Budget/OperationsRepositoryInterface.php @@ -56,11 +56,27 @@ interface OperationsRepositoryInterface */ public function getBudgetPeriodReport(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): array; + /** + * This method returns a list of all the withdrawal transaction journals (as arrays) set in that period + * which have the specified budget set to them. It's grouped per currency, with as few details in the array + * as possible. Amounts are always negative. + * + * @param Carbon $start + * @param Carbon $end + * @param Collection|null $accounts + * @param Collection|null $budgets + * + * @return array + */ + public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null): array; + /** * @param User $user */ public function setUser(User $user): void; + /** @noinspection MoreThanThreeArgumentsInspection */ + /** * Return multi-currency spent information. * @@ -74,7 +90,6 @@ interface OperationsRepositoryInterface */ public function spentInPeriodMc(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): array; - /** @noinspection MoreThanThreeArgumentsInspection */ /** * @param Carbon $start * @param Carbon $end @@ -87,18 +102,4 @@ interface OperationsRepositoryInterface public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null, ?TransactionCurrency $currency = null ): array; - /** - * This method returns a list of all the withdrawal transaction journals (as arrays) set in that period - * which have the specified budget set to them. It's grouped per currency, with as few details in the array - * as possible. Amounts are always negative. - * - * @param Carbon $start - * @param Carbon $end - * @param Collection|null $accounts - * @param Collection|null $budgets - * - * @return array - */ - public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null): array; - } diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 14100abadc..235e97606d 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -24,6 +24,7 @@ namespace FireflyIII\Repositories\Category; use Carbon\Carbon; use DB; +use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Factory\CategoryFactory; use FireflyIII\Models\Attachment; @@ -61,6 +62,22 @@ class CategoryRepository implements CategoryRepositoryInterface return true; } + /** + * Delete all categories. + */ + public function destroyAll(): void + { + $categories = $this->getCategories(); + /** @var Category $category */ + foreach ($categories as $category) { + DB::table('category_transaction')->where('category_id', $category->id)->delete(); + DB::table('category_transaction_journal')->where('category_id', $category->id)->delete(); + RecurrenceTransactionMeta::where('name', 'category_id')->where('value', $category->id)->delete(); + RuleAction::where('action_type', 'set_category')->where('action_value', $category->name)->delete(); + $category->delete(); + } + } + /** * Find a category. * @@ -150,6 +167,27 @@ class CategoryRepository implements CategoryRepositoryInterface return $firstJournalDate; } + /** + * @inheritDoc + */ + public function getAttachments(Category $category): Collection + { + $set = $category->attachments()->get(); + + /** @var Storage $disk */ + $disk = Storage::disk('upload'); + + return $set->each( + static function (Attachment $attachment) use ($disk) { + $notes = $attachment->notes()->first(); + $attachment->file_exists = $disk->exists($attachment->fileName()); + $attachment->notes = $notes ? $notes->text : ''; + + return $attachment; + } + ); + } + /** * Get all categories with ID's. * @@ -172,6 +210,19 @@ class CategoryRepository implements CategoryRepositoryInterface return $this->user->categories()->with(['attachments'])->orderBy('name', 'ASC')->get(); } + /** + * @inheritDoc + */ + public function getNoteText(Category $category): ?string + { + $dbNote = $category->notes()->first(); + if (null === $dbNote) { + return null; + } + + return $dbNote->text; + } + /** * @param Category $category * @param Collection $accounts @@ -201,6 +252,14 @@ class CategoryRepository implements CategoryRepositoryInterface return $lastJournalDate; } + /** + * @param Category $category + */ + public function removeNotes(Category $category): void + { + $category->notes()->delete(); + } + /** * @param string $query * @param int $limit @@ -254,16 +313,6 @@ class CategoryRepository implements CategoryRepositoryInterface } - - /** - * @param Category $category - */ - public function removeNotes(Category $category): void - { - $category->notes()->delete(); - } - - /** * @param Category $category * @param array $data @@ -279,6 +328,20 @@ class CategoryRepository implements CategoryRepositoryInterface return $service->update($category, $data); } + /** + * @inheritDoc + */ + public function updateNotes(Category $category, string $notes): void + { + $dbNote = $category->notes()->first(); + if (null === $dbNote) { + $dbNote = new Note; + $dbNote->noteable()->associate($category); + } + $dbNote->text = trim($notes); + $dbNote->save(); + } + /** * @param Category $category * @@ -345,7 +408,7 @@ class CategoryRepository implements CategoryRepositoryInterface * @param Collection $accounts * * @return Carbon|null - * @throws \Exception + * @throws Exception */ private function getLastTransactionDate(Category $category, Collection $accounts): ?Carbon { @@ -365,68 +428,4 @@ class CategoryRepository implements CategoryRepositoryInterface return null; } - - /** - * Delete all categories. - */ - public function destroyAll(): void - { - $categories = $this->getCategories(); - /** @var Category $category */ - foreach ($categories as $category) { - DB::table('category_transaction')->where('category_id', $category->id)->delete(); - DB::table('category_transaction_journal')->where('category_id', $category->id)->delete(); - RecurrenceTransactionMeta::where('name', 'category_id')->where('value', $category->id)->delete(); - RuleAction::where('action_type', 'set_category')->where('action_value', $category->name)->delete(); - $category->delete(); - } - } - - /** - * @inheritDoc - */ - public function getAttachments(Category $category): Collection - { - $set = $category->attachments()->get(); - - /** @var Storage $disk */ - $disk = Storage::disk('upload'); - - return $set->each( - static function (Attachment $attachment) use ($disk) { - $notes = $attachment->notes()->first(); - $attachment->file_exists = $disk->exists($attachment->fileName()); - $attachment->notes = $notes ? $notes->text : ''; - - return $attachment; - } - ); - } - - /** - * @inheritDoc - */ - public function updateNotes(Category $category, string $notes): void - { - $dbNote = $category->notes()->first(); - if (null === $dbNote) { - $dbNote = new Note; - $dbNote->noteable()->associate($category); - } - $dbNote->text = trim($notes); - $dbNote->save(); - } - - /** - * @inheritDoc - */ - public function getNoteText(Category $category): ?string - { - $dbNote = $category->notes()->first(); - if (null === $dbNote) { - return null; - } - - return $dbNote->text; - } } diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index 7feaa45baf..7a25c17426 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -34,38 +34,6 @@ use Illuminate\Support\Collection; interface CategoryRepositoryInterface { - /** - * Remove notes. - * - * @param Category $category - */ - public function removeNotes(Category $category): void; - - /** - * @param Category $category - * @param string $notes - */ - public function updateNotes(Category $category, string $notes): void; - - /** - * @param Category $category - * - * @return string|null - */ - public function getNoteText(Category $category): ?string; - - /** - * Delete all categories. - */ - public function destroyAll(): void; - - /** - * @param Category $category - * - * @return Collection - */ - public function getAttachments(Category $category): Collection; - /** * @param Category $category * @@ -73,6 +41,11 @@ interface CategoryRepositoryInterface */ public function destroy(Category $category): bool; + /** + * Delete all categories. + */ + public function destroyAll(): void; + /** * Find a category. * @@ -106,6 +79,13 @@ interface CategoryRepositoryInterface */ public function firstUseDate(Category $category): ?Carbon; + /** + * @param Category $category + * + * @return Collection + */ + public function getAttachments(Category $category): Collection; + /** * Get all categories with ID's. * @@ -122,6 +102,13 @@ interface CategoryRepositoryInterface */ public function getCategories(): Collection; + /** + * @param Category $category + * + * @return string|null + */ + public function getNoteText(Category $category): ?string; + /** * Return most recent transaction(journal) date or null when never used before. * @@ -132,9 +119,16 @@ interface CategoryRepositoryInterface */ public function lastUseDate(Category $category, Collection $accounts): ?Carbon; + /** + * Remove notes. + * + * @param Category $category + */ + public function removeNotes(Category $category): void; + /** * @param string $query - * @param int $limit + * @param int $limit * * @return Collection */ @@ -147,8 +141,9 @@ interface CategoryRepositoryInterface /** * @param array $data - * @throws FireflyException + * * @return Category + * @throws FireflyException */ public function store(array $data): Category; @@ -159,4 +154,10 @@ interface CategoryRepositoryInterface * @return Category */ public function update(Category $category, array $data): Category; + + /** + * @param Category $category + * @param string $notes + */ + public function updateNotes(Category $category, string $notes): void; } diff --git a/app/Repositories/Category/NoCategoryRepository.php b/app/Repositories/Category/NoCategoryRepository.php index e86df31d12..6883394a52 100644 --- a/app/Repositories/Category/NoCategoryRepository.php +++ b/app/Repositories/Category/NoCategoryRepository.php @@ -29,7 +29,6 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\TransactionType; use FireflyIII\User; use Illuminate\Support\Collection; -use Log; /** * @@ -37,8 +36,7 @@ use Log; */ class NoCategoryRepository implements NoCategoryRepositoryInterface { - /** @var User */ - private $user; + private User $user; /** * This method returns a list of all the withdrawal transaction journals (as arrays) set in that period @@ -229,4 +227,35 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface return $array; } + + /** + * @inheritDoc + */ + public function sumTransfers(Carbon $start, Carbon $end, ?Collection $accounts = null): array + { + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::TRANSFER])->withoutCategory(); + + if (null !== $accounts && $accounts->count() > 0) { + $collector->setAccounts($accounts); + } + $journals = $collector->getExtractedJournals(); + $array = []; + + foreach ($journals as $journal) { + $currencyId = (int)$journal['currency_id']; + $array[$currencyId] = $array[$currencyId] ?? [ + 'sum' => '0', + 'currency_id' => $currencyId, + 'currency_name' => $journal['currency_name'], + 'currency_symbol' => $journal['currency_symbol'], + 'currency_code' => $journal['currency_code'], + 'currency_decimal_places' => $journal['currency_decimal_places'], + ]; + $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->positive($journal['amount'])); + } + + return $array; + } } diff --git a/app/Repositories/Category/NoCategoryRepositoryInterface.php b/app/Repositories/Category/NoCategoryRepositoryInterface.php index dd82a6aa2c..592526bd09 100644 --- a/app/Repositories/Category/NoCategoryRepositoryInterface.php +++ b/app/Repositories/Category/NoCategoryRepositoryInterface.php @@ -87,5 +87,16 @@ interface NoCategoryRepositoryInterface */ public function sumIncome(Carbon $start, Carbon $end, ?Collection $accounts = null): array; + /** + * Sum of transfers in period without a category, grouped per currency. Amounts are always positive. + * + * @param Carbon $start + * @param Carbon $end + * @param Collection|null $accounts + * + * @return array + */ + public function sumTransfers(Carbon $start, Carbon $end, ?Collection $accounts = null): array; + } diff --git a/app/Repositories/Category/OperationsRepository.php b/app/Repositories/Category/OperationsRepository.php index f20573c630..a640186676 100644 --- a/app/Repositories/Category/OperationsRepository.php +++ b/app/Repositories/Category/OperationsRepository.php @@ -29,7 +29,6 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\TransactionType; use FireflyIII\User; use Illuminate\Support\Collection; -use Log; /** * @@ -110,7 +109,7 @@ class OperationsRepository implements OperationsRepositoryInterface 'source_account_id' => $journal['source_account_id'], 'budget_name' => $journal['budget_name'], 'source_account_name' => $journal['source_account_name'], - 'destination_account_id' => $journal['destination_account_id'], + 'destination_account_id' => $journal['destination_account_id'], 'destination_account_name' => $journal['destination_account_name'], 'description' => $journal['description'], 'transaction_group_id' => $journal['transaction_group_id'], @@ -294,6 +293,49 @@ class OperationsRepository implements OperationsRepositoryInterface return $array; } + /** + * Sum of income journals in period for a set of categories, grouped per currency. Amounts are always positive. + * + * @param Carbon $start + * @param Carbon $end + * @param Collection|null $accounts + * @param Collection|null $categories + * + * @return array + */ + public function sumTransfers(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array + { + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setUser($this->user)->setRange($start, $end) + ->setTypes([TransactionType::TRANSFER]); + + if (null !== $accounts && $accounts->count() > 0) { + $collector->setAccounts($accounts); + } + if (null === $categories || (null !== $categories && 0 === $categories->count())) { + $categories = $this->getCategories(); + } + $collector->setCategories($categories); + $journals = $collector->getExtractedJournals(); + $array = []; + + foreach ($journals as $journal) { + $currencyId = (int)$journal['currency_id']; + $array[$currencyId] = $array[$currencyId] ?? [ + 'sum' => '0', + 'currency_id' => $currencyId, + 'currency_name' => $journal['currency_name'], + 'currency_symbol' => $journal['currency_symbol'], + 'currency_code' => $journal['currency_code'], + 'currency_decimal_places' => $journal['currency_decimal_places'], + ]; + $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->positive($journal['amount'])); + } + + return $array; + } + /** * Returns a list of all the categories belonging to a user. * diff --git a/app/Repositories/Category/OperationsRepositoryInterface.php b/app/Repositories/Category/OperationsRepositoryInterface.php index f2fa4b4e5d..74fc870394 100644 --- a/app/Repositories/Category/OperationsRepositoryInterface.php +++ b/app/Repositories/Category/OperationsRepositoryInterface.php @@ -89,4 +89,16 @@ interface OperationsRepositoryInterface * @return array */ public function sumIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array; + + /** + * Sum of transfers in period for a set of categories, grouped per currency. Amounts are always positive. + * + * @param Carbon $start + * @param Carbon $end + * @param Collection|null $accounts + * @param Collection|null $categories + * + * @return array + */ + public function sumTransfers(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array; } diff --git a/app/Repositories/Currency/CurrencyRepository.php b/app/Repositories/Currency/CurrencyRepository.php index 815f1ba3df..80fdee57c5 100644 --- a/app/Repositories/Currency/CurrencyRepository.php +++ b/app/Repositories/Currency/CurrencyRepository.php @@ -57,6 +57,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface public function countJournals(TransactionCurrency $currency): int { $count = $currency->transactions()->whereNull('deleted_at')->count() + $currency->transactionJournals()->whereNull('deleted_at')->count(); + // also count foreign: return $count + Transaction::where('foreign_currency_id', $currency->id)->count(); } @@ -452,9 +453,17 @@ class CurrencyRepository implements CurrencyRepositoryInterface )->get(); } + /** + * @inheritDoc + */ + public function isFallbackCurrency(TransactionCurrency $currency): bool + { + return $currency->code === config('firefly.default_currency', 'EUR'); + } + /** * @param string $search - * @param int $limit + * @param int $limit * * @return Collection */ @@ -508,12 +517,4 @@ class CurrencyRepository implements CurrencyRepositoryInterface return $service->update($currency, $data); } - - /** - * @inheritDoc - */ - public function isFallbackCurrency(TransactionCurrency $currency): bool - { - return $currency->code === config('firefly.default_currency', 'EUR'); - } } diff --git a/app/Repositories/Currency/CurrencyRepositoryInterface.php b/app/Repositories/Currency/CurrencyRepositoryInterface.php index 60d3eb7760..b6f3daafef 100644 --- a/app/Repositories/Currency/CurrencyRepositoryInterface.php +++ b/app/Repositories/Currency/CurrencyRepositoryInterface.php @@ -36,13 +36,6 @@ use Illuminate\Support\Collection; interface CurrencyRepositoryInterface { - /** - * @param TransactionCurrency $currency - * - * @return bool - */ - public function isFallbackCurrency(TransactionCurrency $currency): bool; - /** * @param TransactionCurrency $currency * @@ -223,6 +216,13 @@ interface CurrencyRepositoryInterface */ public function getExchangeRates(TransactionCurrency $currency): Collection; + /** + * @param TransactionCurrency $currency + * + * @return bool + */ + public function isFallbackCurrency(TransactionCurrency $currency): bool; + /** * @param string $search * @param int $limit @@ -238,8 +238,9 @@ interface CurrencyRepositoryInterface /** * @param array $data - * @throws FireflyException + * * @return TransactionCurrency + * @throws FireflyException */ public function store(array $data): TransactionCurrency; diff --git a/app/Repositories/Journal/JournalAPIRepository.php b/app/Repositories/Journal/JournalAPIRepository.php index c8ad53c407..7873ba6565 100644 --- a/app/Repositories/Journal/JournalAPIRepository.php +++ b/app/Repositories/Journal/JournalAPIRepository.php @@ -48,9 +48,9 @@ class JournalAPIRepository implements JournalAPIRepositoryInterface public function findTransaction(int $transactionId): ?Transaction { return Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->where('transaction_journals.user_id', $this->user->id) - ->where('transactions.id', $transactionId) - ->first(['transactions.*']); + ->where('transaction_journals.user_id', $this->user->id) + ->where('transactions.id', $transactionId) + ->first(['transactions.*']); } /** @@ -78,6 +78,16 @@ class JournalAPIRepository implements JournalAPIRepositoryInterface ); } + /** + * @inheritDoc + */ + public function getJournalLinks(TransactionJournal $journal): Collection + { + $collection = $journal->destJournalLinks()->get(); + + return $journal->sourceJournalLinks()->get()->merge($collection); + } + /** * Get all piggy bank events for a journal. * @@ -105,14 +115,4 @@ class JournalAPIRepository implements JournalAPIRepositoryInterface { $this->user = $user; } - - /** - * @inheritDoc - */ - public function getJournalLinks(TransactionJournal $journal): Collection - { - $collection = $journal->destJournalLinks()->get(); - - return $journal->sourceJournalLinks()->get()->merge($collection); - } } diff --git a/app/Repositories/Journal/JournalCLIRepositoryInterface.php b/app/Repositories/Journal/JournalCLIRepositoryInterface.php index 82f0015572..f9b2c1e899 100644 --- a/app/Repositories/Journal/JournalCLIRepositoryInterface.php +++ b/app/Repositories/Journal/JournalCLIRepositoryInterface.php @@ -33,64 +33,6 @@ use Illuminate\Support\Collection; */ interface JournalCLIRepositoryInterface { - /** - * @param User $user - */ - public function setUser(User $user); - - /** - * Return all tags as strings in an array. - * - * @param TransactionJournal $journal - * - * @return array - */ - public function getTags(TransactionJournal $journal): array; - - /** - * Returns all journals with more than 2 transactions. Should only return empty collections - * in Firefly III > v4.8,0. - * - * @return Collection - */ - public function getSplitJournals(): Collection; - - /** - * Return text of a note attached to journal, or NULL - * - * @param TransactionJournal $journal - * - * @return string|null - */ - public function getNoteText(TransactionJournal $journal): ?string; - - /** - * Return value of a meta field (or NULL). - * - * @param TransactionJournal $journal - * @param string $field - * - * @return null|string - */ - public function getMetaField(TransactionJournal $journal, string $field): ?string; - - /** - * Return Carbon value of a meta field (or NULL). - * - * @param TransactionJournal $journal - * @param string $field - * - * @return null|Carbon - */ - public function getMetaDate(TransactionJournal $journal, string $field): ?Carbon; - - /** - * Return all journals without a group, used in an upgrade routine. - * - * @return array - */ - public function getJournalsWithoutGroup(): array; - /** * Get all transaction journals with a specific type, regardless of user. * @@ -118,4 +60,62 @@ interface JournalCLIRepositoryInterface */ public function getJournalCategoryId(TransactionJournal $journal): int; + /** + * Return all journals without a group, used in an upgrade routine. + * + * @return array + */ + public function getJournalsWithoutGroup(): array; + + /** + * Return Carbon value of a meta field (or NULL). + * + * @param TransactionJournal $journal + * @param string $field + * + * @return null|Carbon + */ + public function getMetaDate(TransactionJournal $journal, string $field): ?Carbon; + + /** + * Return value of a meta field (or NULL). + * + * @param TransactionJournal $journal + * @param string $field + * + * @return null|string + */ + public function getMetaField(TransactionJournal $journal, string $field): ?string; + + /** + * Return text of a note attached to journal, or NULL + * + * @param TransactionJournal $journal + * + * @return string|null + */ + public function getNoteText(TransactionJournal $journal): ?string; + + /** + * Returns all journals with more than 2 transactions. Should only return empty collections + * in Firefly III > v4.8,0. + * + * @return Collection + */ + public function getSplitJournals(): Collection; + + /** + * Return all tags as strings in an array. + * + * @param TransactionJournal $journal + * + * @return array + */ + public function getTags(TransactionJournal $journal): array; + + /** + * @param User $user + */ + public function setUser(User $user); + } diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php index 6fb1b7c16b..45f9e760cc 100644 --- a/app/Repositories/Journal/JournalRepository.php +++ b/app/Repositories/Journal/JournalRepository.php @@ -37,7 +37,6 @@ use FireflyIII\Services\Internal\Update\JournalUpdateService; use FireflyIII\Support\CacheProperties; use FireflyIII\User; use Illuminate\Support\Collection; -use Log; /** * Class JournalRepository. @@ -49,24 +48,6 @@ class JournalRepository implements JournalRepositoryInterface /** @var User */ private $user; - /** - * Search in journal descriptions. - * - * @param string $search - * @param int $limit - * @return Collection - */ - public function searchJournalDescriptions(string $search, int $limit): Collection - { - $query = $this->user->transactionJournals() - ->orderBy('date', 'DESC'); - if ('' !== $query) { - $query->where('description', 'LIKE', sprintf('%%%s%%', $search)); - } - - return $query->take($limit)->get(); - } - /** * @param TransactionGroup $transactionGroup * @@ -89,6 +70,18 @@ class JournalRepository implements JournalRepositoryInterface $service->destroy($journal); } + /** + * @inheritDoc + */ + public function findByType(array $types): Collection + { + return $this->user + ->transactionJournals() + ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') + ->whereIn('transaction_types.type', $types) + ->get(['transaction_journals.*']); + } + /** * Find a specific journal. * @@ -118,11 +111,25 @@ class JournalRepository implements JournalRepositoryInterface return $result; } + /** + * @inheritDoc + */ + public function getDestinationAccount(TransactionJournal $journal): Account + { + /** @var Transaction $transaction */ + $transaction = $journal->transactions()->with('account')->where('amount', '>', 0)->first(); + if (null === $transaction) { + throw new FireflyException(sprintf('Your administration is broken. Transaction journal #%d has no destination transaction.', $journal->id)); + } + + return $transaction->account; + } + /** * Return a list of all destination accounts related to journal. * * @param TransactionJournal $journal - * @param bool $useCache + * @param bool $useCache * * @return Collection */ @@ -150,7 +157,7 @@ class JournalRepository implements JournalRepositoryInterface * Return a list of all source accounts related to journal. * * @param TransactionJournal $journal - * @param bool $useCache + * @param bool $useCache * * @return Collection */ @@ -198,6 +205,21 @@ class JournalRepository implements JournalRepositoryInterface return $amount; } + /** + * @return TransactionJournal|null + */ + public function getLast(): ?TransactionJournal + { + /** @var TransactionJournal $entry */ + $entry = $this->user->transactionJournals()->orderBy('date', 'DESC')->first(['transaction_journals.*']); + $result = null; + if (null !== $entry) { + $result = $entry; + } + + return $result; + } + /** * @param TransactionJournalLink $link * @@ -214,107 +236,6 @@ class JournalRepository implements JournalRepositoryInterface return ''; } - - - - - - - - - /** - * @param int $transactionId - */ - public function reconcileById(int $journalId): void - { - /** @var TransactionJournal $journal */ - $journal = $this->user->transactionJournals()->find($journalId); - if (null !== $journal) { - $journal->transactions()->update(['reconciled' => true]); - } - } - - /** - * @param User $user - */ - public function setUser(User $user): void - { - $this->user = $user; - } - - /** - * Update budget for a journal. - * - * @param TransactionJournal $journal - * @param int $budgetId - * - * @return TransactionJournal - */ - public function updateBudget(TransactionJournal $journal, int $budgetId): TransactionJournal - { - /** @var JournalUpdateService $service */ - $service = app(JournalUpdateService::class); - - $service->setTransactionJournal($journal); - $service->setData( - [ - 'budget_id' => $budgetId, - ] - ); - $service->update(); - $journal->refresh(); - - return $journal; - } - - /** - * Update category for a journal. - * - * @param TransactionJournal $journal - * @param string $category - * - * @return TransactionJournal - */ - public function updateCategory(TransactionJournal $journal, string $category): TransactionJournal - { - /** @var JournalUpdateService $service */ - $service = app(JournalUpdateService::class); - $service->setTransactionJournal($journal); - $service->setData( - [ - 'category_name' => $category, - ] - ); - $service->update(); - $journal->refresh(); - - return $journal; - } - - /** - * Update tag(s) for a journal. - * - * @param TransactionJournal $journal - * @param array $tags - * - * @return TransactionJournal - */ - public function updateTags(TransactionJournal $journal, array $tags): TransactionJournal - { - /** @var JournalUpdateService $service */ - $service = app(JournalUpdateService::class); - $service->setTransactionJournal($journal); - $service->setData( - [ - 'tags' => $tags, - ] - ); - $service->update(); - $journal->refresh(); - - return $journal; - } - /** * Return Carbon value of a meta field (or NULL). * @@ -359,43 +280,114 @@ class JournalRepository implements JournalRepositoryInterface } /** - * @inheritDoc + * @param int $transactionId */ - public function getDestinationAccount(TransactionJournal $journal): Account + public function reconcileById(int $journalId): void { - /** @var Transaction $transaction */ - $transaction = $journal->transactions()->with('account')->where('amount', '>', 0)->first(); - if (null === $transaction) { - throw new FireflyException(sprintf('Your administration is broken. Transaction journal #%d has no destination transaction.', $journal->id)); + /** @var TransactionJournal $journal */ + $journal = $this->user->transactionJournals()->find($journalId); + if (null !== $journal) { + $journal->transactions()->update(['reconciled' => true]); } - - return $transaction->account; } /** - * @return TransactionJournal|null + * Search in journal descriptions. + * + * @param string $search + * @param int $limit + * + * @return Collection */ - public function getLast(): ?TransactionJournal + public function searchJournalDescriptions(string $search, int $limit): Collection { - /** @var TransactionJournal $entry */ - $entry = $this->user->transactionJournals()->orderBy('date', 'DESC')->first(['transaction_journals.*']); - $result = null; - if (null !== $entry) { - $result = $entry; + $query = $this->user->transactionJournals() + ->orderBy('date', 'DESC'); + if ('' !== $query) { + $query->where('description', 'LIKE', sprintf('%%%s%%', $search)); } - return $result; + return $query->take($limit)->get(); } /** - * @inheritDoc + * @param User $user */ - public function findByType(array $types): Collection + public function setUser(User $user): void { - return $this->user - ->transactionJournals() - ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') - ->whereIn('transaction_types.type', $types) - ->get(['transaction_journals.*']); + $this->user = $user; + } + + /** + * Update budget for a journal. + * + * @param TransactionJournal $journal + * @param int $budgetId + * + * @return TransactionJournal + */ + public function updateBudget(TransactionJournal $journal, int $budgetId): TransactionJournal + { + /** @var JournalUpdateService $service */ + $service = app(JournalUpdateService::class); + + $service->setTransactionJournal($journal); + $service->setData( + [ + 'budget_id' => $budgetId, + ] + ); + $service->update(); + $journal->refresh(); + + return $journal; + } + + /** + * Update category for a journal. + * + * @param TransactionJournal $journal + * @param string $category + * + * @return TransactionJournal + */ + public function updateCategory(TransactionJournal $journal, string $category): TransactionJournal + { + /** @var JournalUpdateService $service */ + $service = app(JournalUpdateService::class); + $service->setTransactionJournal($journal); + $service->setData( + [ + 'category_name' => $category, + ] + ); + $service->update(); + $journal->refresh(); + + return $journal; + } + + /** + * Update tag(s) for a journal. + * + * @param TransactionJournal $journal + * @param array $tags + * + * @return TransactionJournal + */ + public function updateTags(TransactionJournal $journal, array $tags): TransactionJournal + { + /** @var JournalUpdateService $service */ + $service = app(JournalUpdateService::class); + $service->setTransactionJournal($journal); + $service->setData( + [ + 'tags' => $tags, + ] + ); + $service->update(); + $journal->refresh(); + + return $journal; } } diff --git a/app/Repositories/Journal/JournalRepositoryInterface.php b/app/Repositories/Journal/JournalRepositoryInterface.php index fd4a27572c..cc5e509514 100644 --- a/app/Repositories/Journal/JournalRepositoryInterface.php +++ b/app/Repositories/Journal/JournalRepositoryInterface.php @@ -36,28 +36,6 @@ use Illuminate\Support\Collection; */ interface JournalRepositoryInterface { - /** - * @return TransactionJournal|null - */ - public function getLast(): ?TransactionJournal; - - /** - * @param array $types - * - * @return Collection - */ - public function findByType(array $types): Collection; - - /** - * Search in journal descriptions. - * - * @param string $search - * @param int $limit - * - * @return Collection - */ - public function searchJournalDescriptions(string $search, int $limit): Collection; - /** * Deletes a transaction group. * @@ -72,6 +50,13 @@ interface JournalRepositoryInterface */ public function destroyJournal(TransactionJournal $journal): void; + /** + * @param array $types + * + * @return Collection + */ + public function findByType(array $types): Collection; + /** * TODO Refactor to "find". * Find a specific journal. @@ -89,13 +74,24 @@ interface JournalRepositoryInterface */ public function firstNull(): ?TransactionJournal; + /** + * Returns the destination account of the journal. + * + * @param TransactionJournal $journal + * + * @return Account + * @throws FireflyException + */ + public function getDestinationAccount(TransactionJournal $journal): Account; + /** * TODO this method is no longer well-fitted in 4.8,0. Should be refactored and/or removed. * Return a list of all destination accounts related to journal. * * @param TransactionJournal $journal - * @deprecated + * * @return Collection + * @deprecated */ public function getJournalDestinationAccounts(TransactionJournal $journal): Collection; @@ -104,11 +100,45 @@ interface JournalRepositoryInterface * Return a list of all source accounts related to journal. * * @param TransactionJournal $journal - * @deprecated + * * @return Collection + * @deprecated */ public function getJournalSourceAccounts(TransactionJournal $journal): Collection; + /** + * Return total amount of journal. Is always positive. + * + * @param TransactionJournal $journal + * + * @return string + */ + public function getJournalTotal(TransactionJournal $journal): string; + + /** + * @return TransactionJournal|null + */ + public function getLast(): ?TransactionJournal; + + /** + * TODO used only in transformer, so only for API use. + * + * @param TransactionJournalLink $link + * + * @return string + */ + public function getLinkNoteText(TransactionJournalLink $link): string; + + /** + * Return Carbon value of a meta field (or NULL). + * + * @param int $journalId + * @param string $field + * + * @return null|Carbon + */ + public function getMetaDateById(int $journalId, string $field): ?Carbon; + /** * Returns the source account of the journal. * @@ -119,47 +149,6 @@ interface JournalRepositoryInterface */ public function getSourceAccount(TransactionJournal $journal): Account; - /** - * Returns the destination account of the journal. - * - * @param TransactionJournal $journal - * @return Account - * @throws FireflyException - */ - public function getDestinationAccount(TransactionJournal $journal): Account; - - /** - * Return total amount of journal. Is always positive. - * - * @param TransactionJournal $journal - * - * @return string - */ - public function getJournalTotal(TransactionJournal $journal): string; - - /** - * TODO used only in transformer, so only for API use. - * @param TransactionJournalLink $link - * - * @return string - */ - public function getLinkNoteText(TransactionJournalLink $link): string; - - - /** - * Return Carbon value of a meta field (or NULL). - * - * @param int $journalId - * @param string $field - * - * @return null|Carbon - */ - public function getMetaDateById(int $journalId, string $field): ?Carbon; - - - - - /** * TODO maybe move to account repository? * @@ -167,6 +156,16 @@ interface JournalRepositoryInterface */ public function reconcileById(int $journalId): void; + /** + * Search in journal descriptions. + * + * @param string $search + * @param int $limit + * + * @return Collection + */ + public function searchJournalDescriptions(string $search, int $limit): Collection; + /** * @param User $user */ @@ -176,7 +175,7 @@ interface JournalRepositoryInterface * Update budget for a journal. * * @param TransactionJournal $journal - * @param int $budgetId + * @param int $budgetId * * @return TransactionJournal */ @@ -186,7 +185,7 @@ interface JournalRepositoryInterface * Update category for a journal. * * @param TransactionJournal $journal - * @param string $category + * @param string $category * * @return TransactionJournal */ @@ -196,7 +195,7 @@ interface JournalRepositoryInterface * Update tag(s) for a journal. * * @param TransactionJournal $journal - * @param array $tags + * @param array $tags * * @return TransactionJournal */ diff --git a/app/Repositories/LinkType/LinkTypeRepository.php b/app/Repositories/LinkType/LinkTypeRepository.php index 99be363dce..6602e06d99 100644 --- a/app/Repositories/LinkType/LinkTypeRepository.php +++ b/app/Repositories/LinkType/LinkTypeRepository.php @@ -24,7 +24,6 @@ namespace FireflyIII\Repositories\LinkType; use Exception; use FireflyIII\Events\DestroyedTransactionLink; -use FireflyIII\Events\RemovedTransactionLink; use FireflyIII\Events\StoredTransactionLink; use FireflyIII\Events\UpdatedTransactionLink; use FireflyIII\Models\LinkType; @@ -41,8 +40,7 @@ use Log; */ class LinkTypeRepository implements LinkTypeRepositoryInterface { - /** @var User */ - private $user; + private User $user; /** * @param LinkType $linkType @@ -59,7 +57,7 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface * @param LinkType $moveTo * * @return bool - * @throws \Exception + * @throws Exception */ public function destroy(LinkType $linkType, LinkType $moveTo = null): bool { @@ -75,7 +73,7 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface * @param TransactionJournalLink $link * * @return bool - * @throws \Exception + * @throws Exception */ public function destroyLink(TransactionJournalLink $link): bool { @@ -109,6 +107,7 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface */ public function findLink(TransactionJournal $one, TransactionJournal $two): bool { + Log::debug(sprintf('Now in findLink(%d, %d)', $one->id, $two->id)); $count = TransactionJournalLink::whereDestinationId($one->id)->whereSourceId($two->id)->count(); $opposingCount = TransactionJournalLink::whereDestinationId($two->id)->whereSourceId($one->id)->count(); @@ -177,7 +176,7 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface public function getJournalLinks(LinkType $linkType = null): Collection { $query = TransactionJournalLink - ::with(['source','destination']) + ::with(['source', 'destination']) ->leftJoin('transaction_journals as source_journals', 'journal_links.source_id', '=', 'source_journals.id') ->leftJoin('transaction_journals as dest_journals', 'journal_links.destination_id', '=', 'dest_journals.id') ->where('source_journals.user_id', $this->user->id) @@ -188,7 +187,8 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface if (null !== $linkType) { $query->where('journal_links.link_type_id', $linkType->id); } - return $query->get(['journal_links.*']); + + return $query->get(['journal_links.*']); } /** @@ -310,9 +310,15 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface */ public function update(LinkType $linkType, array $data): LinkType { - $linkType->name = $data['name']; - $linkType->inward = $data['inward']; - $linkType->outward = $data['outward']; + if (array_key_exists('name', $data) && '' !== (string)$data['name']) { + $linkType->name = $data['name']; + } + if (array_key_exists('inward', $data) && '' !== (string)$data['inward']) { + $linkType->inward = $data['inward']; + } + if (array_key_exists('outward', $data) && '' !== (string)$data['outward']) { + $linkType->outward = $data['outward']; + } $linkType->save(); return $linkType; @@ -328,11 +334,25 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface */ public function updateLink(TransactionJournalLink $journalLink, array $data): TransactionJournalLink { - $journalLink->source_id = $data['inward']->id; - $journalLink->destination_id = $data['outward']->id; - $journalLink->link_type_id = $data['link_type_id']; + $journalLink->source_id = $data['inward_id'] ? $data['inward_id'] : $journalLink->source_id; + $journalLink->destination_id = $data['outward_id'] ? $data['outward_id'] : $journalLink->destination_id; $journalLink->save(); - $this->setNoteText($journalLink, $data['notes']); + if (array_key_exists('link_type_name', $data)) { + $linkType = LinkType::whereName($data['link_type_name'])->first(); + if (null !== $linkType) { + $journalLink->link_type_id = $linkType->id; + $journalLink->save(); + } + $journalLink->refresh(); + } + + $journalLink->link_type_id = $data['link_type_id'] ? $data['link_type_id'] : $journalLink->link_type_id; + + + $journalLink->save(); + if (array_key_exists('notes', $data) && null !== $data['notes']) { + $this->setNoteText($journalLink, $data['notes']); + } event(new UpdatedTransactionLink($journalLink)); @@ -343,7 +363,7 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface * @param TransactionJournalLink $link * @param string $text * - * @throws \Exception + * @throws Exception */ private function setNoteText(TransactionJournalLink $link, string $text): void { @@ -367,4 +387,17 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface } } + + /** + * @inheritDoc + */ + public function getLink(TransactionJournal $one, TransactionJournal $two): ?TransactionJournalLink + { + $left = TransactionJournalLink::whereDestinationId($one->id)->whereSourceId($two->id)->first(); + if (null !== $left) { + return $left; + } + + return TransactionJournalLink::whereDestinationId($two->id)->whereSourceId($one->id)->first(); + } } diff --git a/app/Repositories/ObjectGroup/CreatesObjectGroups.php b/app/Repositories/ObjectGroup/CreatesObjectGroups.php index 5fd1ad3bb5..02691f6089 100644 --- a/app/Repositories/ObjectGroup/CreatesObjectGroups.php +++ b/app/Repositories/ObjectGroup/CreatesObjectGroups.php @@ -32,16 +32,6 @@ use FireflyIII\User; */ trait CreatesObjectGroups { - /** - * @param string $title - * - * @return null|ObjectGroup - */ - protected function findObjectGroup(string $title): ?ObjectGroup - { - return $this->user->objectGroups()->where('title', $title)->first(); - } - /** * @param int $groupId * @@ -79,7 +69,7 @@ trait CreatesObjectGroups */ protected function getObjectGroupMaxOrder(): int { - return (int) $this->user->objectGroups()->max('order'); + return (int)$this->user->objectGroups()->max('order'); } /** @@ -91,4 +81,14 @@ trait CreatesObjectGroups { return 1 === $this->user->objectGroups()->where('title', $title)->count(); } + + /** + * @param string $title + * + * @return null|ObjectGroup + */ + protected function findObjectGroup(string $title): ?ObjectGroup + { + return $this->user->objectGroups()->where('title', $title)->first(); + } } diff --git a/app/Repositories/ObjectGroup/ObjectGroupRepository.php b/app/Repositories/ObjectGroup/ObjectGroupRepository.php index 4522dce4a8..ef16cff48e 100644 --- a/app/Repositories/ObjectGroup/ObjectGroupRepository.php +++ b/app/Repositories/ObjectGroup/ObjectGroupRepository.php @@ -36,8 +36,50 @@ use Log; */ class ObjectGroupRepository implements ObjectGroupRepositoryInterface { - /** @var User */ - private $user; + private User $user; + + /** + * @inheritDoc + */ + public function deleteAll(): void + { + $all = $this->get(); + /** @var ObjectGroup $group */ + foreach ($all as $group) { + $group->piggyBanks()->sync([]); + $group->bills()->sync([]); + $group->delete(); + } + } + + /** + * @inheritDoc + */ + public function deleteEmpty(): void + { + $all = $this->get(); + /** @var ObjectGroup $group */ + foreach ($all as $group) { + $count = DB::table('object_groupables')->where('object_groupables.object_group_id', $group->id)->count(); + if (0 === $count) { + $group->delete(); + } + } + } + + /** + * @inheritDoc + */ + public function destroy(ObjectGroup $objectGroup): void + { + $list = $objectGroup->piggyBanks; + /** @var PiggyBank $piggy */ + foreach ($list as $piggy) { + $piggy->objectGroups()->sync([]); + $piggy->save(); + } + $objectGroup->delete(); + } /** * @inheritDoc @@ -46,12 +88,29 @@ class ObjectGroupRepository implements ObjectGroupRepositoryInterface { return $this->user->objectGroups() ->with(['piggyBanks', 'bills']) - ->orderBy('order', 'ASC')->orderBy('title', 'ASC')->get(); + ->orderBy('order', 'ASC') + ->orderBy('title', 'ASC')->get(); + } + + /** + * @inheritDoc + */ + public function getBills(ObjectGroup $objectGroup): Collection + { + return $objectGroup->bills; + } + + /** + * @inheritDoc + */ + public function getPiggyBanks(ObjectGroup $objectGroup): Collection + { + return $objectGroup->piggyBanks; } /** * @param string $query - * @param int $limit + * @param int $limit * * @return Collection */ @@ -74,44 +133,28 @@ class ObjectGroupRepository implements ObjectGroupRepositoryInterface /** * @inheritDoc */ - public function deleteEmpty(): void + public function setOrder(ObjectGroup $objectGroup, int $newOrder): ObjectGroup { - $all = $this->get(); - /** @var ObjectGroup $group */ - foreach ($all as $group) { - $count = DB::table('object_groupables')->where('object_groupables.object_group_id', $group->id)->count(); - if (0 === $count) { - $group->delete(); - } + $oldOrder = (int)$objectGroup->order; + + if ($newOrder > $oldOrder) { + $this->user->objectGroups()->where('object_groups.order', '<=', $newOrder)->where('object_groups.order', '>', $oldOrder) + ->where('object_groups.id', '!=', $objectGroup->id) + ->decrement('object_groups.order', 1); + + $objectGroup->order = $newOrder; + $objectGroup->save(); } - } + if ($newOrder < $oldOrder) { + $this->user->objectGroups()->where('object_groups.order', '>=', $newOrder)->where('object_groups.order', '<', $oldOrder) + ->where('object_groups.id', '!=', $objectGroup->id) + ->increment('object_groups.order', 1); - /** - * @inheritDoc - */ - public function sort(): void - { - $all = $this->get(); - /** - * @var int $index - * @var ObjectGroup $group - */ - foreach ($all as $index => $group) { - $group->order = $index + 1; - $group->save(); + $objectGroup->order = $newOrder; + $objectGroup->save(); } - } - /** - * @inheritDoc - */ - public function setOrder(ObjectGroup $objectGroup, int $order): ObjectGroup - { - $order = 0 === $order ? 1 : $order; - $objectGroup->order = $order; - $objectGroup->save(); - - Log::debug(sprintf('Objectgroup #%d order is now %d', $objectGroup->id, $order)); + Log::debug(sprintf('Objectgroup #%d order is now %d', $objectGroup->id, $newOrder)); return $objectGroup; } @@ -121,11 +164,12 @@ class ObjectGroupRepository implements ObjectGroupRepositoryInterface */ public function update(ObjectGroup $objectGroup, array $data): ObjectGroup { - $objectGroup->title = $data['title']; + if(array_key_exists('title', $data)) { + $objectGroup->title = $data['title']; + } - if (isset($data['order'])) { - $order = 0 === $data['order'] ? 1 : $data['order']; - $objectGroup->order = $order; + if(array_key_exists('order', $data)) { + $this->setOrder($objectGroup, (int)$data['order']); } $objectGroup->save(); @@ -133,20 +177,6 @@ class ObjectGroupRepository implements ObjectGroupRepositoryInterface return $objectGroup; } - /** - * @inheritDoc - */ - public function destroy(ObjectGroup $objectGroup): void - { - $list = $objectGroup->piggyBanks; - /** @var PiggyBank $piggy */ - foreach($list as $piggy) { - $piggy->objectGroups()->sync([]); - $piggy->save(); - } - $objectGroup->delete(); - } - /** * @param User $user */ @@ -158,22 +188,21 @@ class ObjectGroupRepository implements ObjectGroupRepositoryInterface /** * @inheritDoc */ - public function getPiggyBanks(ObjectGroup $objectGroup): Collection + public function resetOrder(): void { - return $objectGroup->piggyBanks; - } - - /** - * @inheritDoc - */ - public function deleteAll(): void - { - $all = $this->get(); - /** @var ObjectGroup $group */ - foreach ($all as $group) { - $group->piggyBanks()->sync([]); - $group->bills()->sync([]); - $group->delete(); + Log::debug('Now in resetOrder'); + $list = $this->get(); + $index = 1; + /** @var ObjectGroup $objectGroup */ + foreach ($list as $objectGroup) { + if ($index !== (int)$objectGroup->order) { + Log::debug( + sprintf('objectGroup #%d ("%s"): order should %d be but is %d.', $objectGroup->id, $objectGroup->title, $index, $objectGroup->order) + ); + $objectGroup->order = $index; + $objectGroup->save(); + } + $index++; } } } diff --git a/app/Repositories/ObjectGroup/ObjectGroupRepositoryInterface.php b/app/Repositories/ObjectGroup/ObjectGroupRepositoryInterface.php index 0a8d8b2763..be3c9a84a4 100644 --- a/app/Repositories/ObjectGroup/ObjectGroupRepositoryInterface.php +++ b/app/Repositories/ObjectGroup/ObjectGroupRepositoryInterface.php @@ -33,26 +33,36 @@ use Illuminate\Support\Collection; interface ObjectGroupRepositoryInterface { /** - * @return Collection + * Delete all. */ - public function get(): Collection; + public function deleteAll(): void; /** - * @param string $query - * @param int $limit - * - * @return Collection + * Delete all. */ - public function search(string $query, int $limit): Collection; + public function resetOrder(): void; /** * Delete empty ones. */ public function deleteEmpty(): void; + /** - * Delete all. + * @param ObjectGroup $objectGroup */ - public function deleteAll(): void; + public function destroy(ObjectGroup $objectGroup): void; + + /** + * @return Collection + */ + public function get(): Collection; + + /** + * @param ObjectGroup $objectGroup + * + * @return Collection + */ + public function getBills(ObjectGroup $objectGroup): Collection; /** * @param ObjectGroup $objectGroup @@ -62,17 +72,20 @@ interface ObjectGroupRepositoryInterface public function getPiggyBanks(ObjectGroup $objectGroup): Collection; /** - * Sort + * @param string $query + * @param int $limit + * + * @return Collection */ - public function sort(): void; + public function search(string $query, int $limit): Collection; /** * @param ObjectGroup $objectGroup - * @param int $index + * @param int $newOrder * * @return ObjectGroup */ - public function setOrder(ObjectGroup $objectGroup, int $index): ObjectGroup; + public function setOrder(ObjectGroup $objectGroup, int $newOrder): ObjectGroup; /** * @param ObjectGroup $objectGroup @@ -82,9 +95,4 @@ interface ObjectGroupRepositoryInterface */ public function update(ObjectGroup $objectGroup, array $data): ObjectGroup; - /** - * @param ObjectGroup $objectGroup - */ - public function destroy(ObjectGroup $objectGroup): void; - } diff --git a/app/Repositories/ObjectGroup/OrganisesObjectGroups.php b/app/Repositories/ObjectGroup/OrganisesObjectGroups.php index 62e75ee93b..1b1e53aaa7 100644 --- a/app/Repositories/ObjectGroup/OrganisesObjectGroups.php +++ b/app/Repositories/ObjectGroup/OrganisesObjectGroups.php @@ -53,6 +53,6 @@ trait OrganisesObjectGroups private function sortObjectGroups(): void { $repository = app(ObjectGroupRepositoryInterface::class); - $repository->sort(); + $repository->resetOrder(); } } diff --git a/app/Repositories/PiggyBank/ModifiesPiggyBanks.php b/app/Repositories/PiggyBank/ModifiesPiggyBanks.php index ce01799a0d..5aad8ddb3f 100644 --- a/app/Repositories/PiggyBank/ModifiesPiggyBanks.php +++ b/app/Repositories/PiggyBank/ModifiesPiggyBanks.php @@ -26,7 +26,6 @@ namespace FireflyIII\Repositories\PiggyBank; use Carbon\Carbon; -use DB; use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Note; @@ -35,7 +34,6 @@ use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\PiggyBankRepetition; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups; -use FireflyIII\User; use Illuminate\Database\QueryException; use Log; @@ -68,17 +66,6 @@ trait ModifiesPiggyBanks return true; } - /** - * @inheritDoc - */ - public function removeObjectGroup(PiggyBank $piggyBank): PiggyBank - { - $piggyBank->objectGroups()->sync([]); - - return $piggyBank; - } - - /** * @param PiggyBankRepetition $repetition * @param string $amount @@ -105,7 +92,7 @@ trait ModifiesPiggyBanks $leftOnAccount = $this->leftOnAccount($piggyBank, today(config('app.timezone'))); $savedSoFar = (string)$this->getRepetition($piggyBank)->currentamount; $leftToSave = bcsub($piggyBank->targetamount, $savedSoFar); - $maxAmount = (string)min(round((float) $leftOnAccount, 12), round((float)$leftToSave, 12)); + $maxAmount = (string)min(round((float)$leftOnAccount, 12), round((float)$leftToSave, 12)); $compare = bccomp($amount, $maxAmount); $result = $compare <= 0; @@ -138,12 +125,13 @@ trait ModifiesPiggyBanks /** * Correct order of piggies in case of issues. */ - public function correctOrder(): void + public function resetOrder(): void { - $set = $this->user->piggyBanks()->orderBy('order', 'ASC')->get(); + $set = $this->user->piggyBanks()->orderBy('piggy_banks.order', 'ASC')->get(['piggy_banks.*']); $current = 1; foreach ($set as $piggyBank) { if ((int)$piggyBank->order !== $current) { + Log::debug(sprintf('Piggy bank #%d ("%s") was at place %d but should be on %d', $piggyBank->id, $piggyBank->name, $piggyBank->order, $current)); $piggyBank->order = $current; $piggyBank->save(); } @@ -188,7 +176,7 @@ trait ModifiesPiggyBanks * @param PiggyBank $piggyBank * * @return bool - * @throws \Exception + * @throws Exception */ public function destroy(PiggyBank $piggyBank): bool { @@ -219,6 +207,16 @@ trait ModifiesPiggyBanks return true; } + /** + * @inheritDoc + */ + public function removeObjectGroup(PiggyBank $piggyBank): PiggyBank + { + $piggyBank->objectGroups()->sync([]); + + return $piggyBank; + } + /** * @param PiggyBank $piggyBank * @param string $amount @@ -245,23 +243,6 @@ trait ModifiesPiggyBanks return $piggyBank; } - /** - * set id of piggy bank. - * - * @param PiggyBank $piggyBank - * @param int $order - * - * @return bool - */ - public function setOrder(PiggyBank $piggyBank, int $order): bool - { - $piggyBank->order = $order; - $piggyBank->save(); - - return true; - } - - /** * @inheritDoc */ @@ -276,6 +257,33 @@ trait ModifiesPiggyBanks } + /** + * @inheritDoc + */ + public function setOrder(PiggyBank $piggyBank, int $newOrder): bool + { + $oldOrder = (int)$piggyBank->order; + Log::debug(sprintf('Will move piggy bank #%d ("%s") from %d to %d', $piggyBank->id, $piggyBank->name, $oldOrder, $newOrder)); + if ($newOrder > $oldOrder) { + $this->user->piggyBanks()->where('piggy_banks.order', '<=', $newOrder)->where('piggy_banks.order', '>', $oldOrder) + ->where('piggy_banks.id', '!=', $piggyBank->id) + ->decrement('piggy_banks.order', 1); + $piggyBank->order = $newOrder; + Log::debug(sprintf('Order of piggy #%d ("%s") is now %d', $piggyBank->id, $piggyBank->name, $newOrder)); + $piggyBank->save(); + + return true; + } + + $this->user->piggyBanks()->where('piggy_banks.order', '>=', $newOrder)->where('piggy_banks.order', '<', $oldOrder) + ->where('piggy_banks.id', '!=', $piggyBank->id) + ->increment('piggy_banks.order', 1); + $piggyBank->order = $newOrder; + Log::debug(sprintf('Order of piggy #%d ("%s") is now %d', $piggyBank->id, $piggyBank->name, $newOrder)); + $piggyBank->save(); + + return true; + } /** * @param array $data @@ -285,15 +293,26 @@ trait ModifiesPiggyBanks */ public function store(array $data): PiggyBank { - $data['order'] = $this->getMaxOrder() + 1; + $order = $this->getMaxOrder() + 1; + if (array_key_exists('order', $data)) { + $order = $data['order']; + } + $data['order'] = 31337; // very high when creating. + $piggyData = $data; + // unset fields just in case. + unset($piggyData['object_group_title'], $piggyData['object_group_id'], $piggyData['notes'], $piggyData['current_amount']); try { /** @var PiggyBank $piggyBank */ - $piggyBank = PiggyBank::create($data); + $piggyBank = PiggyBank::create($piggyData); } catch (QueryException $e) { - Log::error(sprintf('Could not store piggy bank: %s', $e->getMessage())); + Log::error(sprintf('Could not store piggy bank: %s', $e->getMessage()), $piggyData); throw new FireflyException('400005: Could not store new piggy bank.'); } + // reset order then set order: + $this->resetOrder(); + $this->setOrder($piggyBank, $order); + $this->updateNote($piggyBank, $data['notes']); // repetition is auto created. @@ -303,15 +322,16 @@ trait ModifiesPiggyBanks $repetition->save(); } - $objectGroupTitle = $data['object_group'] ?? ''; + $objectGroupTitle = $data['object_group_title'] ?? ''; if ('' !== $objectGroupTitle) { $objectGroup = $this->findOrCreateObjectGroup($objectGroupTitle); if (null !== $objectGroup) { $piggyBank->objectGroups()->sync([$objectGroup->id]); $piggyBank->save(); } + } - // try also with ID: + // try also with ID $objectGroupId = (int)($data['object_group_id'] ?? 0); if (0 !== $objectGroupId) { $objectGroup = $this->findObjectGroupById($objectGroupId); @@ -324,32 +344,6 @@ trait ModifiesPiggyBanks return $piggyBank; } - /** - * @param PiggyBank $piggyBank - * @param array $data - * - * @return PiggyBank - */ - private function updateProperties(PiggyBank $piggyBank, array $data): PiggyBank - { - if (array_key_exists('name', $data) && '' !== $data['name']) { - $piggyBank->name = $data['name']; - } - if (array_key_exists('account_id', $data) && 0 !== $data['account_id']) { - $piggyBank->account_id = (int)$data['account_id']; - } - if (array_key_exists('targetamount', $data) && '' !== $data['targetamount']) { - $piggyBank->targetamount = $data['targetamount']; - } - if (array_key_exists('targetdate', $data) && '' !== $data['targetdate']) { - $piggyBank->targetdate = $data['targetdate']; - } - $piggyBank->startdate = $data['startdate'] ?? $piggyBank->startdate; - $piggyBank->save(); - - return $piggyBank; - } - /** * @param PiggyBank $piggyBank * @param array $data @@ -359,13 +353,15 @@ trait ModifiesPiggyBanks public function update(PiggyBank $piggyBank, array $data): PiggyBank { $piggyBank = $this->updateProperties($piggyBank, $data); - $this->updateNote($piggyBank, $data['notes'] ?? ''); + if (array_key_exists('notes', $data)) { + $this->updateNote($piggyBank, (string)$data['notes']); + } // update the order of the piggy bank: $oldOrder = (int)$piggyBank->order; $newOrder = (int)($data['order'] ?? $oldOrder); if ($oldOrder !== $newOrder) { - $this->updateOrder($piggyBank, $oldOrder, $newOrder); + $this->setOrder($piggyBank, $newOrder); } // if the piggy bank is now smaller than the current relevant rep, @@ -380,8 +376,8 @@ trait ModifiesPiggyBanks } // update using name: - if (array_key_exists('object_group', $data)) { - $objectGroupTitle = (string)$data['object_group']; + if (array_key_exists('object_group_title', $data)) { + $objectGroupTitle = (string)$data['object_group_title']; if ('' !== $objectGroupTitle) { $objectGroup = $this->findOrCreateObjectGroup($objectGroupTitle); if (null !== $objectGroup) { @@ -448,36 +444,30 @@ trait ModifiesPiggyBanks /** * @param PiggyBank $piggyBank - * @param int $oldOrder - * @param int $newOrder + * @param array $data + * + * @return PiggyBank */ - private function updateOrder(PiggyBank $piggyBank, int $oldOrder, int $newOrder): void + private function updateProperties(PiggyBank $piggyBank, array $data): PiggyBank { - if ($newOrder > $oldOrder) { - // Iedereen [7 en lager] [hoger dan 3] behalve piggy zelf, puntje er af: - //piggy zelf naar 7 - /** @var User $user */ - $user = $this->user; - $user->piggyBanks()->where('piggy_banks.order', '<=', $newOrder)->where('piggy_banks.order', '>', $oldOrder) - ->where('piggy_banks.id', '!=', $piggyBank->id) - ->update(['piggy_banks.order' => DB::raw('piggy_banks.order-1')]); - $piggyBank->order = $newOrder; - $piggyBank->save(); + if (array_key_exists('name', $data) && '' !== $data['name']) { + $piggyBank->name = $data['name']; } - if ($newOrder < $oldOrder) { - // - //Van 8 naar 2 - // iedereen [2 of hoger] en [kleiner dan 8] puntje er bij. - // 8 naar 2 - /** @var User $user */ - $user = $this->user; - $user->piggyBanks()->where('piggy_banks.order', '>=', $newOrder)->where('piggy_banks.order', '<', $oldOrder) - ->where('piggy_banks.id', '!=', $piggyBank->id) - ->update(['piggy_banks.order' => DB::raw('piggy_banks.order+1')]); - $piggyBank->order = $newOrder; - $piggyBank->save(); + if (array_key_exists('account_id', $data) && 0 !== $data['account_id']) { + $piggyBank->account_id = (int)$data['account_id']; } + if (array_key_exists('targetamount', $data) && '' !== $data['targetamount']) { + $piggyBank->targetamount = $data['targetamount']; + } + if (array_key_exists('targetdate', $data) && '' !== $data['targetdate']) { + $piggyBank->targetdate = $data['targetdate']; + } + if (array_key_exists('startdate', $data)) { + $piggyBank->startdate = $data['startdate']; + } + $piggyBank->save(); + + return $piggyBank; } - } diff --git a/app/Repositories/PiggyBank/PiggyBankRepository.php b/app/Repositories/PiggyBank/PiggyBankRepository.php index 3d9506e81d..82bb5d5ab6 100644 --- a/app/Repositories/PiggyBank/PiggyBankRepository.php +++ b/app/Repositories/PiggyBank/PiggyBankRepository.php @@ -46,6 +46,14 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface private User $user; + /** + * @inheritDoc + */ + public function destroyAll(): void + { + $this->user->piggyBanks()->delete(); + } + /** * Find by name or return NULL. * @@ -74,7 +82,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface } /** - * @param int|null $piggyBankId + * @param int|null $piggyBankId * @param string|null $piggyBankName * * @return PiggyBank|null @@ -104,6 +112,27 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface return null; } + /** + * @inheritDoc + */ + public function getAttachments(PiggyBank $piggyBank): Collection + { + $set = $piggyBank->attachments()->get(); + + /** @var Storage $disk */ + $disk = Storage::disk('upload'); + + return $set->each( + static function (Attachment $attachment) use ($disk) { + $notes = $attachment->notes()->first(); + $attachment->file_exists = $disk->exists($attachment->fileName()); + $attachment->notes = $notes ? $notes->text : ''; + + return $attachment; + } + ); + } + /** * Get current amount saved in piggy bank. * @@ -134,9 +163,9 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface /** * Used for connecting to a piggy bank. * - * @param PiggyBank $piggyBank + * @param PiggyBank $piggyBank * @param PiggyBankRepetition $repetition - * @param TransactionJournal $journal + * @param TransactionJournal $journal * * @return string * @@ -155,7 +184,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface $accountRepos = app(AccountRepositoryInterface::class); $accountRepos->setUser($this->user); - $defaultCurrency = app('amount')->getDefaultCurrencyByUser($this->user); + $defaultCurrency = app('amount')->getDefaultCurrencyByUser($this->user); $piggyBankCurrency = $accountRepos->getAccountCurrency($piggyBank->account) ?? $defaultCurrency; Log::debug(sprintf('Piggy bank #%d currency is %s', $piggyBank->id, $piggyBankCurrency->code)); @@ -180,6 +209,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface } if (null === $operator || null === $currency) { Log::debug('Currency is NULL and operator is NULL, return "0".'); + return '0'; } // currency of the account + the piggy bank currency are almost the same. @@ -195,13 +225,14 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface } if (null === $amount) { Log::debug('No match on currency, so amount remains null, return "0".'); + return '0'; } Log::debug(sprintf('The currency is %s and the amount is %s', $currency->code, $amount)); - $room = bcsub((string)$piggyBank->targetamount, (string)$repetition->currentamount); + $room = bcsub((string)$piggyBank->targetamount, (string)$repetition->currentamount); $compare = bcmul($repetition->currentamount, '-1'); Log::debug(sprintf('Will add/remove %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); @@ -211,6 +242,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface Log::debug(sprintf('Room in piggy bank for extra money is %f', $room)); 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)); + return $room; } @@ -219,6 +251,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface Log::debug(sprintf('Max amount to remove is %f', $repetition->currentamount)); Log::debug(sprintf('Cannot remove %f from piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); Log::debug(sprintf('New amount is %f', $compare)); + return $compare; } @@ -259,7 +292,6 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface return $this->user->piggyBanks()->with(['account', 'objectGroups'])->orderBy('order', 'ASC')->get(); } - /** * Also add amount in name. * @@ -275,7 +307,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface /** @var PiggyBank $piggy */ foreach ($set as $piggy) { $currentAmount = $this->getRepetition($piggy)->currentamount ?? '0'; - $piggy->name = $piggy->name . ' (' . app('amount')->formatAnything($currency, $currentAmount, false) . ')'; + $piggy->name = $piggy->name . ' (' . app('amount')->formatAnything($currency, $currentAmount, false) . ')'; } @@ -303,13 +335,13 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface public function getSuggestedMonthlyAmount(PiggyBank $piggyBank): string { $savePerMonth = '0'; - $repetition = $this->getRepetition($piggyBank); + $repetition = $this->getRepetition($piggyBank); if (null === $repetition) { return $savePerMonth; } if (null !== $piggyBank->targetdate && $repetition->currentamount < $piggyBank->targetamount) { - $now = Carbon::now(); - $diffInMonths = $now->diffInMonths($piggyBank->targetdate, false); + $now = Carbon::now(); + $diffInMonths = $now->diffInMonths($piggyBank->targetdate, false); $remainingAmount = bcsub($piggyBank->targetamount, $repetition->currentamount); // more than 1 month to go and still need money to save: @@ -330,7 +362,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface * Get for piggy account what is left to put in piggies. * * @param PiggyBank $piggyBank - * @param Carbon $date + * @param Carbon $date * * @return string */ @@ -353,45 +385,6 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface return $balance; } - /** - * @param User $user - */ - public function setUser(User $user): void - { - $this->user = $user; - } - - - /** - * @inheritDoc - */ - public function getAttachments(PiggyBank $piggyBank): Collection - { - $set = $piggyBank->attachments()->get(); - - /** @var Storage $disk */ - $disk = Storage::disk('upload'); - - return $set->each( - static function (Attachment $attachment) use ($disk) { - $notes = $attachment->notes()->first(); - $attachment->file_exists = $disk->exists($attachment->fileName()); - $attachment->notes = $notes ? $notes->text : ''; - - return $attachment; - } - ); - } - - - /** - * @inheritDoc - */ - public function destroyAll(): void - { - $this->user->piggyBanks()->delete(); - } - /** * @inheritDoc */ @@ -402,8 +395,16 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface $search->where('piggy_banks.name', 'LIKE', sprintf('%%%s%%', $query)); } $search->orderBy('piggy_banks.order', 'ASC') - ->orderBy('piggy_banks.name', 'ASC'); + ->orderBy('piggy_banks.name', 'ASC'); return $search->take($limit)->get(); } + + /** + * @param User $user + */ + public function setUser(User $user): void + { + $this->user = $user; + } } diff --git a/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php b/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php index e8848c6912..3f891f2c94 100644 --- a/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php +++ b/app/Repositories/PiggyBank/PiggyBankRepositoryInterface.php @@ -36,41 +36,6 @@ use Illuminate\Support\Collection; */ interface PiggyBankRepositoryInterface { - /** - * - */ - public function destroyAll(): void; - - /** - * @param PiggyBank $piggyBank - * @param string $objectGroupTitle - * - * @return PiggyBank - */ - public function setObjectGroup(PiggyBank $piggyBank, string $objectGroupTitle): PiggyBank; - - /** - * @param PiggyBank $piggyBank - * - * @return PiggyBank - */ - public function removeObjectGroup(PiggyBank $piggyBank): PiggyBank; - - /** - * @param PiggyBank $piggyBank - * @param string $amount - * - * @return PiggyBank - */ - public function setCurrentAmount(PiggyBank $piggyBank, string $amount): PiggyBank; - - /** - * @param PiggyBank $piggyBank - * - * @return Collection - */ - public function getAttachments(PiggyBank $piggyBank): Collection; - /** * @param PiggyBank $piggyBank * @param string $amount @@ -106,7 +71,7 @@ interface PiggyBankRepositoryInterface /** * Correct order of piggies in case of issues. */ - public function correctOrder(): void; + public function resetOrder(): void; /** * Create a new event. @@ -136,6 +101,11 @@ interface PiggyBankRepositoryInterface */ public function destroy(PiggyBank $piggyBank): bool; + /** + * + */ + public function destroyAll(): void; + /** * Find by name or return NULL. * @@ -145,16 +115,6 @@ interface PiggyBankRepositoryInterface */ public function findByName(string $name): ?PiggyBank; - /** - * Search for piggy banks. - * - * @param string $query - * @param int $limit - * - * @return Collection - */ - public function searchPiggyBank(string $query, int $limit): Collection; - /** * @param int $piggyBankId * @@ -163,13 +123,20 @@ interface PiggyBankRepositoryInterface public function findNull(int $piggyBankId): ?PiggyBank; /** - * @param int|null $piggyBankId - * @param string|null $piggyBankName + * @param int|null $piggyBankId + * @param string|null $piggyBankName * * @return PiggyBank|null */ public function findPiggyBank(?int $piggyBankId, ?string $piggyBankName): ?PiggyBank; + /** + * @param PiggyBank $piggyBank + * + * @return Collection + */ + public function getAttachments(PiggyBank $piggyBank): Collection; + /** * Get current amount saved in piggy bank. * @@ -263,15 +230,48 @@ interface PiggyBankRepositoryInterface */ public function removeAmount(PiggyBank $piggyBank, string $amount): bool; + /** + * @param PiggyBank $piggyBank + * + * @return PiggyBank + */ + public function removeObjectGroup(PiggyBank $piggyBank): PiggyBank; + + /** + * Search for piggy banks. + * + * @param string $query + * @param int $limit + * + * @return Collection + */ + public function searchPiggyBank(string $query, int $limit): Collection; + + /** + * @param PiggyBank $piggyBank + * @param string $amount + * + * @return PiggyBank + */ + public function setCurrentAmount(PiggyBank $piggyBank, string $amount): PiggyBank; + + /** + * @param PiggyBank $piggyBank + * @param string $objectGroupTitle + * + * @return PiggyBank + */ + public function setObjectGroup(PiggyBank $piggyBank, string $objectGroupTitle): PiggyBank; + /** * Set specific piggy bank to specific order. * * @param PiggyBank $piggyBank - * @param int $order + * @param int $newOrder * * @return bool */ - public function setOrder(PiggyBank $piggyBank, int $order): bool; + public function setOrder(PiggyBank $piggyBank, int $newOrder): bool; /** * @param User $user diff --git a/app/Repositories/Recurring/RecurringRepository.php b/app/Repositories/Recurring/RecurringRepository.php index 97671ed50e..80ba813e26 100644 --- a/app/Repositories/Recurring/RecurringRepository.php +++ b/app/Repositories/Recurring/RecurringRepository.php @@ -53,6 +53,7 @@ use Log; class RecurringRepository implements RecurringRepositoryInterface { use CalculateRangeOccurrences, CalculateXOccurrences, CalculateXOccurrencesSince, FiltersWeekends; + private User $user; @@ -68,6 +69,14 @@ class RecurringRepository implements RecurringRepositoryInterface $service->destroy($recurrence); } + /** + * @inheritDoc + */ + public function destroyAll(): void + { + $this->user->recurrences()->delete(); + } + /** * Returns all of the user's recurring transactions. * @@ -377,6 +386,50 @@ class RecurringRepository implements RecurringRepositoryInterface return $this->filterWeekends($repetition, $occurrences); } + /** + * Calculate the next X iterations starting on the date given in $date. + * Returns an array of Carbon objects. + * + * Only returns them of they are after $afterDate + * + * @param RecurrenceRepetition $repetition + * @param Carbon $date + * @param Carbon $afterDate + * @param int $count + * + * @return array + * @throws FireflyException + */ + public function getXOccurrencesSince(RecurrenceRepetition $repetition, Carbon $date, Carbon $afterDate, int $count): array + { + Log::debug('Now in getXOccurrencesSince()'); + $skipMod = $repetition->repetition_skip + 1; + $occurrences = []; + if ('daily' === $repetition->repetition_type) { + $occurrences = $this->getXDailyOccurrencesSince($date, $afterDate, $count, $skipMod); + } + if ('weekly' === $repetition->repetition_type) { + $occurrences = $this->getXWeeklyOccurrencesSince($date, $afterDate, $count, $skipMod, $repetition->repetition_moment); + } + if ('monthly' === $repetition->repetition_type) { + $occurrences = $this->getXMonthlyOccurrencesSince($date, $afterDate, $count, $skipMod, $repetition->repetition_moment); + } + if ('ndom' === $repetition->repetition_type) { + $occurrences = $this->getXNDomOccurrencesSince($date, $afterDate, $count, $skipMod, $repetition->repetition_moment); + } + if ('yearly' === $repetition->repetition_type) { + $occurrences = $this->getXYearlyOccurrencesSince($date, $afterDate, $count, $skipMod, $repetition->repetition_moment); + } + + // filter out all the weekend days: + $occurrences = $this->filterWeekends($repetition, $occurrences); + + // filter out everything if "repeat_until" is set. + $repeatUntil = $repetition->recurrence->repeat_until; + + return $this->filterMaxDate($repeatUntil, $occurrences); + } + /** * Parse the repetition in a string that is user readable. * @@ -436,6 +489,21 @@ class RecurringRepository implements RecurringRepositoryInterface } + /** + * @inheritDoc + */ + public function searchRecurrence(string $query, int $limit): Collection + { + $search = $this->user->recurrences(); + if ('' !== $query) { + $search->where('recurrences.title', 'LIKE', sprintf('%%%s%%', $query)); + } + $search + ->orderBy('recurrences.title', 'ASC'); + + return $search->take($limit)->get(['id', 'title', 'description']); + } + /** * Set user for in repository. * @@ -465,6 +533,30 @@ class RecurringRepository implements RecurringRepositoryInterface return $result; } + /** + * @inheritDoc + */ + public function totalTransactions(Recurrence $recurrence, RecurrenceRepetition $repetition): int + { + // if repeat = null just return 0. + if (null === $recurrence->repeat_until && 0 === (int)$recurrence->repetitions) { + return 0; + } + // expect X transactions then stop. Return that number + if (null === $recurrence->repeat_until && 0 !== (int)$recurrence->repetitions) { + return (int)$recurrence->repetitions; + } + + // need to calculate, this depends on the repetition: + if (null !== $recurrence->repeat_until && 0 === (int)$recurrence->repetitions) { + $occurrences = $this->getOccurrencesInRange($repetition, $recurrence->first_date ?? today(), $recurrence->repeat_until); + + return count($occurrences); + } + + return 0; + } + /** * Update a recurring transaction. * @@ -478,53 +570,9 @@ class RecurringRepository implements RecurringRepositoryInterface { /** @var RecurrenceUpdateService $service */ $service = app(RecurrenceUpdateService::class); - return $service->update($recurrence, $data); } - /** - * Calculate the next X iterations starting on the date given in $date. - * Returns an array of Carbon objects. - * - * Only returns them of they are after $afterDate - * - * @param RecurrenceRepetition $repetition - * @param Carbon $date - * @param Carbon $afterDate - * @param int $count - * - * @return array - * @throws FireflyException - */ - public function getXOccurrencesSince(RecurrenceRepetition $repetition, Carbon $date, Carbon $afterDate, int $count): array - { - Log::debug('Now in getXOccurrencesSince()'); - $skipMod = $repetition->repetition_skip + 1; - $occurrences = []; - if ('daily' === $repetition->repetition_type) { - $occurrences = $this->getXDailyOccurrencesSince($date, $afterDate, $count, $skipMod); - } - if ('weekly' === $repetition->repetition_type) { - $occurrences = $this->getXWeeklyOccurrencesSince($date, $afterDate, $count, $skipMod, $repetition->repetition_moment); - } - if ('monthly' === $repetition->repetition_type) { - $occurrences = $this->getXMonthlyOccurrencesSince($date, $afterDate, $count, $skipMod, $repetition->repetition_moment); - } - if ('ndom' === $repetition->repetition_type) { - $occurrences = $this->getXNDomOccurrencesSince($date, $afterDate, $count, $skipMod, $repetition->repetition_moment); - } - if ('yearly' === $repetition->repetition_type) { - $occurrences = $this->getXYearlyOccurrencesSince($date, $afterDate, $count, $skipMod, $repetition->repetition_moment); - } - - // filter out all the weekend days: - $occurrences = $this->filterWeekends($repetition, $occurrences); - - // filter out everything if "repeat_until" is set. - $repeatUntil = $repetition->recurrence->repeat_until; - return $this->filterMaxDate($repeatUntil, $occurrences); - } - /** * @param Carbon|null $max * @param array $occurrences @@ -545,36 +593,4 @@ class RecurringRepository implements RecurringRepositoryInterface return $filtered; } - - /** - * @inheritDoc - */ - public function destroyAll(): void - { - $this->user->recurrences()->delete(); - } - - /** - * @inheritDoc - */ - public function totalTransactions(Recurrence $recurrence, RecurrenceRepetition $repetition): int - { - // if repeat = null just return 0. - if (null === $recurrence->repeat_until && 0 === (int) $recurrence->repetitions) { - return 0; - } - // expect X transactions then stop. Return that number - if (null === $recurrence->repeat_until && 0 !== (int) $recurrence->repetitions) { - return (int) $recurrence->repetitions; - } - - // need to calculate, this depends on the repetition: - if (null !== $recurrence->repeat_until && 0 === (int) $recurrence->repetitions) { - $occurrences = $this->getOccurrencesInRange($repetition, $recurrence->first_date ?? today(), $recurrence->repeat_until); - - return count($occurrences); - } - - return 0; - } } diff --git a/app/Repositories/Recurring/RecurringRepositoryInterface.php b/app/Repositories/Recurring/RecurringRepositoryInterface.php index ddca5c2b21..ffab9e36e8 100644 --- a/app/Repositories/Recurring/RecurringRepositoryInterface.php +++ b/app/Repositories/Recurring/RecurringRepositoryInterface.php @@ -39,20 +39,6 @@ use Illuminate\Support\Collection; */ interface RecurringRepositoryInterface { - /** - * Destroy all recurring transactions. - */ - public function destroyAll(): void; - - /** - * Calculate how many transactions are to be expected from this recurrence. - * - * @param Recurrence $recurrence - * @param RecurrenceRepetition $repetition - * @return int - */ - public function totalTransactions(Recurrence $recurrence, RecurrenceRepetition $repetition): int; - /** * Destroy a recurring transaction. * @@ -60,6 +46,11 @@ interface RecurringRepositoryInterface */ public function destroy(Recurrence $recurrence): void; + /** + * Destroy all recurring transactions. + */ + public function destroyAll(): void; + /** * Returns all of the user's recurring transactions. * @@ -134,6 +125,7 @@ interface RecurringRepositoryInterface /** * @param RecurrenceTransaction $transaction + * * @return int|null */ public function getPiggyBank(RecurrenceTransaction $transaction): ?int; @@ -171,8 +163,8 @@ interface RecurringRepositoryInterface * @param Carbon $date * @param int $count * - * @throws FireflyException * @return array + * @throws FireflyException */ public function getXOccurrences(RecurrenceRepetition $repetition, Carbon $date, int $count): array; @@ -184,13 +176,13 @@ interface RecurringRepositoryInterface * * @param RecurrenceRepetition $repetition * @param Carbon $date - * @param Carbon $afterDate + * @param Carbon $afterDate * @param int $count * - * @throws FireflyException * @return array + * @throws FireflyException */ - public function getXOccurrencesSince(RecurrenceRepetition $repetition, Carbon $date,Carbon $afterDate, int $count): array; + public function getXOccurrencesSince(RecurrenceRepetition $repetition, Carbon $date, Carbon $afterDate, int $count): array; /** * Parse the repetition in a string that is user readable. @@ -201,6 +193,14 @@ interface RecurringRepositoryInterface */ public function repetitionDescription(RecurrenceRepetition $repetition): string; + /** + * @param string $query + * @param int $limit + * + * @return Collection + */ + public function searchRecurrence(string $query, int $limit): Collection; + /** * Set user for in repository. * @@ -212,11 +212,22 @@ interface RecurringRepositoryInterface * Store a new recurring transaction. * * @param array $data - * @throws FireflyException + * * @return Recurrence + * @throws FireflyException */ public function store(array $data): Recurrence; + /** + * Calculate how many transactions are to be expected from this recurrence. + * + * @param Recurrence $recurrence + * @param RecurrenceRepetition $repetition + * + * @return int + */ + public function totalTransactions(Recurrence $recurrence, RecurrenceRepetition $repetition): int; + /** * Update a recurring transaction. * diff --git a/app/Repositories/Rule/RuleRepository.php b/app/Repositories/Rule/RuleRepository.php index ac49dd5c41..8c83111d22 100644 --- a/app/Repositories/Rule/RuleRepository.php +++ b/app/Repositories/Rule/RuleRepository.php @@ -22,11 +22,13 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Rule; +use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleGroup; use FireflyIII\Models\RuleTrigger; +use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use FireflyIII\Support\Search\OperatorQuerySearch; use FireflyIII\User; use Illuminate\Support\Collection; @@ -53,7 +55,7 @@ class RuleRepository implements RuleRepositoryInterface * @param Rule $rule * * @return bool - * @throws \Exception + * @throws Exception */ public function destroy(Rule $rule): bool { @@ -68,6 +70,34 @@ class RuleRepository implements RuleRepositoryInterface return true; } + /** + * @inheritDoc + */ + public function duplicate(Rule $rule): Rule + { + $newRule = $rule->replicate(); + $newRule->title = (string)trans('firefly.rule_copy_of', ['title' => $rule->title]); + $newRule->save(); + + // replicate all triggers + /** @var RuleTrigger $trigger */ + foreach ($rule->ruleTriggers as $trigger) { + $newTrigger = $trigger->replicate(); + $newTrigger->rule_id = $newRule->id; + $newTrigger->save(); + } + + // replicate all actions + /** @var RuleAction $action */ + foreach ($rule->ruleActions as $action) { + $newAction = $action->replicate(); + $newAction->rule_id = $newRule->id; + $newAction->save(); + } + + return $newRule; + } + /** * @param int $ruleId * @@ -110,7 +140,7 @@ class RuleRepository implements RuleRepositoryInterface */ public function getHighestOrderInRuleGroup(RuleGroup $ruleGroup): int { - return (int) $ruleGroup->rules()->max('order'); + return (int)$ruleGroup->rules()->max('order'); } /** @@ -150,6 +180,84 @@ class RuleRepository implements RuleRepositoryInterface return $rule->ruleTriggers()->orderBy('order', 'ASC')->get(); } + /** + * @inheritDoc + */ + public function getSearchQuery(Rule $rule): string + { + $params = []; + /** @var RuleTrigger $trigger */ + foreach ($rule->ruleTriggers as $trigger) { + if ('user_action' === $trigger->trigger_type) { + continue; + } + $needsContext = config(sprintf('firefly.search.operators.%s.needs_context', $trigger->trigger_type)) ?? true; + if (false === $needsContext) { + $params[] = sprintf('%s:true', OperatorQuerySearch::getRootOperator($trigger->trigger_type)); + } + if (true === $needsContext) { + $params[] = sprintf('%s:"%s"', OperatorQuerySearch::getRootOperator($trigger->trigger_type), $trigger->trigger_value); + } + } + + return implode(' ', $params); + + } + + /** + * @inheritDoc + */ + public function getStoreRules(): Collection + { + $collection = $this->user->rules() + ->leftJoin('rule_groups', 'rule_groups.id', '=', 'rules.rule_group_id') + ->where('rules.active', 1) + ->where('rule_groups.active', 1) + ->orderBy('rule_groups.order', 'ASC') + ->orderBy('rules.order', 'ASC') + ->orderBy('rules.id', 'ASC') + ->with(['ruleGroup', 'ruleTriggers'])->get(['rules.*']); + $filtered = new Collection; + /** @var Rule $rule */ + foreach ($collection as $rule) { + /** @var RuleTrigger $ruleTrigger */ + foreach ($rule->ruleTriggers as $ruleTrigger) { + if ('user_action' === $ruleTrigger->trigger_type && 'store-journal' === $ruleTrigger->trigger_value) { + $filtered->push($rule); + } + } + } + + return $filtered; + } + + /** + * @inheritDoc + */ + public function getUpdateRules(): Collection + { + $collection = $this->user->rules() + ->leftJoin('rule_groups', 'rule_groups.id', '=', 'rules.rule_group_id') + ->where('rules.active', 1) + ->where('rule_groups.active', 1) + ->orderBy('rule_groups.order', 'ASC') + ->orderBy('rules.order', 'ASC') + ->orderBy('rules.id', 'ASC') + ->with(['ruleGroup', 'ruleTriggers'])->get(); + $filtered = new Collection; + /** @var Rule $rule */ + foreach ($collection as $rule) { + /** @var RuleTrigger $ruleTrigger */ + foreach ($rule->ruleTriggers as $ruleTrigger) { + if ('user_action' === $ruleTrigger->trigger_type && 'update-journal' === $ruleTrigger->trigger_value) { + $filtered->push($rule); + } + } + } + + return $filtered; + } + /** * @param Rule $rule * @@ -168,11 +276,25 @@ class RuleRepository implements RuleRepositoryInterface ++$rule->order; $rule->save(); - $this->resetRulesInGroupOrder($rule->ruleGroup); + $this->resetRuleOrder($rule->ruleGroup); return true; } + /** + * @inheritDoc + */ + public function moveRule(Rule $rule, RuleGroup $ruleGroup, int $order): Rule + { + $rule->order = $order; + if ($rule->rule_group_id !== $ruleGroup->id) { + $rule->rule_group_id = $ruleGroup->id; + } + $rule->save(); + + return $rule; + } + /** * @param Rule $rule * @@ -191,7 +313,7 @@ class RuleRepository implements RuleRepositoryInterface --$rule->order; $rule->save(); - $this->resetRulesInGroupOrder($rule->ruleGroup); + $this->resetRuleOrder($rule->ruleGroup); return true; } @@ -245,25 +367,30 @@ class RuleRepository implements RuleRepositoryInterface * * @return bool */ - public function resetRulesInGroupOrder(RuleGroup $ruleGroup): bool + public function resetRuleOrder(RuleGroup $ruleGroup): bool { - $ruleGroup->rules()->withTrashed()->whereNotNull('deleted_at')->update(['order' => 0]); - - $set = $ruleGroup->rules() - ->orderBy('order', 'ASC') - ->orderBy('updated_at', 'DESC') - ->get(); - $count = 1; - /** @var Rule $entry */ - foreach ($set as $entry) { - $entry->order = $count; - $entry->save(); - ++$count; - } + $groupRepository = app(RuleGroupRepositoryInterface::class); + $groupRepository->setUser($ruleGroup->user); + $groupRepository->resetRuleOrder($ruleGroup); return true; } + /** + * @inheritDoc + */ + public function searchRule(string $query, int $limit): Collection + { + $search = $this->user->rules(); + if ('' !== $query) { + $search->where('rules.title', 'LIKE', sprintf('%%%s%%', $query)); + } + $search->orderBy('rules.order', 'ASC') + ->orderBy('rules.title', 'ASC'); + + return $search->take($limit)->get(['id', 'title', 'description']); + } + /** * @param User $user */ @@ -272,6 +399,43 @@ class RuleRepository implements RuleRepositoryInterface $this->user = $user; } + + /** + * @inheritDoc + */ + public function setOrder(Rule $rule, int $newOrder): void + { + $oldOrder = (int)$rule->order; + $groupId = (int)$rule->rule_group_id; + $maxOrder = $this->maxOrder($rule->ruleGroup); + $newOrder = $newOrder > $maxOrder ? $maxOrder + 1 : $newOrder; + Log::debug(sprintf('New order will be %d', $newOrder)); + + if ($newOrder > $oldOrder) { + $this->user->rules() + ->where('rules.rule_group_id', $groupId) + ->where('rules.order', '<=', $newOrder) + ->where('rules.order', '>', $oldOrder) + ->where('rules.id', '!=', $rule->id) + ->decrement('rules.order', 1); + $rule->order = $newOrder; + Log::debug(sprintf('Order of rule #%d ("%s") is now %d', $rule->id, $rule->title, $newOrder)); + $rule->save(); + + return; + } + + $this->user->rules() + ->where('rules.rule_group_id', $groupId) + ->where('rules.order', '>=', $newOrder) + ->where('rules.order', '<', $oldOrder) + ->where('rules.id', '!=', $rule->id) + ->increment('rules.order', 1); + $rule->order = $newOrder; + Log::debug(sprintf('Order of rule #%d ("%s") is now %d', $rule->id, $rule->title, $newOrder)); + $rule->save(); + } + /** * @param array $data * @@ -279,31 +443,48 @@ class RuleRepository implements RuleRepositoryInterface */ public function store(array $data): Rule { - /** @var RuleGroup $ruleGroup */ - $ruleGroup = $this->user->ruleGroups()->find($data['rule_group_id']); - - // get max order: - $order = $this->getHighestOrderInRuleGroup($ruleGroup); + $ruleGroup = null; + if (array_key_exists('rule_group_id', $data)) { + $ruleGroup = $this->user->ruleGroups()->find($data['rule_group_id']); + } + if (array_key_exists('rule_group_title', $data)) { + $ruleGroup = $this->user->ruleGroups()->where('title', $data['rule_group_title'])->first(); + } + if (null === $ruleGroup) { + throw new FireflyException('No such rule group.'); + } // start by creating a new rule: $rule = new Rule; $rule->user()->associate($this->user->id); - $rule->rule_group_id = $data['rule_group_id']; - $rule->order = ($order + 1); - $rule->active = $data['active']; - $rule->strict = $data['strict']; - $rule->stop_processing = $data['stop_processing']; + $rule->rule_group_id = $ruleGroup->id; + $rule->order = 31337; + $rule->active = array_key_exists('active', $data) ? $data['active'] : true; + $rule->strict = array_key_exists('strict', $data) ? $data['strict'] : false; + $rule->stop_processing = array_key_exists('stop_processing', $data) ? $data['stop_processing'] : false; $rule->title = $data['title']; - $rule->description = strlen($data['description']) > 0 ? $data['description'] : null; - + $rule->description = array_key_exists('stop_processing', $data) ? $data['stop_processing'] : null; $rule->save(); + $rule->refresh(); + + // save update trigger: + $this->setRuleTrigger($data['trigger'] ?? 'store-journal', $rule); + + // reset order: + $this->resetRuleOrder($ruleGroup); + Log::debug('Done with resetting.'); + if (array_key_exists('order', $data)) { + Log::debug(sprintf('User has submitted order %d', $data['order'])); + $this->setOrder($rule, $data['order']); + } // start storing triggers: $this->storeTriggers($rule, $data); // same for actions. $this->storeActions($rule, $data); + $rule->refresh(); return $rule; } @@ -319,7 +500,7 @@ class RuleRepository implements RuleRepositoryInterface $ruleAction = new RuleAction; $ruleAction->rule()->associate($rule); $ruleAction->order = $values['order']; - $ruleAction->active = true; + $ruleAction->active = $values['active']; $ruleAction->stop_processing = $values['stop_processing']; $ruleAction->action_type = $values['action']; $ruleAction->action_value = $values['value'] ?? ''; @@ -339,7 +520,7 @@ class RuleRepository implements RuleRepositoryInterface $ruleTrigger = new RuleTrigger; $ruleTrigger->rule()->associate($rule); $ruleTrigger->order = $values['order']; - $ruleTrigger->active = true; + $ruleTrigger->active = $values['active']; $ruleTrigger->stop_processing = $values['stop_processing']; $ruleTrigger->trigger_type = $values['action']; $ruleTrigger->trigger_value = $values['value'] ?? ''; @@ -357,24 +538,37 @@ class RuleRepository implements RuleRepositoryInterface public function update(Rule $rule, array $data): Rule { // update rule: - - $rule->rule_group_id = $data['rule_group_id'] ?? $rule->rule_group_id; - $rule->active = $data['active'] ?? $rule->active; - $rule->stop_processing = $data['stop_processing'] ?? $rule->stop_processing; - $rule->title = $data['title'] ?? $rule->title; - $rule->strict = $data['strict'] ?? $rule->strict; - $rule->description = $data['description'] ?? $rule->description; + $fields = [ + 'title', + 'description', + 'strict', + 'rule_group_id', + 'active', + 'stop_processing', + ]; + foreach ($fields as $field) { + if (array_key_exists($field, $data)) { + $rule->$field = $data[$field]; + } + } $rule->save(); - - if (null !== $data['triggers']) { + // update the triggers: + if (array_key_exists('trigger', $data) && 'update-journal' === $data['trigger']) { + $this->setRuleTrigger('update-journal', $rule); + } + if (array_key_exists('trigger', $data) && 'store-journal' === $data['trigger']) { + $this->setRuleTrigger('store-journal', $rule); + } + if (array_key_exists('triggers', $data)) { // delete triggers: - $rule->ruleTriggers()->delete(); + $rule->ruleTriggers()->where('trigger_type', '!=', 'user_action')->delete(); // recreate triggers: $this->storeTriggers($rule, $data); } - if (null !== $data['actions']) { - // delete actions: + + if (array_key_exists('actions', $data)) { + // delete triggers: $rule->ruleActions()->delete(); // recreate actions: @@ -384,33 +578,6 @@ class RuleRepository implements RuleRepositoryInterface return $rule; } - /** - * @param Rule $rule - * @param array $data - * - * @return bool - */ - private function storeActions(Rule $rule, array $data): bool - { - $order = 1; - foreach ($data['actions'] as $action) { - $value = $action['value'] ?? ''; - $stopProcessing = $action['stop_processing'] ?? false; - - $actionValues = [ - 'action' => $action['type'], - 'value' => $value, - 'stop_processing' => $stopProcessing, - 'order' => $order, - ]; - app('telemetry')->feature('rules.actions.uses_action', $action['type']); - - $this->storeAction($rule, $actionValues); - } - - return true; - } - /** * @param Rule $rule * @param array $data @@ -419,26 +586,18 @@ class RuleRepository implements RuleRepositoryInterface */ private function storeTriggers(Rule $rule, array $data): bool { - $order = 1; - $stopProcessing = false; - - $triggerValues = [ - 'action' => 'user_action', - 'value' => $data['trigger'], - 'stop_processing' => $stopProcessing, - 'order' => $order, - ]; - - $this->storeTrigger($rule, $triggerValues); + $order = 1; foreach ($data['triggers'] as $trigger) { $value = $trigger['value'] ?? ''; $stopProcessing = $trigger['stop_processing'] ?? false; + $active = $trigger['active'] ?? true; $triggerValues = [ 'action' => $trigger['type'], 'value' => $value, 'stop_processing' => $stopProcessing, 'order' => $order, + 'active' => $active, ]; app('telemetry')->feature('rules.triggers.uses_trigger', $trigger['type']); @@ -450,119 +609,63 @@ class RuleRepository implements RuleRepositoryInterface } /** - * @inheritDoc + * @param Rule $rule + * @param array $data + * + * @return bool */ - public function duplicate(Rule $rule): Rule + private function storeActions(Rule $rule, array $data): bool { - $newRule = $rule->replicate(); - $newRule->title = (string) trans('firefly.rule_copy_of', ['title' => $rule->title]); - $newRule->save(); + $order = 1; + foreach ($data['actions'] as $action) { + $value = $action['value'] ?? ''; + $stopProcessing = $action['stop_processing'] ?? false; + $active = $action['active'] ?? true; + $actionValues = [ + 'action' => $action['type'], + 'value' => $value, + 'stop_processing' => $stopProcessing, + 'order' => $order, + 'active' => $active, + ]; + app('telemetry')->feature('rules.actions.uses_action', $action['type']); - // replicate all triggers + $this->storeAction($rule, $actionValues); + ++$order; + } + + return true; + } + + /** + * @param string $moment + * @param Rule $rule + */ + private function setRuleTrigger(string $moment, Rule $rule): void + { /** @var RuleTrigger $trigger */ - foreach ($rule->ruleTriggers as $trigger) { - $newTrigger = $trigger->replicate(); - $newTrigger->rule_id = $newRule->id; - $newTrigger->save(); - } + $trigger = $rule->ruleTriggers()->where('trigger_type', 'user_action')->first(); + if (null !== $trigger) { + $trigger->trigger_value = $moment; + $trigger->save(); - // replicate all actions - /** @var RuleAction $action */ - foreach ($rule->ruleActions as $action) { - $newAction = $action->replicate(); - $newAction->rule_id = $newRule->id; - $newAction->save(); + return; } - - return $newRule; + $trigger = new RuleTrigger; + $trigger->order = 0; + $trigger->trigger_type = 'user_action'; + $trigger->trigger_value = $moment; + $trigger->rule_id = $rule->id; + $trigger->active = true; + $trigger->stop_processing = false; + $trigger->save(); } /** * @inheritDoc */ - public function moveRule(Rule $rule, RuleGroup $ruleGroup, int $order): Rule + public function maxOrder(RuleGroup $ruleGroup): int { - $rule->order = $order; - if ($rule->rule_group_id !== $ruleGroup->id) { - $rule->rule_group_id = $ruleGroup->id; - } - $rule->save(); - - return $rule; - } - - /** - * @inheritDoc - */ - public function getStoreRules(): Collection - { - $collection = $this->user->rules() - ->leftJoin('rule_groups', 'rule_groups.id', '=', 'rules.rule_group_id') - ->where('rules.active', 1) - ->where('rule_groups.active', 1) - ->orderBy('rule_groups.order', 'ASC') - ->orderBy('rules.order', 'ASC') - ->orderBy('rules.id', 'ASC') - ->with(['ruleGroup', 'ruleTriggers'])->get(['rules.*']); - $filtered = new Collection; - /** @var Rule $rule */ - foreach ($collection as $rule) { - /** @var RuleTrigger $ruleTrigger */ - foreach ($rule->ruleTriggers as $ruleTrigger) { - if ('user_action' === $ruleTrigger->trigger_type && 'store-journal' === $ruleTrigger->trigger_value) { - $filtered->push($rule); - } - } - } - return $filtered; - } - - /** - * @inheritDoc - */ - public function getUpdateRules(): Collection - { - $collection = $this->user->rules() - ->leftJoin('rule_groups', 'rule_groups.id', '=', 'rules.rule_group_id') - ->where('rules.active', 1) - ->where('rule_groups.active', 1) - ->orderBy('rule_groups.order', 'ASC') - ->orderBy('rules.order', 'ASC') - ->orderBy('rules.id', 'ASC') - ->with(['ruleGroup', 'ruleTriggers'])->get(); - $filtered = new Collection; - /** @var Rule $rule */ - foreach ($collection as $rule) { - /** @var RuleTrigger $ruleTrigger */ - foreach ($rule->ruleTriggers as $ruleTrigger) { - if ('user_action' === $ruleTrigger->trigger_type && 'update-journal' === $ruleTrigger->trigger_value) { - $filtered->push($rule); - } - } - } - return $filtered; - } - - /** - * @inheritDoc - */ - public function getSearchQuery(Rule $rule): string - { - $params = []; - /** @var RuleTrigger $trigger */ - foreach ($rule->ruleTriggers as $trigger) { - if ('user_action' === $trigger->trigger_type) { - continue; - } - $needsContext = config(sprintf('firefly.search.operators.%s.needs_context', $trigger->trigger_type)) ?? true; - if (false === $needsContext) { - $params[] = sprintf('%s:true', OperatorQuerySearch::getRootOperator($trigger->trigger_type)); - } - if (true === $needsContext) { - $params[] = sprintf('%s:"%s"', OperatorQuerySearch::getRootOperator($trigger->trigger_type), $trigger->trigger_value); - } - } - return implode(' ', $params); - + return (int)$ruleGroup->rules()->max('order'); } } diff --git a/app/Repositories/Rule/RuleRepositoryInterface.php b/app/Repositories/Rule/RuleRepositoryInterface.php index d49199c4fc..1e25e11541 100644 --- a/app/Repositories/Rule/RuleRepositoryInterface.php +++ b/app/Repositories/Rule/RuleRepositoryInterface.php @@ -39,23 +39,6 @@ interface RuleRepositoryInterface */ public function count(): int; - /** - * Return search query for rule. - * - * @param Rule $rule - * @return string - */ - public function getSearchQuery(Rule $rule): string; - - /** - * @param Rule $rule - * @param RuleGroup $ruleGroup - * @param int $order - * - * @return Rule - */ - public function moveRule(Rule $rule, RuleGroup $ruleGroup, int $order): Rule; - /** * @param Rule $rule * @@ -84,20 +67,6 @@ interface RuleRepositoryInterface */ public function getAll(): Collection; - /** - * Get all the users rules that trigger on storage. - * - * @return Collection - */ - public function getStoreRules(): Collection; - - /** - * Get all the users rules that trigger on update. - * - * @return Collection - */ - public function getUpdateRules(): Collection; - /** * @return RuleGroup */ @@ -131,6 +100,29 @@ interface RuleRepositoryInterface */ public function getRuleTriggers(Rule $rule): Collection; + /** + * Return search query for rule. + * + * @param Rule $rule + * + * @return string + */ + public function getSearchQuery(Rule $rule): string; + + /** + * Get all the users rules that trigger on storage. + * + * @return Collection + */ + public function getStoreRules(): Collection; + + /** + * Get all the users rules that trigger on update. + * + * @return Collection + */ + public function getUpdateRules(): Collection; + /** * @param Rule $rule * @@ -138,6 +130,15 @@ interface RuleRepositoryInterface */ public function moveDown(Rule $rule): bool; + /** + * @param Rule $rule + * @param RuleGroup $ruleGroup + * @param int $order + * + * @return Rule + */ + public function moveRule(Rule $rule, RuleGroup $ruleGroup, int $order): Rule; + /** * @param Rule $rule * @@ -166,7 +167,15 @@ interface RuleRepositoryInterface * * @return bool */ - public function resetRulesInGroupOrder(RuleGroup $ruleGroup): bool; + public function resetRuleOrder(RuleGroup $ruleGroup): bool; + + /** + * @param string $query + * @param int $limit + * + * @return Collection + */ + public function searchRule(string $query, int $limit): Collection; /** * @param User $user @@ -180,6 +189,19 @@ interface RuleRepositoryInterface */ public function store(array $data): Rule; + /** + * @param Rule $rule + * @param int $newOrder + */ + public function setOrder(Rule $rule, int $newOrder): void; + + /** + * @param RuleGroup $ruleGroup + * + * @return int + */ + public function maxOrder(RuleGroup $ruleGroup): int; + /** * @param Rule $rule * @param array $values diff --git a/app/Repositories/RuleGroup/RuleGroupRepository.php b/app/Repositories/RuleGroup/RuleGroupRepository.php index 298cc573c3..6981d3cb7d 100644 --- a/app/Repositories/RuleGroup/RuleGroupRepository.php +++ b/app/Repositories/RuleGroup/RuleGroupRepository.php @@ -22,8 +22,11 @@ declare(strict_types=1); namespace FireflyIII\Repositories\RuleGroup; +use Exception; use FireflyIII\Models\Rule; +use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleGroup; +use FireflyIII\Models\RuleTrigger; use FireflyIII\User; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Support\Collection; @@ -34,8 +37,29 @@ use Log; */ class RuleGroupRepository implements RuleGroupRepositoryInterface { - /** @var User */ - private $user; + private User $user; + + /** + * @inheritDoc + */ + public function correctRuleGroupOrder(): void + { + $set = $this->user + ->ruleGroups() + ->orderBy('order', 'ASC') + ->orderBy('active', 'DESC') + ->orderBy('title', 'ASC') + ->get(['rule_groups.id']); + $index = 1; + /** @var RuleGroup $ruleGroup */ + foreach ($set as $ruleGroup) { + if ($ruleGroup->order !== $index) { + $ruleGroup->order = $index; + $ruleGroup->save(); + } + $index++; + } + } /** * @return int @@ -50,7 +74,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface * @param RuleGroup|null $moveTo * * @return bool - * @throws \Exception + * @throws Exception */ public function destroy(RuleGroup $ruleGroup, ?RuleGroup $moveTo): bool { @@ -67,61 +91,25 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface $ruleGroup->delete(); - $this->resetRuleGroupOrder(); + $this->resetOrder(); if (null !== $moveTo) { - $this->resetRulesInGroupOrder($moveTo); + $this->resetRuleOrder($moveTo); } return true; } /** - * @return bool + * @inheritDoc */ - public function resetRuleGroupOrder(): bool + public function destroyAll(): void { - $this->user->ruleGroups()->whereNotNull('deleted_at')->update(['order' => 0]); - - $set = $this->user - ->ruleGroups() - ->orderBy('order', 'ASC')->get(); - $count = 1; - /** @var RuleGroup $entry */ - foreach ($set as $entry) { - $entry->order = $count; - $entry->save(); - - // also update rules in group. - $this->resetRulesInGroupOrder($entry); - - ++$count; + $groups = $this->get(); + /** @var RuleGroup $group */ + foreach ($groups as $group) { + $group->rules()->delete(); + $group->delete(); } - - return true; - } - - /** - * @param RuleGroup $ruleGroup - * - * @return bool - */ - public function resetRulesInGroupOrder(RuleGroup $ruleGroup): bool - { - $ruleGroup->rules()->whereNotNull('deleted_at')->update(['order' => 0]); - - $set = $ruleGroup->rules() - ->orderBy('order', 'ASC') - ->orderBy('updated_at', 'DESC') - ->get(); - $count = 1; - /** @var Rule $entry */ - foreach ($set as $entry) { - $entry->order = $count; - $entry->save(); - ++$count; - } - - return true; } /** @@ -139,6 +127,16 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface return $group; } + /** + * @param string $title + * + * @return RuleGroup|null + */ + public function findByTitle(string $title): ?RuleGroup + { + return $this->user->ruleGroups()->where('title', $title)->first(); + } + /** * @return Collection */ @@ -198,26 +196,66 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface } /** + * @return int + */ + public function getHighestOrderRuleGroup(): int + { + $entry = $this->user->ruleGroups()->max('order'); + + return (int)$entry; + } + + /** + * @param string|null $filter + * * @return Collection */ - public function getRuleGroupsWithRules(): Collection + public function getRuleGroupsWithRules(?string $filter): Collection { - return $this->user->ruleGroups() - ->orderBy('order', 'ASC') - ->where('active', true) - ->with( - [ - 'rules' => static function (HasMany $query) { - $query->orderBy('order', 'ASC'); - }, - 'rules.ruleTriggers' => static function (HasMany $query) { - $query->orderBy('order', 'ASC'); - }, - 'rules.ruleActions' => static function (HasMany $query) { - $query->orderBy('order', 'ASC'); - }, - ] - )->get(); + $groups = $this->user->ruleGroups() + ->orderBy('order', 'ASC') + ->where('active', true) + ->with( + [ + 'rules' => static function (HasMany $query) { + $query->orderBy('order', 'ASC'); + }, + 'rules.ruleTriggers' => static function (HasMany $query) { + $query->orderBy('order', 'ASC'); + }, + 'rules.ruleActions' => static function (HasMany $query) { + $query->orderBy('order', 'ASC'); + }, + ] + )->get(); + if (null === $filter) { + return $groups; + } + Log::debug(sprintf('Will filter getRuleGroupsWithRules on "%s".', $filter)); + + return $groups->map( + function (RuleGroup $group) use ($filter) { + Log::debug(sprintf('Now filtering group #%d', $group->id)); + // filter the rules in the rule group: + $group->rules = $group->rules->filter( + function (Rule $rule) use ($filter) { + Log::debug(sprintf('Now filtering rule #%d', $rule->id)); + foreach ($rule->ruleTriggers as $trigger) { + if ('user_action' === $trigger->trigger_type && $filter === $trigger->trigger_value) { + Log::debug(sprintf('Rule #%d triggers on %s, include it.', $rule->id, $filter)); + + return true; + } + } + Log::debug(sprintf('Rule #%d does not trigger on %s, do not include it.', $rule->id, $filter)); + + return false; + } + ); + + return $group; + } + ); } /** @@ -231,6 +269,14 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface ->get(['rules.*']); } + /** + * @inheritDoc + */ + public function maxOrder(): int + { + return (int)$this->user->ruleGroups()->max('order'); + } + /** * @param RuleGroup $ruleGroup * @@ -277,6 +323,124 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface return true; } + /** + * @return bool + */ + public function resetOrder(): bool + { + $this->user->ruleGroups()->whereNotNull('deleted_at'); + + $set = $this->user + ->ruleGroups() + ->orderBy('order', 'ASC') + ->orderBy('title', 'DESC') + ->get(); + $count = 1; + /** @var RuleGroup $entry */ + foreach ($set as $entry) { + if ($entry->order !== $count) { + $entry->order = $count; + $entry->save(); + } + + // also update rules in group. + $this->resetRuleOrder($entry); + + ++$count; + } + + return true; + } + + /** + * @param RuleGroup $ruleGroup + * + * @return bool + */ + public function resetRuleOrder(RuleGroup $ruleGroup): bool + { + $set = $ruleGroup->rules() + ->orderBy('order', 'ASC') + ->orderBy('title', 'DESC') + ->orderBy('updated_at', 'DESC') + ->get(['rules.*']); + $count = 1; + /** @var Rule $entry */ + foreach ($set as $entry) { + if ((int)$entry->order !== $count) { + Log::debug(sprintf('Rule #%d was on spot %d but must be on spot %d', $entry->id, $entry->order, $count)); + $entry->order = $count; + $entry->save(); + } + $this->resetRuleActionOrder($entry); + $this->resetRuleTriggerOrder($entry); + + ++$count; + } + + return true; + } + + /** + * @param Rule $rule + */ + private function resetRuleActionOrder(Rule $rule): void + { + $actions = $rule->ruleActions() + ->orderBy('order', 'ASC') + ->orderBy('active', 'DESC') + ->orderBy('action_type', 'ASC') + ->get(); + $index = 1; + /** @var RuleAction $action */ + foreach ($actions as $action) { + if ((int)$action->order !== $index) { + $action->order = $index; + $action->save(); + Log::debug(sprintf('Rule action #%d was on spot %d but must be on spot %d', $action->id, $action->order, $index)); + } + $index++; + } + } + + /** + * @param Rule $rule + */ + private function resetRuleTriggerOrder(Rule $rule): void + { + $triggers = $rule->ruleTriggers() + ->orderBy('order', 'ASC') + ->orderBy('active', 'DESC') + ->orderBy('trigger_type', 'ASC') + ->get(); + $index = 1; + /** @var RuleTrigger $trigger */ + foreach ($triggers as $trigger) { + $order = (int) $trigger->order; + if ($order !== $index) { + $trigger->order = $index; + $trigger->save(); + Log::debug(sprintf('Rule trigger #%d was on spot %d but must be on spot %d', $trigger->id, $order, $index)); + } + $index++; + } + } + + /** + * @inheritDoc + */ + public function searchRuleGroup(string $query, int $limit): Collection + { + $search = $this->user->ruleGroups(); + if ('' !== $query) { + $search->where('rule_groups.title', 'LIKE', sprintf('%%%s%%', $query)); + } + $search->orderBy('rule_groups.order', 'ASC') + ->orderBy('rule_groups.title', 'ASC'); + + return $search->take($limit)->get(['id', 'title', 'description']); + } + /** * @param User $user */ @@ -292,33 +456,24 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface */ public function store(array $data): RuleGroup { - $order = $this->getHighestOrderRuleGroup(); - $newRuleGroup = new RuleGroup( [ 'user_id' => $this->user->id, 'title' => $data['title'], 'description' => $data['description'], - 'order' => $order + 1, - 'active' => $data['active'], + 'order' => 31337, + 'active' => array_key_exists('active', $data) ? $data['active'] : true, ] ); $newRuleGroup->save(); - $this->resetRuleGroupOrder(); + $this->resetOrder(); + if (array_key_exists('order', $data)) { + $this->setOrder($newRuleGroup, $data['order']); + } return $newRuleGroup; } - /** - * @return int - */ - public function getHighestOrderRuleGroup(): int - { - $entry = $this->user->ruleGroups()->max('order'); - - return (int)$entry; - } - /** * @param RuleGroup $ruleGroup * @param array $data @@ -328,35 +483,50 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface public function update(RuleGroup $ruleGroup, array $data): RuleGroup { // update the account: - $ruleGroup->title = $data['title']; - $ruleGroup->description = $data['description']; - $ruleGroup->active = $data['active']; + if (array_key_exists('title', $data)) { + $ruleGroup->title = $data['title']; + } + if (array_key_exists('description', $data)) { + $ruleGroup->description = $data['description']; + } + if (array_key_exists('active', $data)) { + $ruleGroup->active = $data['active']; + } + // order + if (array_key_exists('order', $data) && $ruleGroup->order !== $data['order']) { + $this->resetOrder(); + $this->setOrder($ruleGroup, (int)$data['order']); + } + $ruleGroup->save(); - $this->resetRuleGroupOrder(); return $ruleGroup; } - /** - * @param string $title - * - * @return RuleGroup|null - */ - public function findByTitle(string $title): ?RuleGroup - { - return $this->user->ruleGroups()->where('title', $title)->first(); - } /** * @inheritDoc */ - public function destroyAll(): void + public function setOrder(RuleGroup $ruleGroup, int $newOrder): void { - $groups = $this->get(); - /** @var RuleGroup $group */ - foreach ($groups as $group) { - $group->rules()->delete(); - $group->delete(); + $oldOrder = (int)$ruleGroup->order; + + if ($newOrder > $oldOrder) { + $this->user->ruleGroups()->where('rule_groups.order', '<=', $newOrder)->where('rule_groups.order', '>', $oldOrder) + ->where('rule_groups.id', '!=', $ruleGroup->id) + ->decrement('order', 1); + $ruleGroup->order = $newOrder; + Log::debug(sprintf('Order of group #%d ("%s") is now %d', $ruleGroup->id, $ruleGroup->title, $newOrder)); + $ruleGroup->save(); + + return; } + + $this->user->ruleGroups()->where('rule_groups.order', '>=', $newOrder)->where('rule_groups.order', '<', $oldOrder) + ->where('rule_groups.id', '!=', $ruleGroup->id) + ->increment('order', 1); + $ruleGroup->order = $newOrder; + Log::debug(sprintf('Order of group #%d ("%s") is now %d', $ruleGroup->id, $ruleGroup->title, $newOrder)); + $ruleGroup->save(); } } diff --git a/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php b/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php index 577133407f..84f8fbc436 100644 --- a/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php +++ b/app/Repositories/RuleGroup/RuleGroupRepositoryInterface.php @@ -31,10 +31,11 @@ use Illuminate\Support\Collection; */ interface RuleGroupRepositoryInterface { + /** - * Delete everything. + * Make sure rule group order is correct in DB. */ - public function destroyAll(): void; + public function correctRuleGroupOrder(): void; /** * @return int @@ -49,6 +50,11 @@ interface RuleGroupRepositoryInterface */ public function destroy(RuleGroup $ruleGroup, ?RuleGroup $moveTo): bool; + /** + * Delete everything. + */ + public function destroyAll(): void; + /** * @param int $ruleGroupId * @@ -102,9 +108,11 @@ interface RuleGroupRepositoryInterface public function getHighestOrderRuleGroup(): int; /** + * @param string|null $filter + * * @return Collection */ - public function getRuleGroupsWithRules(): Collection; + public function getRuleGroupsWithRules(?string $filter): Collection; /** * @param RuleGroup $group @@ -113,6 +121,13 @@ interface RuleGroupRepositoryInterface */ public function getRules(RuleGroup $group): Collection; + /** + * Get highest possible order for a rule group. + * + * @return int + */ + public function maxOrder(): int; + /** * @param RuleGroup $ruleGroup * @@ -130,14 +145,28 @@ interface RuleGroupRepositoryInterface /** * @return bool */ - public function resetRuleGroupOrder(): bool; + public function resetOrder(): bool; /** * @param RuleGroup $ruleGroup * * @return bool */ - public function resetRulesInGroupOrder(RuleGroup $ruleGroup): bool; + public function resetRuleOrder(RuleGroup $ruleGroup): bool; + + /** + * @param RuleGroup $ruleGroup + * @param int $newOrder + */ + public function setOrder(RuleGroup $ruleGroup, int $newOrder): void; + + /** + * @param string $query + * @param int $limit + * + * @return Collection + */ + public function searchRuleGroup(string $query, int $limit): Collection; /** * @param User $user @@ -153,7 +182,7 @@ interface RuleGroupRepositoryInterface /** * @param RuleGroup $ruleGroup - * @param array $data + * @param array $data * * @return RuleGroup */ diff --git a/app/Repositories/Tag/OperationsRepository.php b/app/Repositories/Tag/OperationsRepository.php index 45c3af02ea..38e424a96f 100644 --- a/app/Repositories/Tag/OperationsRepository.php +++ b/app/Repositories/Tag/OperationsRepository.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Tag; use Carbon\Carbon; -use Log; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\TransactionType; diff --git a/app/Repositories/Tag/TagRepository.php b/app/Repositories/Tag/TagRepository.php index ceb605c5c5..80ddfecf57 100644 --- a/app/Repositories/Tag/TagRepository.php +++ b/app/Repositories/Tag/TagRepository.php @@ -24,12 +24,11 @@ namespace FireflyIII\Repositories\Tag; use Carbon\Carbon; use DB; +use Exception; use FireflyIII\Factory\TagFactory; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Attachment; use FireflyIII\Models\Location; -use FireflyIII\Models\RuleAction; -use FireflyIII\Models\RuleTrigger; use FireflyIII\Models\Tag; use FireflyIII\Models\TransactionType; use FireflyIII\User; @@ -57,7 +56,7 @@ class TagRepository implements TagRepositoryInterface * @param Tag $tag * * @return bool - * @throws \Exception + * @throws Exception */ public function destroy(Tag $tag): bool { @@ -141,6 +140,26 @@ class TagRepository implements TagRepositoryInterface return $this->user->tags()->orderBy('tag', 'ASC')->get(); } + /** + * @inheritDoc + */ + public function getAttachments(Tag $tag): Collection + { + $set = $tag->attachments()->get(); + /** @var Storage $disk */ + $disk = Storage::disk('upload'); + + return $set->each( + static function (Attachment $attachment) use ($disk) { + $notes = $attachment->notes()->first(); + $attachment->file_exists = $disk->exists($attachment->fileName()); + $attachment->notes = $notes ? $notes->text : ''; + + return $attachment; + } + ); + } + /** * @inheritDoc */ @@ -317,7 +336,7 @@ class TagRepository implements TagRepositoryInterface /** @var array $journal */ foreach ($journals as $journal) { - $currencyId = (int) $journal['currency_id']; + $currencyId = (int)$journal['currency_id']; $sums[$currencyId] = $sums[$currencyId] ?? [ 'currency_id' => $currencyId, 'currency_name' => $journal['currency_name'], @@ -331,7 +350,7 @@ class TagRepository implements TagRepositoryInterface ]; // add amount to correct type: - $amount = app('steam')->positive((string) $journal['amount']); + $amount = app('steam')->positive((string)$journal['amount']); $type = $journal['transaction_type_type']; if (TransactionType::WITHDRAWAL === $type) { $amount = bcmul($amount, '-1'); @@ -352,7 +371,7 @@ class TagRepository implements TagRepositoryInterface TransactionType::OPENING_BALANCE => '0', ]; // add foreign amount to correct type: - $amount = app('steam')->positive((string) $journal['foreign_amount']); + $amount = app('steam')->positive((string)$journal['foreign_amount']); $type = $journal['transaction_type_type']; if (TransactionType::WITHDRAWAL === $type) { $amount = bcmul($amount, '-1'); @@ -361,6 +380,7 @@ class TagRepository implements TagRepositoryInterface } } + return $sums; } @@ -395,7 +415,7 @@ class TagRepository implements TagRepositoryInterface Log::debug(sprintf('Each coin in a tag earns it %s points', $pointsPerCoin)); /** @var Tag $tag */ foreach ($tags as $tag) { - $amount = (string) $tag->amount_sum; + $amount = (string)$tag->amount_sum; $amount = '' === $amount ? '0' : $amount; $amountMin = bcsub($amount, $min); $pointsForTag = bcmul($amountMin, $pointsPerCoin); @@ -440,16 +460,24 @@ class TagRepository implements TagRepositoryInterface */ public function update(Tag $tag, array $data): Tag { - $tag->tag = $data['tag']; - $tag->date = $data['date']; - $tag->description = $data['description']; - $tag->latitude = null; - $tag->longitude = null; - $tag->zoomLevel = null; + if (array_key_exists('tag', $data)) { + $tag->tag = $data['tag']; + } + if (array_key_exists('date', $data)) { + $tag->date = $data['date']; + } + if (array_key_exists('description', $data)) { + $tag->description = $data['description']; + } + + $tag->latitude = null; + $tag->longitude = null; + $tag->zoomLevel = null; $tag->save(); // update, delete or create location: $updateLocation = $data['update_location'] ?? false; + $deleteLocation = $data['remove_location'] ?? false; // location must be updated? if (true === $updateLocation) { @@ -472,6 +500,9 @@ class TagRepository implements TagRepositoryInterface $location->save(); } } + if (true === $deleteLocation) { + $tag->locations()->delete(); + } return $tag; } @@ -486,7 +517,7 @@ class TagRepository implements TagRepositoryInterface $max = '0'; /** @var Tag $tag */ foreach ($tags as $tag) { - $amount = (string) $tag->amount_sum; + $amount = (string)$tag->amount_sum; $amount = '' === $amount ? '0' : $amount; $max = 1 === bccomp($amount, $max) ? $amount : $max; @@ -508,7 +539,7 @@ class TagRepository implements TagRepositoryInterface /** @var Tag $tag */ foreach ($tags as $tag) { - $amount = (string) $tag->amount_sum; + $amount = (string)$tag->amount_sum; $amount = '' === $amount ? '0' : $amount; if (null === $min) { @@ -525,24 +556,4 @@ class TagRepository implements TagRepositoryInterface return $min; } - - /** - * @inheritDoc - */ - public function getAttachments(Tag $tag): Collection - { - $set = $tag->attachments()->get(); - /** @var Storage $disk */ - $disk = Storage::disk('upload'); - - return $set->each( - static function (Attachment $attachment) use ($disk) { - $notes = $attachment->notes()->first(); - $attachment->file_exists = $disk->exists($attachment->fileName()); - $attachment->notes = $notes ? $notes->text : ''; - - return $attachment; - } - ); - } } diff --git a/app/Repositories/Tag/TagRepositoryInterface.php b/app/Repositories/Tag/TagRepositoryInterface.php index caedad9b8f..c5a019bd0b 100644 --- a/app/Repositories/Tag/TagRepositoryInterface.php +++ b/app/Repositories/Tag/TagRepositoryInterface.php @@ -39,13 +39,6 @@ interface TagRepositoryInterface */ public function count(): int; - /** - * @param Tag $tag - * - * @return Collection - */ - public function getAttachments(Tag $tag): Collection; - /** * This method destroys a tag. * @@ -97,6 +90,13 @@ interface TagRepositoryInterface */ public function get(): Collection; + /** + * @param Tag $tag + * + * @return Collection + */ + public function getAttachments(Tag $tag): Collection; + /** * Return location, or NULL. * @@ -156,7 +156,7 @@ interface TagRepositoryInterface * Search the users tags. * * @param string $query - * @param int $limit + * @param int $limit * * @return Collection */ @@ -189,11 +189,12 @@ interface TagRepositoryInterface /** * Generates a tag cloud. - * @deprecated * * @param int|null $year * * @return array + * @deprecated + * */ public function tagCloud(?int $year): array; diff --git a/app/Repositories/Telemetry/TelemetryRepository.php b/app/Repositories/Telemetry/TelemetryRepository.php index 21f6ed2fac..ad6026a167 100644 --- a/app/Repositories/Telemetry/TelemetryRepository.php +++ b/app/Repositories/Telemetry/TelemetryRepository.php @@ -50,14 +50,6 @@ class TelemetryRepository implements TelemetryRepositoryInterface Telemetry::whereNotNull('created_at')->delete(); } - /** - * @inheritDoc - */ - public function paginated(int $pageSize): LengthAwarePaginator - { - return Telemetry::orderBy('created_at', 'DESC')->paginate($pageSize); - } - /** * @inheritDoc */ @@ -66,4 +58,12 @@ class TelemetryRepository implements TelemetryRepositoryInterface // created_at is never NULL. Telemetry::whereNotNull('submitted')->delete(); } + + /** + * @inheritDoc + */ + public function paginated(int $pageSize): LengthAwarePaginator + { + return Telemetry::orderBy('created_at', 'DESC')->paginate($pageSize); + } } diff --git a/app/Repositories/Telemetry/TelemetryRepositoryInterface.php b/app/Repositories/Telemetry/TelemetryRepositoryInterface.php index 0e42fcec49..532dd38154 100644 --- a/app/Repositories/Telemetry/TelemetryRepositoryInterface.php +++ b/app/Repositories/Telemetry/TelemetryRepositoryInterface.php @@ -39,15 +39,6 @@ interface TelemetryRepositoryInterface */ public function count(): int; - /** - * Return paginated result of telemetry records. - * - * @param int $pageSize - * - * @return LengthAwarePaginator - */ - public function paginated( int $pageSize): LengthAwarePaginator; - /** * Delete all records. */ @@ -58,4 +49,13 @@ interface TelemetryRepositoryInterface */ public function deleteSubmitted(): void; + /** + * Return paginated result of telemetry records. + * + * @param int $pageSize + * + * @return LengthAwarePaginator + */ + public function paginated(int $pageSize): LengthAwarePaginator; + } diff --git a/app/Repositories/TransactionGroup/TransactionGroupRepository.php b/app/Repositories/TransactionGroup/TransactionGroupRepository.php index 4404b1966d..1abeb637a4 100644 --- a/app/Repositories/TransactionGroup/TransactionGroupRepository.php +++ b/app/Repositories/TransactionGroup/TransactionGroupRepository.php @@ -32,6 +32,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Factory\TransactionGroupFactory; use FireflyIII\Models\AccountMeta; use FireflyIII\Models\Attachment; +use FireflyIII\Models\Location; use FireflyIII\Models\Note; use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\Transaction; @@ -176,6 +177,17 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface return $return; } + /** + * @inheritDoc + */ + public function getLocation(int $journalId): ?Location + { + /** @var TransactionJournal $journal */ + $journal = $this->user->transactionJournals()->find($journalId); + + return $journal->locations()->first(); + } + /** * Return object with all found meta field things as Carbon objects. * @@ -265,7 +277,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface ->get(['piggy_bank_events.*']); /** @var PiggyBankEvent $row */ foreach ($data as $row) { - if(null === $row->piggyBank) { + if (null === $row->piggyBank) { continue; } // get currency preference. @@ -293,6 +305,17 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface return $return; } + /** + * @inheritDoc + */ + public function getTagObjects(int $journalId): Collection + { + /** @var TransactionJournal $journal */ + $journal = $this->user->transactionJournals()->find($journalId); + + return $journal->tags()->get(); + } + /** * Get the tags for a journal (by ID). * @@ -336,7 +359,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface } catch (DuplicateTransactionException $e) { Log::warning('Group repository caught group factory with a duplicate exception!'); throw new DuplicateTransactionException($e->getMessage()); - } catch(FireflyException $e) { + } catch (FireflyException $e) { Log::warning('Group repository caught group factory with an exception!'); Log::error($e->getMessage()); Log::error($e->getTraceAsString()); @@ -396,9 +419,9 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface */ private function expandTransaction(Transaction $transaction): array { - $array = $transaction->toArray(); - $array['account'] = $transaction->account->toArray(); - $array['budgets'] = []; + $array = $transaction->toArray(); + $array['account'] = $transaction->account->toArray(); + $array['budgets'] = []; $array['categories'] = []; foreach ($transaction->categories as $category) { @@ -461,15 +484,4 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface return $return; } - - /** - * @inheritDoc - */ - public function getTagObjects(int $journalId): Collection - { - /** @var TransactionJournal $journal */ - $journal = $this->user->transactionJournals()->find($journalId); - - return $journal->tags()->get(); - } } diff --git a/app/Repositories/TransactionGroup/TransactionGroupRepositoryInterface.php b/app/Repositories/TransactionGroup/TransactionGroupRepositoryInterface.php index 2bf572db27..4fd2384149 100644 --- a/app/Repositories/TransactionGroup/TransactionGroupRepositoryInterface.php +++ b/app/Repositories/TransactionGroup/TransactionGroupRepositoryInterface.php @@ -25,6 +25,7 @@ namespace FireflyIII\Repositories\TransactionGroup; use FireflyIII\Exceptions\DuplicateTransactionException; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Location; use FireflyIII\Models\TransactionGroup; use FireflyIII\Support\NullArrayObject; use FireflyIII\User; @@ -76,6 +77,15 @@ interface TransactionGroupRepositoryInterface */ public function getLinks(TransactionGroup $group): array; + /** + * Get the location of a journal or NULL. + * + * @param int $journalId + * + * @return Location|null + */ + public function getLocation(int $journalId): ?Location; + /** * Return object with all found meta field things as Carbon objects. * @@ -114,15 +124,6 @@ interface TransactionGroupRepositoryInterface */ public function getPiggyEvents(TransactionGroup $group): array; - /** - * Get the tags for a journal (by ID). - * - * @param int $journalId - * - * @return array - */ - public function getTags(int $journalId): array; - /** * Get the tags for a journal (by ID) as Tag objects. * @@ -132,6 +133,15 @@ interface TransactionGroupRepositoryInterface */ public function getTagObjects(int $journalId): Collection; + /** + * Get the tags for a journal (by ID). + * + * @param int $journalId + * + * @return array + */ + public function getTags(int $journalId): array; + /** * Set the user. * diff --git a/app/Repositories/TransactionType/TransactionTypeRepository.php b/app/Repositories/TransactionType/TransactionTypeRepository.php index 5b5a319eff..d5aa7c9dc0 100644 --- a/app/Repositories/TransactionType/TransactionTypeRepository.php +++ b/app/Repositories/TransactionType/TransactionTypeRepository.php @@ -47,7 +47,7 @@ class TransactionTypeRepository implements TransactionTypeRepositoryInterface /** * @param TransactionType|null $type - * @param string|null $typeString + * @param string|null $typeString * * @return TransactionType */ @@ -71,7 +71,8 @@ class TransactionTypeRepository implements TransactionTypeRepositoryInterface /** * @param string $query - * @param int $limit + * @param int $limit + * * @return Collection */ public function searchTypes(string $query, int $limit): Collection diff --git a/app/Repositories/TransactionType/TransactionTypeRepositoryInterface.php b/app/Repositories/TransactionType/TransactionTypeRepositoryInterface.php index 65033e48da..ff4c4b296f 100644 --- a/app/Repositories/TransactionType/TransactionTypeRepositoryInterface.php +++ b/app/Repositories/TransactionType/TransactionTypeRepositoryInterface.php @@ -31,6 +31,13 @@ use Illuminate\Support\Collection; */ interface TransactionTypeRepositoryInterface { + /** + * @param string $type + * + * @return TransactionType|null + */ + public function findByType(string $type): ?TransactionType; + /** * @param TransactionType|null $type * @param string|null $typeString @@ -39,16 +46,10 @@ interface TransactionTypeRepositoryInterface */ public function findTransactionType(?TransactionType $type, ?string $typeString): TransactionType; - /** - * @param string $type - * - * @return TransactionType|null - */ - public function findByType(string $type): ?TransactionType; - /** * @param string $query - * @param int $limit + * @param int $limit + * * @return Collection */ public function searchTypes(string $query, int $limit): Collection; diff --git a/app/Repositories/User/UserRepository.php b/app/Repositories/User/UserRepository.php index c2f681e602..d17756ed9a 100644 --- a/app/Repositories/User/UserRepository.php +++ b/app/Repositories/User/UserRepository.php @@ -22,6 +22,7 @@ declare(strict_types=1); namespace FireflyIII\Repositories\User; +use Exception; use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\Role; use FireflyIII\User; @@ -77,7 +78,7 @@ class UserRepository implements UserRepositoryInterface * @param string $newEmail * * @return bool - * @throws \Exception + * @throws Exception * @see updateEmail * */ @@ -157,7 +158,7 @@ class UserRepository implements UserRepositoryInterface * @param User $user * * @return bool - * @throws \Exception + * @throws Exception */ public function destroy(User $user): bool { diff --git a/app/Repositories/Webhook/WebhookRepository.php b/app/Repositories/Webhook/WebhookRepository.php index aa8690c1fc..81d2cff16e 100644 --- a/app/Repositories/Webhook/WebhookRepository.php +++ b/app/Repositories/Webhook/WebhookRepository.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * WebhookRepository.php @@ -23,6 +44,8 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Webhook; use FireflyIII\Models\Webhook; +use FireflyIII\Models\WebhookAttempt; +use FireflyIII\Models\WebhookMessage; use FireflyIII\User; use Illuminate\Support\Collection; use Str; @@ -42,6 +65,64 @@ class WebhookRepository implements WebhookRepositoryInterface return $this->user->webhooks()->get(); } + /** + * @inheritDoc + */ + public function destroy(Webhook $webhook): void + { + $webhook->delete(); + } + + /** + * @inheritDoc + */ + public function destroyAttempt(WebhookAttempt $attempt): void + { + $attempt->delete(); + } + + /** + * @inheritDoc + */ + public function destroyMessage(WebhookMessage $message): void + { + $message->delete(); + } + + /** + * @inheritDoc + */ + public function getAttempts(WebhookMessage $webhookMessage): Collection + { + return $webhookMessage->webhookAttempts()->orderBy('created_at', 'DESC')->get(['webhook_attempts.*']); + } + + /** + * @inheritDoc + */ + public function getMessages(Webhook $webhook): Collection + { + return $webhook->webhookMessages() + ->orderBy('created_at', 'DESC') + ->get(['webhook_messages.*']); + } + + /** + * @inheritDoc + */ + public function getReadyMessages(Webhook $webhook): Collection + { + return $webhook->webhookMessages() + ->where('webhook_messages.sent', 0) + ->where('webhook_messages.errored', 0) + ->get(['webhook_messages.*']) + ->filter( + function (WebhookMessage $message) { + return $message->webhookAttempts()->count() <= 2; + } + )->splice(0, 3); + } + /** * @inheritDoc */ @@ -79,9 +160,10 @@ class WebhookRepository implements WebhookRepositoryInterface $webhook->trigger = $data['trigger'] ?? $webhook->trigger; $webhook->response = $data['response'] ?? $webhook->response; $webhook->delivery = $data['delivery'] ?? $webhook->delivery; + $webhook->title = $data['title'] ?? $webhook->title; $webhook->url = $data['url'] ?? $webhook->url; - if (array_key_exists('secret', $data) && null !== $data['secret']) { + if (true === $data['secret']) { $secret = $random = Str::random(24); $webhook->secret = $secret; } @@ -90,12 +172,4 @@ class WebhookRepository implements WebhookRepositoryInterface return $webhook; } - - /** - * @inheritDoc - */ - public function destroy(Webhook $webhook): void - { - $webhook->delete(); - } } diff --git a/app/Repositories/Webhook/WebhookRepositoryInterface.php b/app/Repositories/Webhook/WebhookRepositoryInterface.php index 8d6dc5fe46..6ea5b3ecc2 100644 --- a/app/Repositories/Webhook/WebhookRepositoryInterface.php +++ b/app/Repositories/Webhook/WebhookRepositoryInterface.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * WebhookRepositoryInterface.php @@ -23,6 +44,8 @@ declare(strict_types=1); namespace FireflyIII\Repositories\Webhook; use FireflyIII\Models\Webhook; +use FireflyIII\Models\WebhookAttempt; +use FireflyIII\Models\WebhookMessage; use FireflyIII\User; use Illuminate\Support\Collection; @@ -38,6 +61,42 @@ interface WebhookRepositoryInterface */ public function all(): Collection; + /** + * @param Webhook $webhook + */ + public function destroy(Webhook $webhook): void; + + /** + * @param WebhookAttempt $attempt + */ + public function destroyAttempt(WebhookAttempt $attempt): void; + + /** + * @param WebhookMessage $message + */ + public function destroyMessage(WebhookMessage $message): void; + + /** + * @param WebhookMessage $webhookMessage + * + * @return Collection + */ + public function getAttempts(WebhookMessage $webhookMessage): Collection; + + /** + * @param Webhook $webhook + * + * @return Collection + */ + public function getMessages(Webhook $webhook): Collection; + + /** + * @param Webhook $webhook + * + * @return Collection + */ + public function getReadyMessages(Webhook $webhook): Collection; + /** * Set user. * @@ -60,9 +119,4 @@ interface WebhookRepositoryInterface */ public function update(Webhook $webhook, array $data): Webhook; - /** - * @param Webhook $webhook - */ - public function destroy(Webhook $webhook): void; - } diff --git a/app/Rules/IsDuplicateTransaction.php b/app/Rules/IsDuplicateTransaction.php index b1f7d3b066..d5dc158697 100644 --- a/app/Rules/IsDuplicateTransaction.php +++ b/app/Rules/IsDuplicateTransaction.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * IsDuplicateTransaction.php diff --git a/app/Rules/UniqueAccountNumber.php b/app/Rules/UniqueAccountNumber.php index 256c073489..2051b65ff3 100644 --- a/app/Rules/UniqueAccountNumber.php +++ b/app/Rules/UniqueAccountNumber.php @@ -47,6 +47,7 @@ class UniqueAccountNumber implements Rule */ public function __construct(?Account $account, ?string $expectedType) { + Log::debug('Constructed UniqueAccountNumber'); $this->account = $account; $this->expectedType = $expectedType; // a very basic fix to make sure we get the correct account type: @@ -59,6 +60,7 @@ class UniqueAccountNumber implements Rule if ('asset' === $expectedType) { $this->expectedType = AccountType::ASSET; } + Log::debug(sprintf('Expected type is "%s"', $this->expectedType)); } /** @@ -106,7 +108,7 @@ class UniqueAccountNumber implements Rule return false; } } - + Log::debug('Account number is valid.'); return true; } @@ -122,6 +124,7 @@ class UniqueAccountNumber implements Rule ::leftJoin('accounts','accounts.id','=','account_meta.account_id') ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') ->where('accounts.user_id', auth()->user()->id) + ->where('account_types.type', $type) ->where('account_meta.name','=','account_number') ->where('account_meta.data',json_encode($accountNumber)); diff --git a/app/Scopes/LdapFilterScope.php b/app/Scopes/LdapFilterScope.php index 1ce0f23b80..5b9fa51b6a 100644 --- a/app/Scopes/LdapFilterScope.php +++ b/app/Scopes/LdapFilterScope.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); namespace FireflyIII\Scopes; diff --git a/app/Services/Internal/Destroy/AccountDestroyService.php b/app/Services/Internal/Destroy/AccountDestroyService.php index f4b1f6b16c..80c8b1d301 100644 --- a/app/Services/Internal/Destroy/AccountDestroyService.php +++ b/app/Services/Internal/Destroy/AccountDestroyService.php @@ -39,18 +39,6 @@ use Log; */ class AccountDestroyService { - /** - * Constructor. - * - * @codeCoverageIgnore - */ - public function __construct() - { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); - } - } - /** * @param Account $account * @param Account|null $moveTo diff --git a/app/Services/Internal/Destroy/BillDestroyService.php b/app/Services/Internal/Destroy/BillDestroyService.php index 34d43a1fd4..735f282ba1 100644 --- a/app/Services/Internal/Destroy/BillDestroyService.php +++ b/app/Services/Internal/Destroy/BillDestroyService.php @@ -33,16 +33,6 @@ use Log; */ class BillDestroyService { - /** - * Constructor. - */ - public function __construct() - { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); - } - } - /** * @param Bill $bill */ diff --git a/app/Services/Internal/Destroy/BudgetDestroyService.php b/app/Services/Internal/Destroy/BudgetDestroyService.php index 745c4886fd..41280b83fb 100644 --- a/app/Services/Internal/Destroy/BudgetDestroyService.php +++ b/app/Services/Internal/Destroy/BudgetDestroyService.php @@ -34,16 +34,6 @@ use Log; */ class BudgetDestroyService { - /** - * Constructor. - */ - public function __construct() - { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); - } - } - /** * @param Budget $budget */ diff --git a/app/Services/Internal/Destroy/CategoryDestroyService.php b/app/Services/Internal/Destroy/CategoryDestroyService.php index e39ab91700..75ae8e5010 100644 --- a/app/Services/Internal/Destroy/CategoryDestroyService.php +++ b/app/Services/Internal/Destroy/CategoryDestroyService.php @@ -34,16 +34,6 @@ use Log; */ class CategoryDestroyService { - /** - * Constructor. - */ - public function __construct() - { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); - } - } - /** * @param Category $category */ diff --git a/app/Services/Internal/Destroy/CurrencyDestroyService.php b/app/Services/Internal/Destroy/CurrencyDestroyService.php index e82fe525f8..bdec425c5d 100644 --- a/app/Services/Internal/Destroy/CurrencyDestroyService.php +++ b/app/Services/Internal/Destroy/CurrencyDestroyService.php @@ -33,16 +33,6 @@ use Log; */ class CurrencyDestroyService { - /** - * Constructor. - */ - public function __construct() - { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); - } - } - /** * @param TransactionCurrency $currency */ diff --git a/app/Services/Internal/Destroy/JournalDestroyService.php b/app/Services/Internal/Destroy/JournalDestroyService.php index 2c9a9867b1..321c3be0ae 100644 --- a/app/Services/Internal/Destroy/JournalDestroyService.php +++ b/app/Services/Internal/Destroy/JournalDestroyService.php @@ -38,16 +38,6 @@ use Log; */ class JournalDestroyService { - /** - * Constructor. - */ - public function __construct() - { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); - } - } - /** * @param TransactionJournal $journal */ diff --git a/app/Services/Internal/Destroy/RecurrenceDestroyService.php b/app/Services/Internal/Destroy/RecurrenceDestroyService.php index c1f7bebb49..6e704bbb2b 100644 --- a/app/Services/Internal/Destroy/RecurrenceDestroyService.php +++ b/app/Services/Internal/Destroy/RecurrenceDestroyService.php @@ -34,16 +34,6 @@ use Log; */ class RecurrenceDestroyService { - /** - * Constructor. - */ - public function __construct() - { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); - } - } - /** * Delete recurrence. * diff --git a/app/Services/Internal/Support/AccountServiceTrait.php b/app/Services/Internal/Support/AccountServiceTrait.php index 55f30c5c9d..563d2b89aa 100644 --- a/app/Services/Internal/Support/AccountServiceTrait.php +++ b/app/Services/Internal/Support/AccountServiceTrait.php @@ -35,6 +35,7 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Services\Internal\Destroy\TransactionGroupDestroyService; use Log; use Validator; @@ -45,6 +46,8 @@ use Validator; */ trait AccountServiceTrait { + protected AccountRepositoryInterface $accountRepository; + /** * @param null|string $iban * @@ -80,10 +83,17 @@ trait AccountServiceTrait public function updateMetaData(Account $account, array $data): void { $fields = $this->validFields; - if ($account->accountType->type === AccountType::ASSET) { $fields = $this->validAssetFields; } + + // the account role may not be set in the data but we may have it already: + if (!array_key_exists('account_role', $data)) { + $data['account_role'] = null; + } + if (null === $data['account_role']) { + $data['account_role'] = $this->accountRepository->getMetaValue($account, 'account_role'); + } if ($account->accountType->type === AccountType::ASSET && isset($data['account_role']) && 'ccAsset' === $data['account_role']) { $fields = $this->validCCFields; // @codeCoverageIgnore } @@ -171,8 +181,8 @@ trait AccountServiceTrait */ public function isEmptyOBData(array $data): bool { - if (!array_key_exists('opening_balance', $data) && - !array_key_exists('opening_balance_date', $data) + if (!array_key_exists('opening_balance', $data) + && !array_key_exists('opening_balance_date', $data) ) { // not set, so false. return false; @@ -180,8 +190,7 @@ trait AccountServiceTrait // if isset, but is empty: if ( (array_key_exists('opening_balance', $data) && '' === $data['opening_balance']) - || - (array_key_exists('opening_balance_date', $data) && '' === $data['opening_balance_date']) + || (array_key_exists('opening_balance_date', $data) && '' === $data['opening_balance_date']) ) { return true; } @@ -223,7 +232,15 @@ trait AccountServiceTrait return null; // @codeCoverageIgnoreEnd } - $amount = app('steam')->positive($amount); + $amount = app('steam')->positive($amount); + if (!array_key_exists('currency_id', $data)) { + $currency = $this->accountRepository->getAccountCurrency($account); + if (null === $currency) { + $currency = app('default')->getDefaultCurrencyByUser($account->user); + } + $data['currency_id'] = $currency->id; + } + $submission = [ 'group_title' => null, 'user' => $account->user_id, @@ -266,6 +283,7 @@ trait AccountServiceTrait Log::error($e->getMessage()); Log::error($e->getTraceAsString()); } + // @codeCoverageIgnoreEnd return $group; @@ -342,6 +360,16 @@ trait AccountServiceTrait if (null === $obGroup) { return $this->createOBGroup($account, $data); } + + // $data['currency_id'] is empty so creating a new journal may break. + if (!array_key_exists('currency_id', $data)) { + $currency = $this->accountRepository->getAccountCurrency($account); + if (null === $currency) { + $currency = app('default')->getDefaultCurrencyByUser($account->user); + } + $data['currency_id'] = $currency->id; + } + /** @var TransactionJournal $journal */ $journal = $obGroup->transactionJournals()->first(); $journal->date = $data['opening_balance_date'] ?? $journal->date; diff --git a/app/Services/Internal/Support/JournalServiceTrait.php b/app/Services/Internal/Support/JournalServiceTrait.php index 8fd9c1ca61..7269164502 100644 --- a/app/Services/Internal/Support/JournalServiceTrait.php +++ b/app/Services/Internal/Support/JournalServiceTrait.php @@ -109,6 +109,7 @@ trait JournalServiceTrait $result = $this->findAccountByName($result, $data, $expectedTypes[$transactionType]); $result = $this->findAccountByIban($result, $data, $expectedTypes[$transactionType]); $result = $this->createAccount($result, $data, $expectedTypes[$transactionType][0]); + return $this->getCashAccount($result, $data, $expectedTypes[$transactionType]); } @@ -182,7 +183,7 @@ trait JournalServiceTrait */ protected function storeNotes(TransactionJournal $journal, ?string $notes): void { - $notes = (string) $notes; + $notes = (string)$notes; $note = $journal->notes()->first(); if ('' !== $notes) { if (null === $note) { @@ -222,11 +223,12 @@ trait JournalServiceTrait $set = []; if (!is_array($tags)) { Log::debug('Tags is not an array, break.'); + return; } Log::debug('Start of loop.'); foreach ($tags as $string) { - $string = (string) $string; + $string = (string)$string; Log::debug(sprintf('Now at tag "%s"', $string)); if ('' !== $string) { $tag = $this->tagFactory->findOrCreate($string); @@ -363,7 +365,7 @@ trait JournalServiceTrait throw new FireflyException('TransactionFactory: Cannot create asset account with these values', $data); } // fix name of account if only IBAN is given: - if ('' === (string) $data['name'] && '' !== (string) $data['iban']) { + if ('' === (string)$data['name'] && '' !== (string)$data['iban']) { Log::debug(sprintf('Account name is now IBAN ("%s")', $data['iban'])); $data['name'] = $data['iban']; } @@ -379,6 +381,7 @@ trait JournalServiceTrait 'active' => true, 'iban' => $data['iban'], 'currency_id' => $data['currency_id'] ?? null, + 'order' => $this->accountRepository->maxOrder($preferredType), ] ); // store BIC diff --git a/app/Services/Internal/Support/RecurringTransactionTrait.php b/app/Services/Internal/Support/RecurringTransactionTrait.php index 4c2c727f9e..e58312b75b 100644 --- a/app/Services/Internal/Support/RecurringTransactionTrait.php +++ b/app/Services/Internal/Support/RecurringTransactionTrait.php @@ -32,6 +32,7 @@ use FireflyIII\Factory\PiggyBankFactory; use FireflyIII\Factory\TransactionCurrencyFactory; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; +use FireflyIII\Models\Note; use FireflyIII\Models\Recurrence; use FireflyIII\Models\RecurrenceMeta; use FireflyIII\Models\RecurrenceRepetition; @@ -61,7 +62,7 @@ trait RecurringTransactionTrait 'recurrence_id' => $recurrence->id, 'repetition_type' => $array['type'], 'repetition_moment' => $array['moment'] ?? '', - 'repetition_skip' => $array['skip'], + 'repetition_skip' => $array['skip'] ?? 0, 'weekend' => $array['weekend'] ?? 1, ] ); @@ -69,6 +70,38 @@ trait RecurringTransactionTrait } } + + /** + * @param Recurrence $recurrence + * @param string $note + * + * @return bool + */ + public function updateNote(Recurrence $recurrence, string $note): bool + { + if ('' === $note) { + $dbNote = $recurrence->notes()->first(); + if (null !== $dbNote) { + try { + $dbNote->delete(); + } catch (Exception $e) { + Log::debug(sprintf('Error deleting note: %s', $e->getMessage())); + } + } + + return true; + } + $dbNote = $recurrence->notes()->first(); + if (null === $dbNote) { + $dbNote = new Note(); + $dbNote->noteable()->associate($recurrence); + } + $dbNote->text = trim($note); + $dbNote->save(); + + return true; + } + /** * Store transactions of a recurring transactions. It's complex but readable. * @@ -82,8 +115,8 @@ trait RecurringTransactionTrait foreach ($transactions as $array) { $sourceTypes = config(sprintf('firefly.expected_source_types.source.%s', $recurrence->transactionType->type)); $destTypes = config(sprintf('firefly.expected_source_types.destination.%s', $recurrence->transactionType->type)); - $source = $this->findAccount($sourceTypes, $array['source_id'], $array['source_name']); - $destination = $this->findAccount($destTypes, $array['destination_id'], $array['destination_name']); + $source = $this->findAccount($sourceTypes, $array['source_id'], null); + $destination = $this->findAccount($destTypes, $array['destination_id'], null); /** @var TransactionCurrencyFactory $factory */ $factory = app(TransactionCurrencyFactory::class); @@ -107,7 +140,6 @@ trait RecurringTransactionTrait } // TODO typeOverrule: the account validator may have another opinion on the transaction type. - $transaction = new RecurrenceTransaction( [ 'recurrence_id' => $recurrence->id, @@ -116,30 +148,36 @@ trait RecurringTransactionTrait 'source_id' => $source->id, 'destination_id' => $destination->id, 'amount' => $array['amount'], - 'foreign_amount' => '' === (string)$array['foreign_amount'] ? null : (string)$array['foreign_amount'], + 'foreign_amount' => array_key_exists('foreign_amount', $array) ? (string)$array['foreign_amount'] : null, 'description' => $array['description'], ] ); $transaction->save(); - /** @var BudgetFactory $budgetFactory */ - $budgetFactory = app(BudgetFactory::class); - $budgetFactory->setUser($recurrence->user); - $budget = $budgetFactory->find($array['budget_id'], $array['budget_name']); + $budget = null; + if (array_key_exists('budget_id', $array)) { + /** @var BudgetFactory $budgetFactory */ + $budgetFactory = app(BudgetFactory::class); + $budgetFactory->setUser($recurrence->user); + $budget = $budgetFactory->find($array['budget_id'], null); + } - /** @var CategoryFactory $categoryFactory */ - $categoryFactory = app(CategoryFactory::class); - $categoryFactory->setUser($recurrence->user); - $category = $categoryFactory->findOrCreate($array['category_id'], $array['category_name']); + $category = null; + if (array_key_exists('category_id', $array)) { + /** @var CategoryFactory $categoryFactory */ + $categoryFactory = app(CategoryFactory::class); + $categoryFactory->setUser($recurrence->user); + $category = $categoryFactory->findOrCreate($array['category_id'], null); + } // same for piggy bank - $piggyId = (int)($array['piggy_bank_id'] ?? 0.0); - $piggyName = $array['piggy_bank_name'] ?? ''; - $this->updatePiggyBank($transaction, $piggyId, $piggyName); + if (array_key_exists('piggy_bank_id', $array)) { + $this->updatePiggyBank($transaction, (int)$array['piggy_bank_id']); + } - // same for tags - $tags = $array['tags'] ?? []; - $this->updateTags($transaction, $tags); + if(array_key_exists('tags', $array)) { + $this->updateTags($transaction, $array['tags']); + } // create recurrence transaction meta: if (null !== $budget) { @@ -246,14 +284,13 @@ trait RecurringTransactionTrait /** * @param RecurrenceTransaction $transaction * @param int $piggyId - * @param string $piggyName */ - protected function updatePiggyBank(RecurrenceTransaction $transaction, int $piggyId, string $piggyName): void + protected function updatePiggyBank(RecurrenceTransaction $transaction, int $piggyId): void { /** @var PiggyBankFactory $factory */ $factory = app(PiggyBankFactory::class); $factory->setUser($transaction->recurrence->user); - $piggyBank = $factory->find($piggyId, $piggyName); + $piggyBank = $factory->find($piggyId, null); if (null !== $piggyBank) { /** @var RecurrenceMeta $entry */ $entry = $transaction->recurrenceTransactionMeta()->where('name', 'piggy_bank_id')->first(); diff --git a/app/Services/Internal/Update/AccountUpdateService.php b/app/Services/Internal/Update/AccountUpdateService.php index 8b345a2790..acbd59960b 100644 --- a/app/Services/Internal/Update/AccountUpdateService.php +++ b/app/Services/Internal/Update/AccountUpdateService.php @@ -33,6 +33,7 @@ use Log; /** * Class AccountUpdateService + * TODO this is a mess. */ class AccountUpdateService { @@ -50,9 +51,6 @@ class AccountUpdateService */ public function __construct() { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); - } // TODO move to configuration. $this->canHaveVirtual = [AccountType::ASSET, AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::CREDITCARD]; $this->accountRepository = app(AccountRepositoryInterface::class); @@ -61,6 +59,14 @@ class AccountUpdateService $this->validFields = ['account_number', 'currency_id', 'BIC', 'interest', 'interest_period', 'include_net_worth']; } + /** + * @param User $user + */ + public function setUser(User $user): void + { + $this->user = $user; + } + /** * Update account data. * @@ -71,14 +77,16 @@ class AccountUpdateService */ public function update(Account $account, array $data): Account { + Log::debug(sprintf('Now in %s', __METHOD__)); $this->accountRepository->setUser($account->user); $this->user = $account->user; $account = $this->updateAccount($account, $data); + $account = $this->updateAccountOrder($account, $data); // find currency, or use default currency instead. - if (isset($data['currency_id']) && (null !== $data['currency_id'] || null !== $data['currency_code'])) { + if (array_key_exists('currency_id', $data) || array_key_exists('currency_code', $data)) { $currency = $this->getCurrency((int)($data['currency_id'] ?? null), (string)($data['currency_code'] ?? null)); - unset($data['currency_code']); + unset($data['currency_code'], $data['currency_id']); $data['currency_id'] = $currency->id; } @@ -105,8 +113,139 @@ class AccountUpdateService /** * @param Account $account * @param array $data + * + * @return Account */ - private function updateLocation(Account $account, array $data): void { + private function updateAccount(Account $account, array $data): Account + { + // update the account itself: + $account->name = $data['name'] ?? $account->name; + $account->active = $data['active'] ?? $account->active; + $account->iban = $data['iban'] ?? $account->iban; + + // liability stuff: + $liabilityType = $data['liability_type'] ?? ''; + if ($this->isLiability($account) && $this->isLiabilityType($liabilityType)) { + $type = $this->getAccountType($liabilityType); + $account->account_type_id = $type->id; + } + + // update virtual balance (could be set to zero if empty string). + if (array_key_exists('virtual_balance', $data) && null !== $data['virtual_balance']) { + $account->virtual_balance = '' === trim($data['virtual_balance']) ? '0' : $data['virtual_balance']; + } + + $account->save(); + + return $account; + } + + /** + * @param Account $account + * + * @return bool + */ + private function isLiability(Account $account): bool + { + $type = $account->accountType->type; + + return in_array($type, [AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE], true); + } + + /** + * @param string $type + * + * @return bool + */ + private function isLiabilityType(string $type): bool + { + if ('' === $type) { + return false; + } + + return 1 === AccountType::whereIn('type', [AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE])->where('type', ucfirst($type))->count(); + } + + /** + * @param string $type + */ + private function getAccountType(string $type): AccountType + { + return AccountType::whereType(ucfirst($type))->first(); + } + + /** + * @param Account $account + * @param array $data + * + * @return Account + */ + public function updateAccountOrder(Account $account, array $data): Account + { + // skip if no order info + if (!array_key_exists('order', $data) || $data['order'] === $account->order) { + Log::debug(sprintf('Account order will not be touched because its not set or already at %d.', $account->order)); + + return $account; + } + // skip if not of orderable type. + $type = $account->accountType->type; + if (!in_array($type, [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT], true)) { + Log::debug('Will not change order of this account.'); + + return $account; + } + // get account type ID's because a join and an update is hard: + $oldOrder = (int)$account->order; + $newOrder = $data['order']; + Log::debug(sprintf('Order is set to be updated from %s to %s', $oldOrder, $newOrder)); + $list = $this->getTypeIds([AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT]); + if (in_array($type, [AccountType::ASSET], true)) { + $list = $this->getTypeIds([AccountType::ASSET]); + } + + if ($newOrder > $oldOrder) { + $this->user->accounts()->where('accounts.order', '<=', $newOrder)->where('accounts.order', '>', $oldOrder) + ->where('accounts.id', '!=', $account->id) + ->whereIn('accounts.account_type_id', $list) + ->decrement('order', 1); + $account->order = $newOrder; + Log::debug(sprintf('Order of account #%d ("%s") is now %d', $account->id, $account->name, $newOrder)); + $account->save(); + + return $account; + } + + $this->user->accounts()->where('accounts.order', '>=', $newOrder)->where('accounts.order', '<', $oldOrder) + ->where('accounts.id', '!=', $account->id) + ->whereIn('accounts.account_type_id', $list) + ->increment('order', 1); + $account->order = $newOrder; + Log::debug(sprintf('Order of account #%d ("%s") is now %d', $account->id, $account->name, $newOrder)); + $account->save(); + + return $account; + } + + private function getTypeIds(array $array): array + { + $return = []; + /** @var string $type */ + foreach ($array as $type) { + /** @var AccountType $type */ + $type = AccountType::whereType($type)->first(); + $return[] = (int)$type->id; + } + + return $return; + } + + /** + * @param Account $account + * @param array $data + */ + private function updateLocation(Account $account, array $data): void + { $updateLocation = $data['update_location'] ?? false; // location must be updated? if (true === $updateLocation) { @@ -131,93 +270,6 @@ class AccountUpdateService } } - /** - * @param Account $account - * - * @return bool - */ - private function isLiability(Account $account): bool - { - $type = $account->accountType->type; - - return in_array($type, [AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE], true); - } - - /** - * @param int $accountTypeId - * - * @return bool - */ - private function isLiabilityTypeId(int $accountTypeId): bool - { - if (0 === $accountTypeId) { - return false; - } - - return 1 === AccountType::whereIn('type', [AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE])->where('id', $accountTypeId)->count(); - } - - /** - * @param Account $account - * @param array $data - * - * @return Account - */ - private function updateAccount(Account $account, array $data): Account - { - // update the account itself: - $account->name = $data['name'] ?? $account->name; - $account->active = $data['active'] ?? $account->active; - $account->iban = $data['iban'] ?? $account->iban; - $account->order = $data['order'] ?? $account->order; - - // if account type is a liability, the liability type (account type) - // can be updated to another one. - if ($this->isLiability($account) && $this->isLiabilityTypeId((int)($data['account_type_id'] ?? 0))) { - $account->account_type_id = (int)$data['account_type_id']; - } - - // update virtual balance (could be set to zero if empty string). - if (null !== $data['virtual_balance']) { - $account->virtual_balance = '' === trim($data['virtual_balance']) ? '0' : $data['virtual_balance']; - } - - $account->save(); - - return $account; - } - - /** - * @param Account $account - * @param array $data - */ - private function updatePreferences(Account $account, array $data): void - { - Log::debug(sprintf('Now in updatePreferences(#%d)', $account->id)); - if (array_key_exists('active', $data) && (false === $data['active'] || 0 === $data['active'])) { - Log::debug('Account was marked as inactive.'); - $preference = app('preferences')->getForUser($account->user, 'frontpageAccounts'); - if (null !== $preference) { - $removeAccountId = (int)$account->id; - $array = $preference->data; - Log::debug('Current list of accounts: ', $array); - Log::debug(sprintf('Going to remove account #%d', $removeAccountId)); - $filtered = array_filter( - $array, function ($accountId) use ($removeAccountId) { - return (int)$accountId !== $removeAccountId; - } - ); - Log::debug('Left with accounts', array_values($filtered)); - app('preferences')->setForUser($account->user, 'frontpageAccounts', array_values($filtered)); - app('preferences')->forget($account->user, 'frontpageAccounts'); - return; - } - Log::debug("Found no frontpageAccounts preference, do nothing."); - return; - } - Log::debug('Account was not marked as inactive, do nothing.'); - } - /** * @param Account $account * @param array $data @@ -241,4 +293,37 @@ class AccountUpdateService } } } + + /** + * @param Account $account + * @param array $data + */ + private function updatePreferences(Account $account, array $data): void + { + Log::debug(sprintf('Now in updatePreferences(#%d)', $account->id)); + if (array_key_exists('active', $data) && (false === $data['active'] || 0 === $data['active'])) { + Log::debug('Account was marked as inactive.'); + $preference = app('preferences')->getForUser($account->user, 'frontpageAccounts'); + if (null !== $preference) { + $removeAccountId = (int)$account->id; + $array = $preference->data; + Log::debug('Current list of accounts: ', $array); + Log::debug(sprintf('Going to remove account #%d', $removeAccountId)); + $filtered = array_filter( + $array, function ($accountId) use ($removeAccountId) { + return (int)$accountId !== $removeAccountId; + } + ); + Log::debug('Left with accounts', array_values($filtered)); + app('preferences')->setForUser($account->user, 'frontpageAccounts', array_values($filtered)); + app('preferences')->forget($account->user, 'frontpageAccounts'); + + return; + } + Log::debug("Found no frontpageAccounts preference, do nothing."); + + return; + } + Log::debug('Account was not marked as inactive, do nothing.'); + } } diff --git a/app/Services/Internal/Update/BillUpdateService.php b/app/Services/Internal/Update/BillUpdateService.php index ca4168ebf0..1cad40408f 100644 --- a/app/Services/Internal/Update/BillUpdateService.php +++ b/app/Services/Internal/Update/BillUpdateService.php @@ -23,12 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Services\Internal\Update; -use DB; use FireflyIII\Factory\TransactionCurrencyFactory; use FireflyIII\Models\Bill; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleTrigger; -use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups; use FireflyIII\Services\Internal\Support\BillServiceTrait; @@ -55,25 +53,21 @@ class BillUpdateService public function update(Bill $bill, array $data): Bill { $this->user = $bill->user; - /** @var TransactionCurrencyFactory $factory */ - $factory = app(TransactionCurrencyFactory::class); - /** @var TransactionCurrency $currency */ - $currency = $factory->find($data['currency_id'] ?? null, $data['currency_code'] ?? null); - if (null === $currency) { - // use default currency: - $currency = app('amount')->getDefaultCurrencyByUser($bill->user); + if (array_key_exists('currency_id', $data) || array_key_exists('currency_code', $data)) { + $factory = app(TransactionCurrencyFactory::class); + $currency = $factory->find((int) ($data['currency_id'] ?? null), $data['currency_code'] ?? null) ?? app('amount')->getDefaultCurrencyByUser($bill->user); + + // enable the currency if it isn't. + $currency->enabled = true; + $currency->save(); + $bill->transaction_currency_id = $currency->id; + $bill->save(); } - - // enable the currency if it isn't. - $currency->enabled = true; - $currency->save(); - - // new values - $data['transaction_currency_name'] = $currency->name; - $bill = $this->updateBillProperties($bill, $data); - $bill->transaction_currency_id = $currency->id; + // update bill properties: + $bill = $this->updateBillProperties($bill, $data); $bill->save(); + $bill->refresh(); // old values $oldData = [ 'name' => $bill->name, @@ -84,58 +78,127 @@ class BillUpdateService // update note: - if (isset($data['notes'])) { + if (array_key_exists('notes', $data)) { $this->updateNote($bill, (string)$data['notes']); } // update order. - // update the order of the piggy bank: - $oldOrder = (int)$bill->order; - $newOrder = (int)($data['order'] ?? $oldOrder); - if ($oldOrder !== $newOrder) { - $this->updateOrder($bill, $oldOrder, $newOrder); + if (array_key_exists('order', $data)) { + // update the order of the piggy bank: + $oldOrder = (int)$bill->order; + $newOrder = (int)($data['order'] ?? $oldOrder); + if ($oldOrder !== $newOrder) { + $this->updateOrder($bill, $oldOrder, $newOrder); + } } // update rule actions. - $this->updateBillActions($bill, $oldData['name'], $data['name']); - $this->updateBillTriggers($bill, $oldData, $data); + if (array_key_exists('name', $data)) { + $this->updateBillActions($bill, $oldData['name'], $data['name']); + $this->updateBillTriggers($bill, $oldData, $data); + } // update using name: - $objectGroupTitle = $data['object_group'] ?? ''; - if ('' !== $objectGroupTitle) { - $objectGroup = $this->findOrCreateObjectGroup($objectGroupTitle); - if (null !== $objectGroup) { - $bill->objectGroups()->sync([$objectGroup->id]); + if (array_key_exists('object_group_title', $data)) { + $objectGroupTitle = $data['object_group_title'] ?? ''; + if ('' !== $objectGroupTitle) { + $objectGroup = $this->findOrCreateObjectGroup($objectGroupTitle); + if (null !== $objectGroup) { + $bill->objectGroups()->sync([$objectGroup->id]); + $bill->save(); + } + + return $bill; + } + // remove if name is empty. Should be overruled by ID. + if ('' === $objectGroupTitle) { + $bill->objectGroups()->sync([]); $bill->save(); } - - return $bill; - } - // remove if name is empty. Should be overruled by ID. - if ('' === $objectGroupTitle) { - $bill->objectGroups()->sync([]); - $bill->save(); } + if (array_key_exists('object_group_id', $data)) { + // try also with ID: + $objectGroupId = (int)($data['object_group_id'] ?? 0); + if (0 !== $objectGroupId) { + $objectGroup = $this->findObjectGroupById($objectGroupId); + if (null !== $objectGroup) { + $bill->objectGroups()->sync([$objectGroup->id]); + $bill->save(); + } - // try also with ID: - $objectGroupId = (int)($data['object_group_id'] ?? 0); - if (0 !== $objectGroupId) { - $objectGroup = $this->findObjectGroupById($objectGroupId); - if (null !== $objectGroup) { - $bill->objectGroups()->sync([$objectGroup->id]); + return $bill; + } + if (0 === $objectGroupId) { + $bill->objectGroups()->sync([]); $bill->save(); } - - return $bill; - } - if (0 === $objectGroupId) { - $bill->objectGroups()->sync([]); - $bill->save(); } return $bill; } + /** + * @param Bill $bill + * @param array $data + * + * @return Bill + */ + private function updateBillProperties(Bill $bill, array $data): Bill + { + if (isset($data['name']) && '' !== (string)$data['name']) { + $bill->name = $data['name']; + } + + if (isset($data['amount_min']) && '' !== (string)$data['amount_min']) { + $bill->amount_min = $data['amount_min']; + } + if (isset($data['amount_max']) && '' !== (string)$data['amount_max']) { + $bill->amount_max = $data['amount_max']; + } + if (isset($data['date']) && '' !== (string)$data['date']) { + $bill->date = $data['date']; + } + if (isset($data['repeat_freq']) && '' !== (string)$data['repeat_freq']) { + $bill->repeat_freq = $data['repeat_freq']; + } + if (array_key_exists('skip', $data)) { + $bill->skip = $data['skip']; + } + if (array_key_exists('active', $data)) { + $bill->active = $data['active']; + } + + $bill->match = 'EMPTY'; + $bill->automatch = true; + $bill->save(); + + return $bill; + } + + /** + * @param Bill $bill + * @param int $oldOrder + * @param int $newOrder + */ + private function updateOrder(Bill $bill, int $oldOrder, int $newOrder): void + { + if ($newOrder > $oldOrder) { + $this->user->bills()->where('order', '<=', $newOrder)->where('order', '>', $oldOrder) + ->where('bills.id', '!=', $bill->id) + ->decrement('bills.order', 1); + $bill->order = $newOrder; + $bill->save(); + } + if ($newOrder < $oldOrder) { + $this->user->bills()->where('order', '>=', $newOrder)->where('order', '<', $oldOrder) + ->where('bills.id', '!=', $bill->id) + ->increment('bills.order', 1); + $bill->order = $newOrder; + $bill->save(); + } + + } + /** * @param Bill $bill * @param array $oldData @@ -195,7 +258,6 @@ class BillUpdateService } } - /** * @param Rule $rule * @param string $key @@ -206,67 +268,4 @@ class BillUpdateService { return $rule->ruleTriggers()->where('trigger_type', $key)->first(); } - - /** - * @param Bill $bill - * @param int $oldOrder - * @param int $newOrder - */ - private function updateOrder(Bill $bill, int $oldOrder, int $newOrder): void - { - if ($newOrder > $oldOrder) { - $this->user->bills()->where('order', '<=', $newOrder)->where('order', '>', $oldOrder) - ->where('bills.id', '!=', $bill->id) - ->update(['order' => DB::raw('bills.order-1')]); - $bill->order = $newOrder; - $bill->save(); - } - if ($newOrder < $oldOrder) { - $this->user->bills()->where('order', '>=', $newOrder)->where('order', '<', $oldOrder) - ->where('bills.id', '!=', $bill->id) - ->update(['order' => DB::raw('bills.order+1')]); - $bill->order = $newOrder; - $bill->save(); - } - - } - - /** - * @param Bill $bill - * @param array $data - * - * @return Bill - */ - private function updateBillProperties(Bill $bill, array $data): Bill - { - - if (isset($data['name']) && '' !== (string)$data['name']) { - $bill->name = $data['name']; - } - - if (isset($data['amount_min']) && '' !== (string)$data['amount_min']) { - $bill->amount_min = $data['amount_min']; - } - if (isset($data['amount_max']) && '' !== (string)$data['amount_max']) { - $bill->amount_max = $data['amount_max']; - } - if (isset($data['date']) && '' !== (string)$data['date']) { - $bill->date = $data['date']; - } - if (isset($data['repeat_freq']) && '' !== (string)$data['repeat_freq']) { - $bill->repeat_freq = $data['repeat_freq']; - } - if (isset($data['skip']) && '' !== (string)$data['skip']) { - $bill->skip = $data['skip']; - } - if (isset($data['active']) && is_bool($data['active'])) { - $bill->active = $data['active']; - } - - $bill->match = 'EMPTY'; - $bill->automatch = true; - $bill->save(); - - return $bill; - } } diff --git a/app/Services/Internal/Update/CategoryUpdateService.php b/app/Services/Internal/Update/CategoryUpdateService.php index aa78e1161a..819db8b38d 100644 --- a/app/Services/Internal/Update/CategoryUpdateService.php +++ b/app/Services/Internal/Update/CategoryUpdateService.php @@ -23,13 +23,13 @@ declare(strict_types=1); namespace FireflyIII\Services\Internal\Update; +use Exception; use FireflyIII\Models\Category; use FireflyIII\Models\Note; use FireflyIII\Models\RecurrenceTransactionMeta; use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleTrigger; use Log; -use Exception; /** * Class CategoryUpdateService @@ -46,14 +46,19 @@ class CategoryUpdateService */ public function __construct() { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); - } if (auth()->check()) { $this->user = auth()->user(); } } + /** + * @param mixed $user + */ + public function setUser($user): void + { + $this->user = $user; + } + /** * @param Category $category * @param array $data @@ -63,18 +68,41 @@ class CategoryUpdateService public function update(Category $category, array $data): Category { $oldName = $category->name; - $category->name = $data['name']; - $category->save(); + if(array_key_exists('name', $data)) { + $category->name = $data['name']; + $category->save(); + // update triggers and actions + $this->updateRuleTriggers($oldName, $data['name']); + $this->updateRuleActions($oldName, $data['name']); + $this->updateRecurrences($oldName, $data['name']); + } - // update triggers and actions - $this->updateRuleTriggers($oldName, $data['name']); - $this->updateRuleActions($oldName, $data['name']); - $this->updateRecurrences($oldName, $data['name']); $this->updateNotes($category, $data); return $category; } + /** + * @param string $oldName + * @param string $newName + */ + private function updateRuleTriggers(string $oldName, string $newName): void + { + $types = ['category_is',]; + $triggers = RuleTrigger::leftJoin('rules', 'rules.id', '=', 'rule_triggers.rule_id') + ->where('rules.user_id', $this->user->id) + ->whereIn('rule_triggers.trigger_type', $types) + ->where('rule_triggers.trigger_value', $oldName) + ->get(['rule_triggers.*']); + Log::debug(sprintf('Found %d triggers to update.', $triggers->count())); + /** @var RuleTrigger $trigger */ + foreach ($triggers as $trigger) { + $trigger->trigger_value = $newName; + $trigger->save(); + Log::debug(sprintf('Updated trigger %d: %s', $trigger->id, $trigger->trigger_value)); + } + } + /** * @param string $oldName * @param string $newName @@ -96,35 +124,6 @@ class CategoryUpdateService } } - /** - * @param string $oldName - * @param string $newName - */ - private function updateRuleTriggers(string $oldName, string $newName): void - { - $types = ['category_is',]; - $triggers = RuleTrigger::leftJoin('rules', 'rules.id', '=', 'rule_triggers.rule_id') - ->where('rules.user_id', $this->user->id) - ->whereIn('rule_triggers.trigger_type', $types) - ->where('rule_triggers.trigger_value', $oldName) - ->get(['rule_triggers.*']); - Log::debug(sprintf('Found %d triggers to update.', $triggers->count())); - /** @var RuleTrigger $trigger */ - foreach ($triggers as $trigger) { - $trigger->trigger_value = $newName; - $trigger->save(); - Log::debug(sprintf('Updated trigger %d: %s', $trigger->id, $trigger->trigger_value)); - } - } - - /** - * @param mixed $user - */ - public function setUser($user): void - { - $this->user = $user; - } - /** * @param string $oldName * @param string $newName @@ -145,7 +144,7 @@ class CategoryUpdateService * @param Category $category * @param array $data * - * @throws \Exception + * @throws Exception */ private function updateNotes(Category $category, array $data): void { diff --git a/app/Services/Internal/Update/CurrencyUpdateService.php b/app/Services/Internal/Update/CurrencyUpdateService.php index b016dfbd5b..b1d99f0e05 100644 --- a/app/Services/Internal/Update/CurrencyUpdateService.php +++ b/app/Services/Internal/Update/CurrencyUpdateService.php @@ -28,20 +28,11 @@ use Log; /** * Class CurrencyUpdateService + * * @codeCoverageIgnore */ class CurrencyUpdateService { - /** - * Constructor. - */ - public function __construct() - { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); - } - } - /** * @param TransactionCurrency $currency * @param array $data @@ -50,11 +41,26 @@ class CurrencyUpdateService */ public function update(TransactionCurrency $currency, array $data): TransactionCurrency { - $currency->code = $data['code']; - $currency->symbol = $data['symbol']; - $currency->name = $data['name']; - $currency->enabled = $data['enabled']; - $currency->decimal_places = $data['decimal_places']; + if (array_key_exists('code', $data) && '' !== (string)$data['code']) { + $currency->code = $data['code']; + } + + if (array_key_exists('symbol', $data) && '' !== (string)$data['symbol']) { + $currency->symbol = $data['symbol']; + } + + if (array_key_exists('name', $data) && '' !== (string)$data['name']) { + $currency->name = $data['name']; + } + + if (array_key_exists('enabled', $data) && is_bool($data['enabled'])) { + $currency->enabled = $data['enabled']; + } + + if (array_key_exists('decimal_places', $data) && is_int($data['decimal_places'])) { + $currency->decimal_places = $data['decimal_places']; + } + $currency->save(); return $currency; diff --git a/app/Services/Internal/Update/GroupCloneService.php b/app/Services/Internal/Update/GroupCloneService.php index e5fb261197..81abc35a3f 100644 --- a/app/Services/Internal/Update/GroupCloneService.php +++ b/app/Services/Internal/Update/GroupCloneService.php @@ -108,16 +108,15 @@ class GroupCloneService } /** - * @param TransactionJournalMeta $meta - * @param TransactionJournal $newJournal + * @param Transaction $transaction + * @param TransactionJournal $newJournal */ - private function cloneMeta(TransactionJournalMeta $meta, TransactionJournal $newJournal): void + private function cloneTransaction(Transaction $transaction, TransactionJournal $newJournal): void { - $newMeta = $meta->replicate(); - $newMeta->transaction_journal_id = $newJournal->id; - if ('recurrence_id' !== $newMeta->name) { - $newMeta->save(); - } + $newTransaction = $transaction->replicate(); + $newTransaction->transaction_journal_id = $newJournal->id; + $newTransaction->reconciled = false; + $newTransaction->save(); } /** @@ -137,15 +136,16 @@ class GroupCloneService } /** - * @param Transaction $transaction - * @param TransactionJournal $newJournal + * @param TransactionJournalMeta $meta + * @param TransactionJournal $newJournal */ - private function cloneTransaction(Transaction $transaction, TransactionJournal $newJournal): void + private function cloneMeta(TransactionJournalMeta $meta, TransactionJournal $newJournal): void { - $newTransaction = $transaction->replicate(); - $newTransaction->transaction_journal_id = $newJournal->id; - $newTransaction->reconciled = false; - $newTransaction->save(); + $newMeta = $meta->replicate(); + $newMeta->transaction_journal_id = $newJournal->id; + if ('recurrence_id' !== $newMeta->name) { + $newMeta->save(); + } } diff --git a/app/Services/Internal/Update/GroupUpdateService.php b/app/Services/Internal/Update/GroupUpdateService.php index 13f0793c10..0c7bf27885 100644 --- a/app/Services/Internal/Update/GroupUpdateService.php +++ b/app/Services/Internal/Update/GroupUpdateService.php @@ -56,6 +56,13 @@ class GroupUpdateService $transactionGroup->title = $data['group_title']; $transactionGroup->save(); } + + if (0 === count($transactions)) { + Log::debug('No transactions submitted, do nothing.'); + + return $transactionGroup; + } + if (1 === count($transactions) && 1 === $transactionGroup->transactionJournals()->count()) { /** @var TransactionJournal $first */ $first = $transactionGroup->transactionJournals()->first(); @@ -89,37 +96,6 @@ class GroupUpdateService return $transactionGroup; } - /** - * @param TransactionGroup $transactionGroup - * @param array $data - * - * @throws FireflyException - */ - private function createTransactionJournal(TransactionGroup $transactionGroup, array $data): void - { - - $submission = [ - 'transactions' => [ - $data, - ], - ]; - /** @var TransactionJournalFactory $factory */ - $factory = app(TransactionJournalFactory::class); - $factory->setUser($transactionGroup->user); - try { - $collection = $factory->create($submission); - } catch (FireflyException $e) { - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); - throw new FireflyException(sprintf('Could not create new transaction journal: %s', $e->getMessage())); - } - $collection->each( - function (TransactionJournal $journal) use ($transactionGroup) { - $transactionGroup->transactionJournals()->save($journal); - } - ); - } - /** * Update single journal. * @@ -184,4 +160,35 @@ class GroupUpdateService return $updated; } + /** + * @param TransactionGroup $transactionGroup + * @param array $data + * + * @throws FireflyException + */ + private function createTransactionJournal(TransactionGroup $transactionGroup, array $data): void + { + + $submission = [ + 'transactions' => [ + $data, + ], + ]; + /** @var TransactionJournalFactory $factory */ + $factory = app(TransactionJournalFactory::class); + $factory->setUser($transactionGroup->user); + try { + $collection = $factory->create($submission); + } catch (FireflyException $e) { + Log::error($e->getMessage()); + Log::error($e->getTraceAsString()); + throw new FireflyException(sprintf('Could not create new transaction journal: %s', $e->getMessage())); + } + $collection->each( + function (TransactionJournal $journal) use ($transactionGroup) { + $transactionGroup->transactionJournals()->save($journal); + } + ); + } + } diff --git a/app/Services/Internal/Update/JournalUpdateService.php b/app/Services/Internal/Update/JournalUpdateService.php index d6e0efee8f..5394209ba8 100644 --- a/app/Services/Internal/Update/JournalUpdateService.php +++ b/app/Services/Internal/Update/JournalUpdateService.php @@ -54,28 +54,17 @@ class JournalUpdateService { use JournalServiceTrait; - /** @var BillRepositoryInterface */ - private $billRepository; - /** @var CurrencyRepositoryInterface */ - private $currencyRepository; - /** @var array The data to update the journal with. */ - private $data; - /** @var Account The destination account. */ - private $destinationAccount; - /** @var Transaction */ - private $destinationTransaction; - /** @var array All meta values that are dates. */ - private $metaDate; - /** @var array All meta values that are strings. */ - private $metaString; - /** @var Account Source account of the journal */ - private $sourceAccount; - /** @var Transaction Source transaction of the journal. */ - private $sourceTransaction; - /** @var TransactionGroup The parent group. */ - private $transactionGroup; - /** @var TransactionJournal The journal to update. */ - private $transactionJournal; + private BillRepositoryInterface $billRepository; + private CurrencyRepositoryInterface $currencyRepository; + private array $data; + private ?Account $destinationAccount; + private ?Transaction $destinationTransaction; + private array $metaDate; + private array $metaString; + private ?Account $sourceAccount; + private ?Transaction $sourceTransaction; + private TransactionGroup $transactionGroup; + private TransactionJournal $transactionJournal; /** * JournalUpdateService constructor. @@ -112,6 +101,10 @@ class JournalUpdateService $this->budgetRepository->setUser($transactionGroup->user); $this->tagFactory->setUser($transactionGroup->user); $this->accountRepository->setUser($transactionGroup->user); + $this->destinationAccount = null; + $this->destinationTransaction = null; + $this->sourceAccount = null; + $this->sourceTransaction = null; } /** @@ -130,7 +123,7 @@ class JournalUpdateService Log::debug(sprintf('Now in JournalUpdateService for journal #%d.', $this->transactionJournal->id)); // can we update account data using the new type? if ($this->hasValidAccounts()) { - Log::info('-- account info is valid, now update.'); + Log::info('Account info is valid, now update.'); // update accounts: $this->updateAccounts(); @@ -146,12 +139,14 @@ class JournalUpdateService $this->updateField('date'); $this->updateField('order'); + $this->transactionJournal->save(); $this->transactionJournal->refresh(); $this->updateCategory(); $this->updateBudget(); $this->updateTags(); + $this->updateReconciled(); $this->updateNotes(); $this->updateMeta(); $this->updateCurrency(); @@ -166,47 +161,61 @@ class JournalUpdateService } /** - * Get destination transaction. - * - * @return Transaction + * @return bool */ - private function getDestinationTransaction(): Transaction + private function hasValidAccounts(): bool { - if (null === $this->destinationTransaction) { - $this->destinationTransaction = $this->transactionJournal->transactions()->where('amount', '>', 0)->first(); - } - - return $this->destinationTransaction; + return $this->hasValidSourceAccount() && $this->hasValidDestinationAccount(); } /** - * This method returns the current or expected type of the journal (in case of a change) based on the data in the array. - * - * If the array contains key 'type' and the value is correct, this is returned. Otherwise, the original type is returned. - * - * @return string + * @return bool */ - private function getExpectedType(): string + private function hasValidSourceAccount(): bool { - Log::debug('Now in getExpectedType()'); - if ($this->hasFields(['type'])) { - return ucfirst('opening-balance' === $this->data['type'] ? 'opening balance' : $this->data['type']); + Log::debug('Now in hasValidSourceAccount().'); + $sourceId = $this->data['source_id'] ?? null; + $sourceName = $this->data['source_name'] ?? null; + + if (!$this->hasFields(['source_id', 'source_name'])) { + $origSourceAccount = $this->getOriginalSourceAccount(); + $sourceId = $origSourceAccount->id; + $sourceName = $origSourceAccount->name; } - return $this->transactionJournal->transactionType->type; + // make new account validator. + $expectedType = $this->getExpectedType(); + Log::debug(sprintf('Expected type (new or unchanged) is %s', $expectedType)); + + // make a new validator. + /** @var AccountValidator $validator */ + $validator = app(AccountValidator::class); + $validator->setTransactionType($expectedType); + $validator->setUser($this->transactionJournal->user); + + $result = $validator->validateSource($sourceId, $sourceName, null); + Log::debug(sprintf('hasValidSourceAccount(%d, "%s") will return %s', $sourceId, $sourceName, var_export($result, true))); + + // TODO typeOverrule: the account validator may have another opinion on the transaction type. + + // validate submitted info: + return $result; } /** - * @return Account + * @param array $fields + * + * @return bool */ - private function getOriginalDestinationAccount(): Account + private function hasFields(array $fields): bool { - if (null === $this->destinationAccount) { - $destination = $this->getSourceTransaction(); - $this->destinationAccount = $destination->account; + foreach ($fields as $field) { + if (array_key_exists($field, $this->data)) { + return true; + } } - return $this->destinationAccount; + return false; } /** @@ -236,37 +245,84 @@ class JournalUpdateService } /** - * Does a validation and returns the destination account. This method will break if the dest isn't really valid. + * This method returns the current or expected type of the journal (in case of a change) based on the data in the array. * - * @return Account + * If the array contains key 'type' and the value is correct, this is returned. Otherwise, the original type is returned. + * + * @return string */ - private function getValidDestinationAccount(): Account + private function getExpectedType(): string { - Log::debug('Now in getValidDestinationAccount().'); - - if (!$this->hasFields(['destination_id', 'destination_name'])) { - return $this->getOriginalDestinationAccount(); + Log::debug('Now in getExpectedType()'); + if ($this->hasFields(['type'])) { + return ucfirst('opening-balance' === $this->data['type'] ? 'opening balance' : $this->data['type']); } - $destInfo = [ - 'id' => (int)($this->data['destination_id'] ?? null), - 'name' => $this->data['destination_name'] ?? null, - 'iban' => $this->data['destination_iban'] ?? null, - 'number' => $this->data['destination_number'] ?? null, - 'bic' => $this->data['destination_bic'] ?? null, - ]; + return $this->transactionJournal->transactionType->type; + } + + /** + * @return bool + */ + private function hasValidDestinationAccount(): bool + { + Log::debug('Now in hasValidDestinationAccount().'); + $destId = $this->data['destination_id'] ?? null; + $destName = $this->data['destination_name'] ?? null; + + if (!$this->hasFields(['destination_id', 'destination_name'])) { + Log::debug('No destination info submitted, grab the original data.'); + $destination = $this->getOriginalDestinationAccount(); + $destId = $destination->id; + $destName = $destination->name; + } // make new account validator. $expectedType = $this->getExpectedType(); Log::debug(sprintf('Expected type (new or unchanged) is %s', $expectedType)); - try { - $result = $this->getAccount($expectedType, 'destination', $destInfo); - } catch (FireflyException $e) { - Log::error(sprintf('getValidDestinationAccount() threw unexpected error: %s', $e->getMessage())); - $result = $this->getOriginalDestinationAccount(); + + // make a new validator. + /** @var AccountValidator $validator */ + $validator = app(AccountValidator::class); + $validator->setTransactionType($expectedType); + $validator->setUser($this->transactionJournal->user); + $validator->source = $this->getValidSourceAccount(); + + + $result = $validator->validateDestination($destId, $destName, null); + Log::debug(sprintf('hasValidDestinationAccount(%d, "%s") will return %s', $destId, $destName, var_export($result, true))); + + // TODO typeOverrule: the account validator may have another opinion on the transaction type. + + // validate submitted info: + return $result; + } + + /** + * @return Account + */ + private function getOriginalDestinationAccount(): Account + { + if (null === $this->destinationAccount) { + $destination = $this->getDestinationTransaction(); + $this->destinationAccount = $destination->account; } - return $result; + return $this->destinationAccount; + } + + /** + * Get destination transaction. + * + * @return Transaction + */ + private function getDestinationTransaction(): Transaction + { + if (null === $this->destinationTransaction) { + $this->destinationTransaction = $this->transactionJournal->transactions()->where('amount', '>', 0)->first(); + } + + return $this->destinationTransaction; } /** @@ -304,100 +360,6 @@ class JournalUpdateService return $result; } - /** - * @param array $fields - * - * @return bool - */ - private function hasFields(array $fields): bool - { - foreach ($fields as $field) { - if (array_key_exists($field, $this->data)) { - return true; - } - } - - return false; - } - - /** - * @return bool - */ - private function hasValidAccounts(): bool - { - return $this->hasValidSourceAccount() && $this->hasValidDestinationAccount(); - } - - /** - * @return bool - */ - private function hasValidDestinationAccount(): bool - { - Log::debug('Now in hasValidDestinationAccount().'); - $destId = $this->data['destination_id'] ?? null; - $destName = $this->data['destination_name'] ?? null; - - if (!$this->hasFields(['destination_id', 'destination_name'])) { - $destination = $this->getOriginalDestinationAccount(); - $destId = $destination->id; - $destName = $destination->name; - } - - // make new account validator. - $expectedType = $this->getExpectedType(); - Log::debug(sprintf('Expected type (new or unchanged) is %s', $expectedType)); - - // make a new validator. - /** @var AccountValidator $validator */ - $validator = app(AccountValidator::class); - $validator->setTransactionType($expectedType); - $validator->setUser($this->transactionJournal->user); - $validator->source = $this->getValidSourceAccount(); - - - $result = $validator->validateDestination($destId, $destName, null); - Log::debug(sprintf('hasValidDestinationAccount(%d, "%s") will return %s', $destId, $destName, var_export($result, true))); - - // TODO typeOverrule: the account validator may have another opinion on the transaction type. - - // validate submitted info: - return $result; - } - - /** - * @return bool - */ - private function hasValidSourceAccount(): bool - { - Log::debug('Now in hasValidSourceAccount().'); - $sourceId = $this->data['source_id'] ?? null; - $sourceName = $this->data['source_name'] ?? null; - - if (!$this->hasFields(['source_id', 'source_name'])) { - $origSourceAccount = $this->getOriginalSourceAccount(); - $sourceId = $origSourceAccount->id; - $sourceName = $origSourceAccount->name; - } - - // make new account validator. - $expectedType = $this->getExpectedType(); - Log::debug(sprintf('Expected type (new or unchanged) is %s', $expectedType)); - - // make a new validator. - /** @var AccountValidator $validator */ - $validator = app(AccountValidator::class); - $validator->setTransactionType($expectedType); - $validator->setUser($this->transactionJournal->user); - - $result = $validator->validateSource($sourceId, $sourceName, null); - Log::debug(sprintf('hasValidSourceAccount(%d, "%s") will return %s', $sourceId, $sourceName, var_export($result, true))); - - // TODO typeOverrule: the account validator may have another opinion on the transaction type. - - // validate submitted info: - return $result; - } - /** * Will update the source and destination accounts of this journal. Assumes they are valid. */ @@ -431,36 +393,68 @@ class JournalUpdateService } /** + * Does a validation and returns the destination account. This method will break if the dest isn't really valid. * + * @return Account */ - private function updateAmount(): void + private function getValidDestinationAccount(): Account { - if (!$this->hasFields(['amount'])) { - return; + Log::debug('Now in getValidDestinationAccount().'); + + if (!$this->hasFields(['destination_id', 'destination_name'])) { + return $this->getOriginalDestinationAccount(); } - $value = $this->data['amount'] ?? ''; + $destInfo = [ + 'id' => (int)($this->data['destination_id'] ?? null), + 'name' => $this->data['destination_name'] ?? null, + 'iban' => $this->data['destination_iban'] ?? null, + 'number' => $this->data['destination_number'] ?? null, + 'bic' => $this->data['destination_bic'] ?? null, + ]; + + // make new account validator. + $expectedType = $this->getExpectedType(); + Log::debug(sprintf('Expected type (new or unchanged) is %s', $expectedType)); try { - $amount = $this->getAmount($value); + $result = $this->getAccount($expectedType, 'destination', $destInfo); } catch (FireflyException $e) { - Log::debug(sprintf('getAmount("%s") returns error: %s', $value, $e->getMessage())); + Log::error(sprintf('getValidDestinationAccount() threw unexpected error: %s', $e->getMessage())); + $result = $this->getOriginalDestinationAccount(); + } + + return $result; + } + + /** + * Updates journal transaction type. + */ + private function updateType(): void + { + Log::debug('Now in updateType()'); + if ($this->hasFields(['type'])) { + $type = 'opening-balance' === $this->data['type'] ? 'opening balance' : $this->data['type']; + Log::debug( + sprintf( + 'Trying to change journal #%d from a %s to a %s.', + $this->transactionJournal->id, $this->transactionJournal->transactionType->type, $type + ) + ); + + /** @var TransactionTypeFactory $typeFactory */ + $typeFactory = app(TransactionTypeFactory::class); + $result = $typeFactory->find($this->data['type']); + if (null !== $result) { + Log::debug('Changed transaction type!'); + $this->transactionJournal->transaction_type_id = $result->id; + $this->transactionJournal->save(); + + return; + } return; } - $origSourceTransaction = $this->getSourceTransaction(); - $origSourceTransaction->amount = app('steam')->negative($amount); - $origSourceTransaction->save(); - - - $destTransaction = $this->getDestinationTransaction(); - $destTransaction->amount = app('steam')->positive($amount); - $destTransaction->save(); - - - // refresh transactions. - $this->sourceTransaction->refresh(); - $this->destinationTransaction->refresh(); - Log::debug(sprintf('Updated amount to "%s"', $amount)); + Log::debug('No type field present.'); } /** @@ -483,6 +477,47 @@ class JournalUpdateService } } + /** + * Update journal generic field. Cannot be set to NULL. + * + * @param string $fieldName + */ + private function updateField(string $fieldName): void + { + if (array_key_exists($fieldName, $this->data) && '' !== (string)$this->data[$fieldName]) { + $value = $this->data[$fieldName]; + + if ('date' === $fieldName) { + if ($value instanceof Carbon) { + // update timezone. + $value->setTimezone(config('app.timezone')); + } + if (!($value instanceof Carbon)) { + $value = new Carbon($value); + } + // do some parsing. + Log::debug(sprintf('Create date value from string "%s".', $value)); + } + + + $this->transactionJournal->$fieldName = $value; + Log::debug(sprintf('Updated %s', $fieldName)); + } + } + + /** + * + */ + private function updateCategory(): void + { + // update category + if ($this->hasFields(['category_id', 'category_name'])) { + Log::debug('Will update category.'); + + $this->storeCategory($this->transactionJournal, new NullArrayObject($this->data)); + } + } + /** * */ @@ -498,13 +533,103 @@ class JournalUpdateService /** * */ - private function updateCategory(): void + private function updateTags(): void { - // update category - if ($this->hasFields(['category_id', 'category_name'])) { - Log::debug('Will update category.'); + if ($this->hasFields(['tags'])) { + Log::debug('Will update tags.'); + $tags = $this->data['tags'] ?? null; + $this->storeTags($this->transactionJournal, $tags); + } + } - $this->storeCategory($this->transactionJournal, new NullArrayObject($this->data)); + /** + * + */ + private function updateReconciled(): void + { + if (array_key_exists('reconciled', $this->data) && is_bool($this->data['reconciled'])) { + $this->transactionJournal->transactions()->update(['reconciled' => $this->data['reconciled']]); + } + } + + /** + * + */ + private function updateNotes(): void + { + // update notes. + if ($this->hasFields(['notes'])) { + $notes = '' === (string)$this->data['notes'] ? null : $this->data['notes']; + $this->storeNotes($this->transactionJournal, $notes); + } + } + + /** + * + */ + private function updateMeta(): void + { + // update meta fields. + // first string + if ($this->hasFields($this->metaString)) { + Log::debug('Meta string fields are present.'); + $this->updateMetaFields(); + } + + // then date fields. + if ($this->hasFields($this->metaDate)) { + Log::debug('Meta date fields are present.'); + $this->updateMetaDateFields(); + } + } + + /** + * + */ + private function updateMetaFields(): void + { + /** @var TransactionJournalMetaFactory $factory */ + $factory = app(TransactionJournalMetaFactory::class); + + foreach ($this->metaString as $field) { + if ($this->hasFields([$field])) { + $value = '' === $this->data[$field] ? null : $this->data[$field]; + Log::debug(sprintf('Field "%s" is present ("%s"), try to update it.', $field, $value)); + $set = [ + 'journal' => $this->transactionJournal, + 'name' => $field, + 'data' => $value, + ]; + $factory->updateOrCreate($set); + } + } + } + + /** + * + */ + private function updateMetaDateFields(): void + { + /** @var TransactionJournalMetaFactory $factory */ + $factory = app(TransactionJournalMetaFactory::class); + + foreach ($this->metaDate as $field) { + if ($this->hasFields([$field])) { + try { + $value = '' === (string)$this->data[$field] ? null : new Carbon($this->data[$field]); + } catch (Exception $e) { + Log::debug(sprintf('%s is not a valid date value: %s', $this->data[$field], $e->getMessage())); + + return; + } + Log::debug(sprintf('Field "%s" is present ("%s"), try to update it.', $field, $value)); + $set = [ + 'journal' => $this->transactionJournal, + 'name' => $field, + 'data' => $value, + ]; + $factory->updateOrCreate($set); + } } } @@ -541,33 +666,37 @@ class JournalUpdateService } /** - * Update journal generic field. Cannot be set to NULL. * - * @param string $fieldName */ - private function updateField(string $fieldName): void + private function updateAmount(): void { - if (array_key_exists($fieldName, $this->data) && '' !== (string)$this->data[$fieldName]) { - $value = $this->data[$fieldName]; - - if ('date' === $fieldName) { - if ($value instanceof Carbon) { - // update timezone. - $value->setTimezone(config('app.timezone')); - } - if (!($value instanceof Carbon)) { - $value = new Carbon($value); - } - // do some parsing. - Log::debug(sprintf('Create date value from string "%s".', $value)); - } - - - $this->transactionJournal->$fieldName = $value; - Log::debug(sprintf('Updated %s', $fieldName)); + if (!$this->hasFields(['amount'])) { + return; } - } + $value = $this->data['amount'] ?? ''; + try { + $amount = $this->getAmount($value); + } catch (FireflyException $e) { + Log::debug(sprintf('getAmount("%s") returns error: %s', $value, $e->getMessage())); + + return; + } + $origSourceTransaction = $this->getSourceTransaction(); + $origSourceTransaction->amount = app('steam')->negative($amount); + $origSourceTransaction->save(); + + + $destTransaction = $this->getDestinationTransaction(); + $destTransaction->amount = app('steam')->positive($amount); + $destTransaction->save(); + + + // refresh transactions. + $this->sourceTransaction->refresh(); + $this->destinationTransaction->refresh(); + Log::debug(sprintf('Updated amount to "%s"', $amount)); + } /** * @@ -632,128 +761,4 @@ class JournalUpdateService $this->sourceTransaction->refresh(); $this->destinationTransaction->refresh(); } - - /** - * - */ - private function updateMeta(): void - { - // update meta fields. - // first string - if ($this->hasFields($this->metaString)) { - Log::debug('Meta string fields are present.'); - $this->updateMetaFields(); - } - - // then date fields. - if ($this->hasFields($this->metaDate)) { - Log::debug('Meta date fields are present.'); - $this->updateMetaDateFields(); - } - } - - /** - * - */ - private function updateMetaDateFields(): void - { - /** @var TransactionJournalMetaFactory $factory */ - $factory = app(TransactionJournalMetaFactory::class); - - foreach ($this->metaDate as $field) { - if ($this->hasFields([$field])) { - try { - $value = '' === (string)$this->data[$field] ? null : new Carbon($this->data[$field]); - } catch (Exception $e) { - Log::debug(sprintf('%s is not a valid date value: %s', $this->data[$field], $e->getMessage())); - - return; - } - Log::debug(sprintf('Field "%s" is present ("%s"), try to update it.', $field, $value)); - $set = [ - 'journal' => $this->transactionJournal, - 'name' => $field, - 'data' => $value, - ]; - $factory->updateOrCreate($set); - } - } - } - - /** - * - */ - private function updateMetaFields(): void - { - /** @var TransactionJournalMetaFactory $factory */ - $factory = app(TransactionJournalMetaFactory::class); - - foreach ($this->metaString as $field) { - if ($this->hasFields([$field])) { - $value = '' === $this->data[$field] ? null : $this->data[$field]; - Log::debug(sprintf('Field "%s" is present ("%s"), try to update it.', $field, $value)); - $set = [ - 'journal' => $this->transactionJournal, - 'name' => $field, - 'data' => $value, - ]; - $factory->updateOrCreate($set); - } - } - } - - /** - * - */ - private function updateNotes(): void - { - // update notes. - if ($this->hasFields(['notes'])) { - $notes = '' === (string)$this->data['notes'] ? null : $this->data['notes']; - $this->storeNotes($this->transactionJournal, $notes); - } - } - - /** - * - */ - private function updateTags(): void - { - if ($this->hasFields(['tags'])) { - Log::debug('Will update tags.'); - $tags = $this->data['tags'] ?? null; - $this->storeTags($this->transactionJournal, $tags); - } - } - - /** - * Updates journal transaction type. - */ - private function updateType(): void - { - Log::debug('Now in updateType()'); - if ($this->hasFields(['type'])) { - $type = 'opening-balance' === $this->data['type'] ? 'opening balance' : $this->data['type']; - Log::debug( - sprintf( - 'Trying to change journal #%d from a %s to a %s.', - $this->transactionJournal->id, $this->transactionJournal->transactionType->type, $type - ) - ); - - /** @var TransactionTypeFactory $typeFactory */ - $typeFactory = app(TransactionTypeFactory::class); - $result = $typeFactory->find($this->data['type']); - if (null !== $result) { - Log::debug('Changed transaction type!'); - $this->transactionJournal->transaction_type_id = $result->id; - $this->transactionJournal->save(); - - return; - } - - return; - } - Log::debug('No type field present.'); - } } diff --git a/app/Services/Internal/Update/RecurrenceUpdateService.php b/app/Services/Internal/Update/RecurrenceUpdateService.php index 775bb79c7d..71332053f1 100644 --- a/app/Services/Internal/Update/RecurrenceUpdateService.php +++ b/app/Services/Internal/Update/RecurrenceUpdateService.php @@ -27,6 +27,7 @@ use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Note; use FireflyIII\Models\Recurrence; +use FireflyIII\Models\RecurrenceRepetition; use FireflyIII\Services\Internal\Support\RecurringTransactionTrait; use FireflyIII\Services\Internal\Support\TransactionTypeTrait; use FireflyIII\User; @@ -56,53 +57,57 @@ class RecurrenceUpdateService */ public function update(Recurrence $recurrence, array $data): Recurrence { - $this->user = $recurrence->user; - $transactionType = $recurrence->transactionType; - if (isset($data['recurrence']['type'])) { - $transactionType = $this->findTransactionType(ucfirst($data['recurrence']['type'])); - } + $this->user = $recurrence->user; // update basic fields first: - $recurrence->transaction_type_id = $transactionType->id; - $recurrence->title = $data['recurrence']['title'] ?? $recurrence->title; - $recurrence->description = $data['recurrence']['description'] ?? $recurrence->description; - $recurrence->first_date = $data['recurrence']['first_date'] ?? $recurrence->first_date; - $recurrence->repeat_until = $data['recurrence']['repeat_until'] ?? $recurrence->repeat_until; - $recurrence->repetitions = $data['recurrence']['nr_of_repetitions'] ?? $recurrence->repetitions; - $recurrence->apply_rules = $data['recurrence']['apply_rules'] ?? $recurrence->apply_rules; - $recurrence->active = $data['recurrence']['active'] ?? $recurrence->active; - // if nr_of_repetitions is set, then drop the "repeat_until" field. - if (0 !== $recurrence->repetitions) { - $recurrence->repeat_until = null; - } - - if (isset($data['recurrence']['repetition_end'])) { - if (in_array($data['recurrence']['repetition_end'], ['forever', 'until_date'])) { - $recurrence->repetitions = 0; + if (array_key_exists('recurrence', $data)) { + $info = $data['recurrence']; + if (array_key_exists('title', $info)) { + $recurrence->title = $info['title']; } - if (in_array($data['recurrence']['repetition_end'], ['forever', 'times'])) { - $recurrence->repeat_until = null; + if (array_key_exists('description', $info)) { + $recurrence->description = $info['description']; + } + if (array_key_exists('first_date', $info)) { + $recurrence->first_date = $info['first_date']; + } + if (array_key_exists('repeat_until', $info)) { + $recurrence->repeat_until = $info['repeat_until']; + $recurrence->repetitions = 0; + } + if (array_key_exists('nr_of_repetitions', $info)) { + if (0 !== (int)$info['nr_of_repetitions']) { + $recurrence->repeat_until = null; + } + $recurrence->repetitions = $info['nr_of_repetitions']; + } + if (array_key_exists('apply_rules', $info)) { + $recurrence->apply_rules = $info['apply_rules']; + } + if (array_key_exists('active', $info)) { + $recurrence->active = $info['active']; + } + // update all meta data: + if (array_key_exists('notes', $info)) { + $this->setNoteText($recurrence, $info['notes']); } } $recurrence->save(); - // update all meta data: - - if (isset($data['recurrence']['notes']) && null !== $data['recurrence']['notes']) { - $this->setNoteText($recurrence, $data['recurrence']['notes']); - } - // update all repetitions - if (null !== $data['repetitions']) { - $this->deleteRepetitions($recurrence); - $this->createRepetitions($recurrence, $data['repetitions'] ?? []); + if (array_key_exists('repetitions', $data)) { + Log::debug('Will update repetitions array'); + // update each repetition or throw error yay + $this->updateRepetitions($recurrence, $data['repetitions'] ?? []); } + // update all transactions: - // update all transactions (and associated meta-data) - if (null !== $data['transactions']) { - $this->deleteTransactions($recurrence); - $this->createTransactions($recurrence, $data['transactions'] ?? []); - } + +// // update all transactions (and associated meta-data) +// if (array_key_exists('transactions', $data)) { +// $this->deleteTransactions($recurrence); +// $this->createTransactions($recurrence, $data['transactions'] ?? []); +// } return $recurrence; } @@ -133,4 +138,77 @@ class RecurrenceUpdateService } } + + /** + * + * @param Recurrence $recurrence + * @param array $repetitions + */ + private function updateRepetitions(Recurrence $recurrence, array $repetitions): void + { + $originalCount = $recurrence->recurrenceRepetitions()->count(); + if (0 === count($repetitions)) { + // wont drop repetition, rather avoid. + return; + } + // user added or removed repetitions, delete all and recreate: + if ($originalCount !== count($repetitions)) { + Log::debug('Del + recreate'); + $this->deleteRepetitions($recurrence); + $this->createRepetitions($recurrence, $repetitions); + + return; + } + // loop all and try to match them: + if ($originalCount === count($repetitions)) { + Log::debug('Loop and find'); + foreach ($repetitions as $current) { + $match = $this->matchRepetition($recurrence, $current); + if (null === $match) { + throw new FireflyException('Cannot match recurring repetition to existing repetition. Not sure what to do. Break.'); + } + $fields = [ + 'type' => 'repetition_type', + 'moment' => 'repetition_moment', + 'skip' => 'repetition_skip', + 'weekend' => 'weekend', + ]; + foreach ($fields as $field => $column) { + if (array_key_exists($field, $current)) { + $match->$column = $current[$field]; + $match->save(); + } + } + } + } + } + + /** + * @param array $data + * + * @return RecurrenceRepetition|null + */ + private function matchRepetition(Recurrence $recurrence, array $data): ?RecurrenceRepetition + { + $originalCount = $recurrence->recurrenceRepetitions()->count(); + if (1 === $originalCount) { + Log::debug('Return the first one'); + return $recurrence->recurrenceRepetitions()->first(); + } + // find it: + $fields = ['id' => 'id', + 'type' => 'repetition_type', + 'moment' => 'repetition_moment', + 'skip' => 'repetition_skip', + 'weekend' => 'weekend', + ]; + $query = $recurrence->recurrenceRepetitions(); + foreach ($fields as $field => $column) { + if (array_key_exists($field, $data)) { + $query->where($column, $data[$field]); + } + } + + return $query->first(); + } } diff --git a/app/Services/Password/PwndVerifierV2.php b/app/Services/Password/PwndVerifierV2.php index a5588f0b5b..5afdb5e27c 100644 --- a/app/Services/Password/PwndVerifierV2.php +++ b/app/Services/Password/PwndVerifierV2.php @@ -34,16 +34,6 @@ use RuntimeException; */ class PwndVerifierV2 implements Verifier { - /** - * Constructor. - */ - public function __construct() - { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); - } - } - /** * Verify the given password against (some) service. * diff --git a/app/Services/Webhook/StandardWebhookSender.php b/app/Services/Webhook/StandardWebhookSender.php index 544942b364..9f3e720050 100644 --- a/app/Services/Webhook/StandardWebhookSender.php +++ b/app/Services/Webhook/StandardWebhookSender.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * StandardWebhookSender.php @@ -102,10 +123,19 @@ class StandardWebhookSender implements WebhookSenderInterface } catch (ClientException | Exception $e) { Log::error($e->getMessage()); Log::error($e->getTraceAsString()); - //$logs[] = sprintf('%s: %s', date('Y-m-d H:i:s'), $e->getMessage()); + + $logs = sprintf("%s\n%s", $e->getMessage(), $e->getTraceAsString()); + $this->message->errored = true; $this->message->sent = false; $this->message->save(); + + $attempt = new WebhookAttempt; + $attempt->webhookMessage()->associate($this->message); + $attempt->status_code = $e->getResponse() ? $e->getResponse()->getStatusCode() : 0; + $attempt->logs = $logs; + $attempt->save(); + return; } $this->message->save(); diff --git a/app/Services/Webhook/WebhookSenderInterface.php b/app/Services/Webhook/WebhookSenderInterface.php index a5e03c302c..381dd23904 100644 --- a/app/Services/Webhook/WebhookSenderInterface.php +++ b/app/Services/Webhook/WebhookSenderInterface.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * WebhookSenderInterface.php diff --git a/app/Support/Amount.php b/app/Support/Amount.php index ede6ff8dc8..f7b73cd0a2 100644 --- a/app/Support/Amount.php +++ b/app/Support/Amount.php @@ -223,10 +223,6 @@ class Amount */ public function getAllCurrencies(): Collection { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should NOT be called in the TEST environment!', __METHOD__)); - } - return TransactionCurrency::orderBy('code', 'ASC')->get(); } @@ -235,10 +231,6 @@ class Amount */ public function getCurrencies(): Collection { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should NOT be called in the TEST environment!', __METHOD__)); - } - return TransactionCurrency::where('enabled', true)->orderBy('code', 'ASC')->get(); } @@ -247,9 +239,6 @@ class Amount */ public function getCurrencyCode(): string { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should NOT be called in the TEST environment!', __METHOD__)); - } $cache = new CacheProperties; $cache->addProperty('getCurrencyCode'); if ($cache->has()) { @@ -273,9 +262,6 @@ class Amount */ public function getDefaultCurrency(): TransactionCurrency { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should NOT be called in the TEST environment!', __METHOD__)); - } /** @var User $user */ $user = auth()->user(); @@ -287,10 +273,6 @@ class Amount */ public function getSystemCurrency(): TransactionCurrency { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should NOT be called in the TEST environment!', __METHOD__)); - } - return TransactionCurrency::where('code', 'EUR')->first(); } diff --git a/app/Support/Binder/ConfigurationName.php b/app/Support/Binder/DynamicConfigKey.php similarity index 76% rename from app/Support/Binder/ConfigurationName.php rename to app/Support/Binder/DynamicConfigKey.php index 379c50c6e3..d753f0c166 100644 --- a/app/Support/Binder/ConfigurationName.php +++ b/app/Support/Binder/DynamicConfigKey.php @@ -1,7 +1,7 @@ . */ -declare(strict_types=1); - namespace FireflyIII\Support\Binder; - use Illuminate\Routing\Route; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** - * Class ConfigurationName + * Class DynamicConfigKey */ -class ConfigurationName implements BinderInterface +class DynamicConfigKey { + public static array $accepted = [ + 'configuration.is_demo_site', + 'configuration.permission_update_check', + 'configuration.single_user_mode', + 'configuration.last_update_check', + ]; /** * @param string $value * @param Route $route @@ -42,10 +45,10 @@ class ConfigurationName implements BinderInterface */ public static function routeBinder(string $value, Route $route): string { - $accepted = ['is_demo_site', 'permission_update_check', 'single_user_mode']; - if (in_array($value, $accepted, true)) { + if (in_array($value, self::$accepted, true)) { return $value; } throw new NotFoundHttpException; } -} + +} \ No newline at end of file diff --git a/app/Support/Binder/EitherConfigKey.php b/app/Support/Binder/EitherConfigKey.php new file mode 100644 index 0000000000..33c1c9d715 --- /dev/null +++ b/app/Support/Binder/EitherConfigKey.php @@ -0,0 +1,54 @@ +. + */ + +namespace FireflyIII\Support\Binder; + + +use Illuminate\Routing\Route; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; + +/** + * Class EitherConfigKey + */ +class EitherConfigKey +{ + public static array $static = [ + 'firefly.version', + 'firefly.api_version', + 'firefly.default_location', + 'firefly.account_to_transaction', + 'firefly.allowed_opposing_types', + ]; + /** + * @param string $value + * @param Route $route + * + * @return string + * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException + */ + public static function routeBinder(string $value, Route $route): string + { + if (in_array($value, self::$static, true) || in_array($value, DynamicConfigKey::$accepted, true)) { + return $value; + } + throw new NotFoundHttpException; + } +} \ No newline at end of file diff --git a/app/Support/Chart/Category/WholePeriodChartGenerator.php b/app/Support/Chart/Category/WholePeriodChartGenerator.php index c6c7c45bd4..e957d83384 100644 --- a/app/Support/Chart/Category/WholePeriodChartGenerator.php +++ b/app/Support/Chart/Category/WholePeriodChartGenerator.php @@ -37,16 +37,6 @@ use Log; */ class WholePeriodChartGenerator { - /** - * Constructor. - */ - public function __construct() - { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); - } - } - /** * @param Category $category * @param Carbon $start diff --git a/app/Support/ExpandedForm.php b/app/Support/ExpandedForm.php index dacf236793..7a48c38b11 100644 --- a/app/Support/ExpandedForm.php +++ b/app/Support/ExpandedForm.php @@ -62,9 +62,9 @@ class ExpandedForm $value = round((float) $value, 8); } try { - $html = view('form.amount-no-currency', compact('classes', 'name', 'label', 'value', 'options'))->render(); + $html = prefixView('form.amount-no-currency', compact('classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { - Log::debug(sprintf('Could not render amountNoCurrency(): %s', $e->getMessage())); + Log::error(sprintf('Could not render amountNoCurrency(): %s', $e->getMessage())); $html = 'Could not render amountNoCurrency.'; } @@ -98,7 +98,7 @@ class ExpandedForm unset($options['placeholder'], $options['autocomplete'], $options['class']); try { - $html = view('form.checkbox', compact('classes', 'name', 'label', 'value', 'options'))->render(); + $html = prefixView('form.checkbox', compact('classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { Log::debug(sprintf('Could not render checkbox(): %s', $e->getMessage())); $html = 'Could not render checkbox.'; @@ -123,7 +123,7 @@ class ExpandedForm $value = $this->fillFieldValue($name, $value); unset($options['placeholder']); try { - $html = view('form.date', compact('classes', 'name', 'label', 'value', 'options'))->render(); + $html = prefixView('form.date', compact('classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { Log::debug(sprintf('Could not render date(): %s', $e->getMessage())); $html = 'Could not render date.'; @@ -146,7 +146,7 @@ class ExpandedForm $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); try { - $html = view('form.file', compact('classes', 'name', 'label', 'options'))->render(); + $html = prefixView('form.file', compact('classes', 'name', 'label', 'options'))->render(); } catch (Throwable $e) { Log::debug(sprintf('Could not render file(): %s', $e->getMessage())); $html = 'Could not render file.'; @@ -172,7 +172,7 @@ class ExpandedForm $value = $this->fillFieldValue($name, $value); $options['step'] = '1'; try { - $html = view('form.integer', compact('classes', 'name', 'label', 'value', 'options'))->render(); + $html = prefixView('form.integer', compact('classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { Log::debug(sprintf('Could not render integer(): %s', $e->getMessage())); $html = 'Could not render integer.'; @@ -197,7 +197,7 @@ class ExpandedForm $classes = $this->getHolderClasses($name); $value = $this->fillFieldValue($name, $value); try { - $html = view('form.location', compact('classes', 'name', 'label', 'value', 'options'))->render(); + $html = prefixView('form.location', compact('classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { Log::debug(sprintf('Could not render location(): %s', $e->getMessage())); $html = 'Could not render location.'; @@ -255,7 +255,7 @@ class ExpandedForm $value = round((float) $value, $selectedCurrency->decimal_places); } try { - $html = view('form.non-selectable-amount', compact('selectedCurrency', 'classes', 'name', 'label', 'value', 'options'))->render(); + $html = prefixView('form.non-selectable-amount', compact('selectedCurrency', 'classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { Log::debug(sprintf('Could not render nonSelectableAmount(): %s', $e->getMessage())); $html = 'Could not render nonSelectableAmount.'; @@ -281,7 +281,7 @@ class ExpandedForm $options['step'] = 'any'; unset($options['placeholder']); try { - $html = view('form.number', compact('classes', 'name', 'label', 'value', 'options'))->render(); + $html = prefixView('form.number', compact('classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { Log::debug(sprintf('Could not render number(): %s', $e->getMessage())); $html = 'Could not render number.'; @@ -300,7 +300,7 @@ class ExpandedForm public function optionsList(string $type, string $name): string { try { - $html = view('form.options', compact('type', 'name'))->render(); + $html = prefixView('form.options', compact('type', 'name'))->render(); } catch (Throwable $e) { Log::debug(sprintf('Could not render select(): %s', $e->getMessage())); $html = 'Could not render optionsList.'; @@ -323,7 +323,7 @@ class ExpandedForm $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); try { - $html = view('form.password', compact('classes', 'name', 'label', 'options'))->render(); + $html = prefixView('form.password', compact('classes', 'name', 'label', 'options'))->render(); } catch (Throwable $e) { Log::debug(sprintf('Could not render password(): %s', $e->getMessage())); $html = 'Could not render password.'; @@ -351,7 +351,7 @@ class ExpandedForm $options['step'] = 'any'; unset($options['placeholder']); try { - $html = view('form.percentage', compact('classes', 'name', 'label', 'value', 'options'))->render(); + $html = prefixView('form.percentage', compact('classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { Log::debug(sprintf('Could not render percentage(): %s', $e->getMessage())); $html = 'Could not render percentage.'; @@ -374,7 +374,7 @@ class ExpandedForm $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); try { - $html = view('form.static', compact('classes', 'name', 'label', 'value', 'options'))->render(); + $html = prefixView('form.static', compact('classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { Log::debug(sprintf('Could not render staticText(): %s', $e->getMessage())); $html = 'Could not render staticText.'; @@ -398,7 +398,7 @@ class ExpandedForm $classes = $this->getHolderClasses($name); $value = $this->fillFieldValue($name, $value); try { - $html = view('form.text', compact('classes', 'name', 'label', 'value', 'options'))->render(); + $html = prefixView('form.text', compact('classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { Log::debug(sprintf('Could not render text(): %s', $e->getMessage())); $html = 'Could not render text.'; @@ -428,7 +428,7 @@ class ExpandedForm } try { - $html = view('form.textarea', compact('classes', 'name', 'label', 'value', 'options'))->render(); + $html = prefixView('form.textarea', compact('classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { Log::debug(sprintf('Could not render textarea(): %s', $e->getMessage())); $html = 'Could not render textarea.'; @@ -457,7 +457,7 @@ class ExpandedForm } try { - $html = view('form.object_group', compact('classes', 'name', 'label', 'value', 'options'))->render(); + $html = prefixView('form.object_group', compact('classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { Log::debug(sprintf('Could not render objectGroup(): %s', $e->getMessage())); $html = 'Could not render objectGroup.'; diff --git a/app/Support/Export/ExportDataGenerator.php b/app/Support/Export/ExportDataGenerator.php index 0fec36eaab..e54ce6c6d8 100644 --- a/app/Support/Export/ExportDataGenerator.php +++ b/app/Support/Export/ExportDataGenerator.php @@ -50,6 +50,7 @@ use FireflyIII\Repositories\Rule\RuleRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface; use FireflyIII\User; +use Illuminate\Support\Collection; use League\Csv\Writer; /** @@ -57,35 +58,24 @@ use League\Csv\Writer; */ class ExportDataGenerator { - /** @var Carbon */ - private $end; - /** @var bool */ - private $exportTransactions; - /** @var Carbon */ - private $start; - /** @var bool */ - private $exportAccounts; - /** @var bool */ - private $exportBudgets; - /** @var bool */ - private $exportCategories; - /** @var bool */ - private $exportTags; - /** @var bool */ - private $exportRecurring; - /** @var bool */ - private $exportRules; - /** @var bool */ - private $exportBills; - /** @var bool */ - private $exportPiggies; - - /** @var User */ - private $user; + private Carbon $end; + private bool $exportTransactions; + private Carbon $start; + private bool $exportAccounts; + private bool $exportBudgets; + private bool $exportCategories; + private bool $exportTags; + private bool $exportRecurring; + private bool $exportRules; + private bool $exportBills; + private bool $exportPiggies; + private User $user; + private Collection $accounts; public function __construct() { - $this->start = today(config('app.timezone')); + $this->accounts = new Collection; + $this->start = today(config('app.timezone')); $this->start->subYear(); $this->end = today(config('app.timezone')); $this->exportTransactions = false; @@ -107,6 +97,14 @@ class ExportDataGenerator $this->user = $user; } + /** + * @param Collection $accounts + */ + public function setAccounts(Collection $accounts): void + { + $this->accounts = $accounts; + } + /** * @return array * @throws \League\Csv\CannotInsertRecord @@ -506,7 +504,7 @@ class ExportDataGenerator $currency ? $currency->code : null, $piggy->targetamount, $repetition ? $repetition->currentamount : null, - $piggy->startdate->format('Y-m-d'), + $piggy->startdate ? $piggy->startdate->format('Y-m-d') : null, $piggy->targetdate ? $piggy->targetdate->format('Y-m-d') : null, $piggy->order, $piggy->active, @@ -670,6 +668,10 @@ class ExportDataGenerator $collector->setUser($this->user); $collector->setRange($this->start, $this->end)->withAccountInformation()->withCategoryInformation()->withBillInformation() ->withBudgetInformation()->withTagInformation()->withNotes(); + if(0 !== $this->accounts->count()) { + $collector->setAccounts($this->accounts); + } + $journals = $collector->getExtractedJournals(); // get repository for meta data: diff --git a/app/Support/FireflyConfig.php b/app/Support/FireflyConfig.php index 8711785f12..97504e757a 100644 --- a/app/Support/FireflyConfig.php +++ b/app/Support/FireflyConfig.php @@ -41,9 +41,6 @@ class FireflyConfig */ public function delete(string $name): void { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s("%s") should NOT be called in the TEST environment!', __METHOD__, $name)); - } $fullName = 'ff-config-' . $name; if (Cache::has($fullName)) { Cache::forget($fullName); @@ -75,9 +72,6 @@ class FireflyConfig */ public function get(string $name, $default = null): ?Configuration { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s("%s") should NOT be called in the TEST environment!', __METHOD__, $name)); - } $fullName = 'ff-config-' . $name; if (Cache::has($fullName)) { return Cache::get($fullName); @@ -111,9 +105,7 @@ class FireflyConfig */ public function getFresh(string $name, $default = null): ?Configuration { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should NOT be called in the TEST environment!', __METHOD__)); - } + $config = Configuration::where('name', $name)->first(['id', 'name', 'data']); if ($config) { @@ -135,9 +127,6 @@ class FireflyConfig */ public function put(string $name, $value): Configuration { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should NOT be called in the TEST environment!', __METHOD__)); - } return $this->set($name, $value); } @@ -151,9 +140,6 @@ class FireflyConfig */ public function set(string $name, $value): Configuration { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should NOT be called in the TEST environment!', __METHOD__)); - } /** @var Configuration $config */ try { $config = Configuration::whereName($name)->first(); diff --git a/app/Support/Form/AccountForm.php b/app/Support/Form/AccountForm.php index 314e396472..edf9cea406 100644 --- a/app/Support/Form/AccountForm.php +++ b/app/Support/Form/AccountForm.php @@ -139,7 +139,7 @@ class AccountForm unset($options['class']); try { - $html = view('form.assetAccountCheckList', compact('classes', 'selected', 'name', 'label', 'options', 'grouped'))->render(); + $html = prefixView('form.assetAccountCheckList', compact('classes', 'selected', 'name', 'label', 'options', 'grouped'))->render(); } catch (Throwable $e) { Log::debug(sprintf('Could not render assetAccountCheckList(): %s', $e->getMessage())); $html = 'Could not render assetAccountCheckList.'; diff --git a/app/Support/Form/CurrencyForm.php b/app/Support/Form/CurrencyForm.php index f6faa0b0ab..c75f270579 100644 --- a/app/Support/Form/CurrencyForm.php +++ b/app/Support/Form/CurrencyForm.php @@ -154,7 +154,7 @@ class CurrencyForm $value = round((float) $value, $defaultCurrency->decimal_places); } try { - $html = view('form.' . $view, compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); + $html = prefixView('form.' . $view, compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { Log::debug(sprintf('Could not render currencyField(): %s', $e->getMessage())); $html = 'Could not render currencyField.'; @@ -205,7 +205,7 @@ class CurrencyForm $value = round((float) $value, $defaultCurrency->decimal_places); } try { - $html = view('form.' . $view, compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); + $html = prefixView('form.' . $view, compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); } catch (Throwable $e) { Log::debug(sprintf('Could not render currencyField(): %s', $e->getMessage())); $html = 'Could not render currencyField.'; diff --git a/app/Support/Form/FormSupport.php b/app/Support/Form/FormSupport.php index b0c0784080..00e09cd728 100644 --- a/app/Support/Form/FormSupport.php +++ b/app/Support/Form/FormSupport.php @@ -79,7 +79,7 @@ trait FormSupport $selected = $this->fillFieldValue($name, $selected); unset($options['autocomplete'], $options['placeholder']); try { - $html = view('form.select', compact('classes', 'name', 'label', 'selected', 'options', 'list'))->render(); + $html = prefixView('form.select', compact('classes', 'name', 'label', 'selected', 'options', 'list'))->render(); } catch (Throwable $e) { Log::debug(sprintf('Could not render select(): %s', $e->getMessage())); $html = 'Could not render select.'; diff --git a/app/Support/Http/Controllers/AugumentData.php b/app/Support/Http/Controllers/AugumentData.php index eb9002da70..6c1bcb637d 100644 --- a/app/Support/Http/Controllers/AugumentData.php +++ b/app/Support/Http/Controllers/AugumentData.php @@ -199,6 +199,7 @@ trait AugumentData /** @var BudgetLimitRepositoryInterface $blRepository */ $blRepository = app(BudgetLimitRepositoryInterface::class); + // properties for cache $cache = new CacheProperties; $cache->addProperty($start); @@ -207,18 +208,19 @@ trait AugumentData $cache->addProperty('get-limits'); if ($cache->has()) { - return $cache->get(); // @codeCoverageIgnore + return $cache->get(); // @codeCoverageIgnore } $set = $blRepository->getBudgetLimits($budget, $start, $end); $limits = new Collection(); $budgetCollection = new Collection([$budget]); + /** @var BudgetLimit $entry */ foreach ($set as $entry) { $currency = $entry->transactionCurrency; // clone because these objects change each other. - $currentStart = clone $start; - $currentEnd = clone $end; + $currentStart = clone $entry->start_date; + $currentEnd = clone $entry->end_date; $expenses = $opsRepository->sumExpenses($currentStart, $currentEnd, null, $budgetCollection, $currency); $spent = $expenses[(int)$currency->id]['sum'] ?? '0'; $entry->spent = $spent; diff --git a/app/Support/Http/Controllers/CronRunner.php b/app/Support/Http/Controllers/CronRunner.php index f5fb065f69..7d298fba3e 100644 --- a/app/Support/Http/Controllers/CronRunner.php +++ b/app/Support/Http/Controllers/CronRunner.php @@ -46,7 +46,7 @@ trait CronRunner return $e->getMessage(); } if (false === $result) { - return 'The recurring transaction cron job did not fire.'; + return 'The recurring transaction cron job did not fire. It was fired less than half a day ago.'; } return 'The recurring transaction cron job fired successfully.'; diff --git a/app/Support/Http/Controllers/GetConfigurationData.php b/app/Support/Http/Controllers/GetConfigurationData.php index ae21096ea5..2e1a5c32ba 100644 --- a/app/Support/Http/Controllers/GetConfigurationData.php +++ b/app/Support/Http/Controllers/GetConfigurationData.php @@ -205,6 +205,7 @@ trait GetConfigurationData $config = app('fireflyconfig')->get('last_rt_job', 0); $lastTime = (int)$config->data; $now = time(); + Log::debug(sprintf('verifyRecurringCronJob: last time is %d ("%s"), now is %d', $lastTime, $config->data, $now)); if (0 === $lastTime) { request()->session()->flash('info', trans('firefly.recurring_never_cron')); diff --git a/app/Support/Http/Controllers/ModelInformation.php b/app/Support/Http/Controllers/ModelInformation.php index 2c4a9d7ee7..9d486e5e6c 100644 --- a/app/Support/Http/Controllers/ModelInformation.php +++ b/app/Support/Http/Controllers/ModelInformation.php @@ -48,7 +48,7 @@ trait ModelInformation protected function getActionsForBill(Bill $bill): array // get info and augument { try { - $result = view( + $result = prefixView( 'rules.partials.action', [ 'oldAction' => 'link_to_bill', @@ -139,7 +139,7 @@ trait ModelInformation ]; foreach ($billTriggers as $index => $trigger) { try { - $string = view( + $string = prefixView( 'rules.partials.trigger', [ 'oldTrigger' => $trigger, @@ -257,7 +257,7 @@ trait ModelInformation foreach ($journalTriggers as $index => $trigger) { try { - $string = view( + $string = prefixView( 'rules.partials.trigger', [ 'oldTrigger' => $trigger, diff --git a/app/Support/Http/Controllers/RenderPartialViews.php b/app/Support/Http/Controllers/RenderPartialViews.php index 0582694a19..6703547548 100644 --- a/app/Support/Http/Controllers/RenderPartialViews.php +++ b/app/Support/Http/Controllers/RenderPartialViews.php @@ -78,7 +78,7 @@ trait RenderPartialViews // @codeCoverageIgnoreStart try { - $result = view('reports.options.double', compact('set'))->render(); + $result = prefixView('reports.options.double', compact('set'))->render(); } catch (Throwable $e) { Log::error(sprintf('Cannot render reports.options.tag: %s', $e->getMessage())); $result = 'Could not render view.'; @@ -111,7 +111,7 @@ trait RenderPartialViews $journals = $popupHelper->balanceForBudget($budget, $account, $attributes); // @codeCoverageIgnoreStart try { - $view = view('popup.report.balance-amount', compact('journals', 'budget', 'account'))->render(); + $view = prefixView('popup.report.balance-amount', compact('journals', 'budget', 'account'))->render(); } catch (Throwable $e) { Log::error(sprintf('Could not render: %s', $e->getMessage())); $view = 'Firefly III could not render the view. Please see the log files.'; @@ -134,7 +134,7 @@ trait RenderPartialViews $budgets = $repository->getBudgets(); // @codeCoverageIgnoreStart try { - $result = view('reports.options.budget', compact('budgets'))->render(); + $result = prefixView('reports.options.budget', compact('budgets'))->render(); } catch (Throwable $e) { Log::error(sprintf('Cannot render reports.options.tag: %s', $e->getMessage())); $result = 'Could not render view.'; @@ -167,7 +167,7 @@ trait RenderPartialViews $journals = $popupHelper->byBudget($budget, $attributes); // @codeCoverageIgnoreStart try { - $view = view('popup.report.budget-spent-amount', compact('journals', 'budget'))->render(); + $view = prefixView('popup.report.budget-spent-amount', compact('journals', 'budget'))->render(); } catch (Throwable $e) { Log::error(sprintf('Could not render: %s', $e->getMessage())); $view = 'Firefly III could not render the view. Please see the log files.'; @@ -196,7 +196,7 @@ trait RenderPartialViews $journals = $popupHelper->byCategory($category, $attributes); try { - $view = view('popup.report.category-entry', compact('journals', 'category'))->render(); + $view = prefixView('popup.report.category-entry', compact('journals', 'category'))->render(); } catch (Throwable $e) { Log::error(sprintf('Could not render: %s', $e->getMessage())); $view = 'Firefly III could not render the view. Please see the log files.'; @@ -217,7 +217,7 @@ trait RenderPartialViews $categories = $repository->getCategories(); // @codeCoverageIgnoreStart try { - $result = view('reports.options.category', compact('categories'))->render(); + $result = prefixView('reports.options.category', compact('categories'))->render(); } catch (Throwable $e) { Log::error(sprintf('Cannot render reports.options.category: %s', $e->getMessage())); $result = 'Could not render view.'; @@ -252,7 +252,7 @@ trait RenderPartialViews $journals = $popupHelper->byExpenses($account, $attributes); // @codeCoverageIgnoreStart try { - $view = view('popup.report.expense-entry', compact('journals', 'account'))->render(); + $view = prefixView('popup.report.expense-entry', compact('journals', 'account'))->render(); } catch (Throwable $e) { Log::error(sprintf('Could not render: %s', $e->getMessage())); $view = 'Firefly III could not render the view. Please see the log files.'; @@ -280,7 +280,7 @@ trait RenderPartialViews foreach ($currentActions as $entry) { $count = ($index + 1); try { - $actions[] = view( + $actions[] = prefixView( 'rules.partials.action', [ 'oldAction' => $entry->action_type, @@ -332,7 +332,7 @@ trait RenderPartialViews if ('user_action' !== $entry->trigger_type) { $count = ($index + 1); try { - $renderedEntries[] = view( + $renderedEntries[] = prefixView( 'rules.partials.trigger', [ 'oldTrigger' => OperatorQuerySearch::getRootOperator($entry->trigger_type), @@ -378,7 +378,7 @@ trait RenderPartialViews $journals = $popupHelper->byIncome($account, $attributes); // @codeCoverageIgnoreStart try { - $view = view('popup.report.income-entry', compact('journals', 'account'))->render(); + $view = prefixView('popup.report.income-entry', compact('journals', 'account'))->render(); } catch (Throwable $e) { Log::error(sprintf('Could not render: %s', $e->getMessage())); $view = 'Firefly III could not render the view. Please see the log files.'; @@ -398,7 +398,7 @@ trait RenderPartialViews { // @codeCoverageIgnoreStart try { - $result = view('reports.options.no-options')->render(); + $result = prefixView('reports.options.no-options')->render(); } catch (Throwable $e) { Log::error(sprintf('Cannot render reports.options.no-options: %s', $e->getMessage())); $result = 'Could not render view.'; @@ -422,7 +422,7 @@ trait RenderPartialViews // @codeCoverageIgnoreStart try { - $result = view('reports.options.tag', compact('tags'))->render(); + $result = prefixView('reports.options.tag', compact('tags'))->render(); } catch (Throwable $e) { Log::error(sprintf('Cannot render reports.options.tag: %s', $e->getMessage())); $result = 'Could not render view.'; diff --git a/app/Support/Http/Controllers/RuleManagement.php b/app/Support/Http/Controllers/RuleManagement.php index 3045007815..ae6ff1e9a7 100644 --- a/app/Support/Http/Controllers/RuleManagement.php +++ b/app/Support/Http/Controllers/RuleManagement.php @@ -57,7 +57,7 @@ trait RuleManagement $index = 0; foreach ($submittedOperators as $operator) { try { - $renderedEntries[] = view( + $renderedEntries[] = prefixView( 'rules.partials.trigger', [ 'oldTrigger' => OperatorQuerySearch::getRootOperator($operator['type']), @@ -140,7 +140,7 @@ trait RuleManagement if (is_array($oldInput)) { foreach ($oldInput as $oldAction) { try { - $triggers[] = view( + $triggers[] = prefixView( 'rules.partials.action', [ 'oldAction' => $oldAction['type'], @@ -185,7 +185,7 @@ trait RuleManagement if (is_array($oldInput)) { foreach ($oldInput as $oldTrigger) { try { - $renderedEntries[] = view( + $renderedEntries[] = prefixView( 'rules.partials.trigger', [ 'oldTrigger' => OperatorQuerySearch::getRootOperator($oldTrigger['type']), diff --git a/app/Support/Preferences.php b/app/Support/Preferences.php index 702dc20f6b..62bdbdefd7 100644 --- a/app/Support/Preferences.php +++ b/app/Support/Preferences.php @@ -151,10 +151,6 @@ class Preferences */ public function getForUser(User $user, string $name, $default = null): ?Preference { - $fullName = sprintf('preference%s%s', $user->id, $name); - if (Cache::has($fullName)) { - return Cache::get($fullName); - } $preference = Preference::where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data', 'updated_at', 'created_at']); if (null !== $preference && null === $preference->data) { try { @@ -166,7 +162,6 @@ class Preferences } if (null !== $preference) { - Cache::forever($fullName, $preference); return $preference; } @@ -185,32 +180,11 @@ class Preferences * @param null|string $default * * @return \FireflyIII\Models\Preference|null + * TODO remove me */ public function getFreshForUser(User $user, string $name, $default = null): ?Preference { - $fullName = sprintf('preference%s%s', $user->id, $name); - $preference = Preference::where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data', 'updated_at', 'created_at']); - if (null !== $preference && null === $preference->data) { - try { - $preference->delete(); - } catch (Exception $e) { - Log::debug(sprintf('Could not delete preference #%d: %s', $preference->id, $e->getMessage())); - } - $preference = null; - } - - if (null !== $preference) { - Cache::forever($fullName, $preference); - - return $preference; - } - // no preference found and default is null: - if (null === $default) { - // return NULL - return null; - } - - return $this->setForUser($user, $name, $default); + return $this->getForUser($user, $name, $default); } /** diff --git a/app/Support/Report/Category/CategoryReportGenerator.php b/app/Support/Report/Category/CategoryReportGenerator.php index 8e0b8abd58..34431affd5 100644 --- a/app/Support/Report/Category/CategoryReportGenerator.php +++ b/app/Support/Report/Category/CategoryReportGenerator.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * CategoryReportGenerator.php diff --git a/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php b/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php index 0962bbb62c..195946bdd9 100644 --- a/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php +++ b/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php @@ -79,15 +79,18 @@ trait CalculateXOccurrencesSince */ protected function getXMonthlyOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array { - Log::debug(sprintf('Now in %s', __METHOD__)); + Log::debug(sprintf('Now in %s(%s, %s, %d)', __METHOD__, $date->format('Y-m-d'), $afterDate->format('Y-m-d'), $count)); $return = []; $mutator = clone $date; $total = 0; $attempts = 0; $dayOfMonth = (int)$moment; + $dayOfMonth = 0 === $dayOfMonth ? 1 : $dayOfMonth; if ($mutator->day > $dayOfMonth) { + Log::debug(sprintf('%d is after %d, add a month. Mutator is now', $mutator->day, $dayOfMonth)); // day has passed already, add a month. $mutator->addMonth(); + Log::debug(sprintf('%s', $mutator->format('Y-m-d'))); } while ($total < $count) { @@ -98,7 +101,8 @@ trait CalculateXOccurrencesSince $total++; } $attempts++; - $mutator->endOfMonth()->addDay(); + $mutator = $mutator->endOfMonth()->addDay(); + Log::debug(sprintf('Mutator is now %s', $mutator->format('Y-m-d'))); } return $return; @@ -213,7 +217,9 @@ trait CalculateXOccurrencesSince $date = new Carbon($moment); $date->year = $mutator->year; if ($mutator > $date) { - Log::debug(sprintf('mutator (%s) > date (%s), so add a year to date (%s)', $mutator->format('Y-m-d'), $date->format('Y-m-d'), $date->format('Y-m-d'),)); + Log::debug( + sprintf('mutator (%s) > date (%s), so add a year to date (%s)', $mutator->format('Y-m-d'), $date->format('Y-m-d'), $date->format('Y-m-d'),) + ); $date->addYear(); Log::debug(sprintf('Date is now %s', $date->format('Y-m-d'))); } diff --git a/app/Support/Request/AppendsLocationData.php b/app/Support/Request/AppendsLocationData.php index 34ef3c87eb..a5e7b8769a 100644 --- a/app/Support/Request/AppendsLocationData.php +++ b/app/Support/Request/AppendsLocationData.php @@ -79,53 +79,47 @@ trait AppendsLocationData Log::debug(sprintf('Now in appendLocationData("%s")', $prefix), $data); $data['store_location'] = false; $data['update_location'] = false; + $data['remove_location'] = false; $data['longitude'] = null; $data['latitude'] = null; $data['zoom_level'] = null; - $longitudeKey = $this->getLocationKey($prefix, 'longitude'); - $latitudeKey = $this->getLocationKey($prefix, 'latitude'); - $zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level'); - $hasLocationKey = $this->getLocationKey($prefix, 'has_location'); - $hasLocation = $this->boolean($hasLocationKey) || true === ($data['has_location'] ?? false); + $longitudeKey = $this->getLocationKey($prefix, 'longitude'); + $latitudeKey = $this->getLocationKey($prefix, 'latitude'); + $zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level'); + $isValidPOST = $this->isValidPost($prefix); + $isValidPUT = $this->isValidPUT($prefix); + $isValidEmptyPUT = $this->isValidEmptyPUT($prefix); - // for a POST (store), all fields must be present and accounted for: - if ( - ('POST' === $this->method() && $this->routeIs('*.store')) - && ($this->has($longitudeKey) && $this->has($latitudeKey) && $this->has($zoomLevelKey)) - ) { - Log::debug('Method is POST and all fields present.'); - $data['store_location'] = $this->boolean($hasLocationKey); - $data['longitude'] = $this->nullableString($longitudeKey); - $data['latitude'] = $this->nullableString($latitudeKey); - $data['zoom_level'] = $this->nullableString($zoomLevelKey); + // for a POST (store), all fields must be present and not NULL. + if ($isValidPOST) { + Log::debug('Method is POST and all fields present and not NULL.'); + $data['store_location'] = true; + $data['longitude'] = $this->string($longitudeKey); + $data['latitude'] = $this->string($latitudeKey); + $data['zoom_level'] = $this->string($zoomLevelKey); } - if ( - ($this->has($longitudeKey) && $this->has($latitudeKey) && $this->has($zoomLevelKey)) - && ( - ('PUT' === $this->method() && $this->routeIs('*.update')) - || ('POST' === $this->method() && $this->routeIs('*.update')) - ) - ) { - Log::debug('Method is PUT and all fields present.'); + + if ($isValidPUT) { + Log::debug('Method is PUT and all fields present and not NULL.'); $data['update_location'] = true; - $data['longitude'] = $this->nullableString($longitudeKey); - $data['latitude'] = $this->nullableString($latitudeKey); - $data['zoom_level'] = $this->nullableString($zoomLevelKey); + $data['longitude'] = $this->string($longitudeKey); + $data['latitude'] = $this->string($latitudeKey); + $data['zoom_level'] = $this->string($zoomLevelKey); } - if (false === $hasLocation || null === $data['longitude'] || null === $data['latitude'] || null === $data['zoom_level']) { - Log::debug('One of the fields is NULL or hasLocation is false, wont save.'); - Log::debug(sprintf('Longitude : %s', var_export($data['longitude'], true))); - Log::debug(sprintf('Latitude : %s', var_export($data['latitude'], true))); - Log::debug(sprintf('Zoom level : %s', var_export($data['zoom_level'], true))); - Log::debug(sprintf('Has location: %s', var_export($hasLocation, true))); - $data['store_location'] = false; - $data['update_location'] = true; // update is always true, but the values are null: - $data['longitude'] = null; - $data['latitude'] = null; - $data['zoom_level'] = null; + if ($isValidEmptyPUT) { + Log::debug('Method is PUT and all fields present and NULL.'); + $data['remove_location'] = true; } Log::debug(sprintf('Returning longitude: "%s", latitude: "%s", zoom level: "%s"', $data['longitude'], $data['latitude'], $data['zoom_level'])); + Log::debug( + sprintf( + 'Returning actions: store: %s, update: %s, delete: %s', + var_export($data['store_location'], true), + var_export($data['update_location'], true), + var_export($data['remove_location'], true), + ) + ); return $data; } @@ -154,4 +148,63 @@ trait AppendsLocationData return sprintf('%s_%s', $prefix, $key); } + /** + * @param string|null $prefix + * + * @return bool + */ + private function isValidPOST(?string $prefix): bool + { + $longitudeKey = $this->getLocationKey($prefix, 'longitude'); + $latitudeKey = $this->getLocationKey($prefix, 'latitude'); + $zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level'); + + return ('POST' === $this->method() && $this->routeIs('*.store')) + && ( + null !== $this->get($longitudeKey) + && null !== $this->get($latitudeKey) + && null !== $this->get($zoomLevelKey) + ); + } + + /** + * @param string|null $prefix + * + * @return bool + */ + private function isValidPUT(?string $prefix): bool + { + $longitudeKey = $this->getLocationKey($prefix, 'longitude'); + $latitudeKey = $this->getLocationKey($prefix, 'latitude'); + $zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level'); + + return ( + null !== $this->get($longitudeKey) + && null !== $this->get($latitudeKey) + && null !== $this->get($zoomLevelKey)) + && (('PUT' === $this->method() && $this->routeIs('*.update')) + || ('POST' === $this->method() && $this->routeIs('*.update'))); + } + + /** + * @param string|null $prefix + * + * @return bool + */ + private function isValidEmptyPUT(?string $prefix): bool + { + $longitudeKey = $this->getLocationKey($prefix, 'longitude'); + $latitudeKey = $this->getLocationKey($prefix, 'latitude'); + $zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level'); + + return ( + null === $this->get($longitudeKey) + && null === $this->get($latitudeKey) + && null === $this->get($zoomLevelKey)) + && ('PUT' === $this->method() + || ('POST' === $this->method() && $this->routeIs('*.update')) + ); + + } + } diff --git a/app/Support/Request/ChecksLogin.php b/app/Support/Request/ChecksLogin.php index 0b91c83e6a..cba9f91fd9 100644 --- a/app/Support/Request/ChecksLogin.php +++ b/app/Support/Request/ChecksLogin.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * ChecksLogin.php diff --git a/app/Support/Request/ConvertsDataTypes.php b/app/Support/Request/ConvertsDataTypes.php index 695a5d9db6..e2ff0e08fa 100644 --- a/app/Support/Request/ConvertsDataTypes.php +++ b/app/Support/Request/ConvertsDataTypes.php @@ -265,6 +265,9 @@ trait ConvertsDataTypes if (null === $value) { return false; } + if ('' === $value) { + return false; + } if ('true' === $value) { return true; } diff --git a/app/Support/Request/GetRecurrenceData.php b/app/Support/Request/GetRecurrenceData.php index a58f223391..9b8ffe0dc5 100644 --- a/app/Support/Request/GetRecurrenceData.php +++ b/app/Support/Request/GetRecurrenceData.php @@ -33,31 +33,58 @@ trait GetRecurrenceData * * @return array */ - protected function getSingleRecurrenceData(array $transaction): array + protected function getSingleTransactionData(array $transaction): array { - return [ - 'amount' => $transaction['amount'], - 'currency_id' => isset($transaction['currency_id']) ? (int) $transaction['currency_id'] : null, - 'currency_code' => $transaction['currency_code'] ?? null, - 'foreign_amount' => $transaction['foreign_amount'] ?? null, - 'foreign_currency_id' => isset($transaction['foreign_currency_id']) ? (int) $transaction['foreign_currency_id'] : null, - 'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null, - 'source_id' => isset($transaction['source_id']) ? (int) $transaction['source_id'] : null, - 'source_name' => isset($transaction['source_name']) ? (string) $transaction['source_name'] : null, - 'destination_id' => isset($transaction['destination_id']) ? (int) $transaction['destination_id'] : null, - 'destination_name' => isset($transaction['destination_name']) ? (string) $transaction['destination_name'] : null, - 'description' => $transaction['description'], - 'type' => $this->string('type'), + $return = []; - // new and updated fields: - 'piggy_bank_id' => isset($transaction['piggy_bank_id']) ? (int) $transaction['piggy_bank_id'] : null, - 'piggy_bank_name' => $transaction['piggy_bank_name'] ?? null, - 'tags' => $transaction['tags'] ?? [], - 'budget_id' => isset($transaction['budget_id']) ? (int) $transaction['budget_id'] : null, - 'budget_name' => $transaction['budget_name'] ?? null, - 'category_id' => isset($transaction['category_id']) ? (int) $transaction['category_id'] : null, - 'category_name' => $transaction['category_name'] ?? null, - ]; + // amount + currency + if (array_key_exists('amount', $transaction)) { + $return['amount'] = $transaction['amount']; + } + if (array_key_exists('currency_id', $transaction)) { + $return['currency_id'] = (int)$transaction['currency_id']; + } + if (array_key_exists('currency_code', $transaction)) { + $return['currency_code'] = $transaction['currency_code']; + } + + // foreign amount + currency + if (array_key_exists('foreign_amount', $transaction)) { + $return['foreign_amount'] = $transaction['foreign_amount']; + } + if (array_key_exists('foreign_currency_id', $transaction)) { + $return['foreign_currency_id'] = (int)$transaction['foreign_currency_id']; + } + if (array_key_exists('foreign_currency_code', $transaction)) { + $return['foreign_currency_code'] = $transaction['foreign_currency_code']; + } + // source + dest + if (array_key_exists('source_id', $transaction)) { + $return['source_id'] = (int)$transaction['source_id']; + } + if (array_key_exists('destination_id', $transaction)) { + $return['destination_id'] = (int)$transaction['destination_id']; + } + // description + if (array_key_exists('description', $transaction)) { + $return['description'] = $transaction['description']; + } + + if (array_key_exists('piggy_bank_id', $transaction)) { + $return['piggy_bank_id'] = (int)$transaction['piggy_bank_id']; + } + + if (array_key_exists('tags', $transaction)) { + $return['tags'] = $transaction['tags']; + } + if (array_key_exists('budget_id', $transaction)) { + $return['budget_id'] = (int)$transaction['budget_id']; + } + if (array_key_exists('category_id', $transaction)) { + $return['category_id'] = (int)$transaction['category_id']; + } + + return $return; } } diff --git a/app/Support/Request/GetRuleConfiguration.php b/app/Support/Request/GetRuleConfiguration.php index 8be6823193..a852550c38 100644 --- a/app/Support/Request/GetRuleConfiguration.php +++ b/app/Support/Request/GetRuleConfiguration.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * GetRuleConfiguration.php diff --git a/app/Support/Search/OperatorQuerySearch.php b/app/Support/Search/OperatorQuerySearch.php index afabdc2632..966e2905df 100644 --- a/app/Support/Search/OperatorQuerySearch.php +++ b/app/Support/Search/OperatorQuerySearch.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * OperatorQuerySearch.php @@ -79,6 +100,7 @@ class OperatorQuerySearch implements SearchInterface private Collection $modifiers; // obsolete private Collection $operators; private string $originalQuery; + private Carbon $date; /** * OperatorQuerySearch constructor. @@ -94,6 +116,7 @@ class OperatorQuerySearch implements SearchInterface $this->words = []; $this->limit = 25; $this->originalQuery = ''; + $this->date = today(config('app.timezone')); $this->validOperators = array_keys(config('firefly.search.operators')); $this->startTime = microtime(true); $this->accountRepository = app(AccountRepositoryInterface::class); @@ -123,6 +146,14 @@ class OperatorQuerySearch implements SearchInterface return $this->operators; } + /** + * @param Carbon $date + */ + public function setDate(Carbon $date): void + { + $this->date = $date; + } + /** * @inheritDoc * @codeCoverageIgnore @@ -234,7 +265,7 @@ class OperatorQuerySearch implements SearchInterface throw new FireflyException(sprintf('Firefly III search cant handle "%s"-nodes', $class)); case Subquery::class: // loop all notes in subquery: - foreach($searchNode->getNodes() as $subNode) { + foreach ($searchNode->getNodes() as $subNode) { $this->handleSearchNode($subNode); // lets hope its not too recursive! } break; @@ -810,7 +841,7 @@ class OperatorQuerySearch implements SearchInterface { $parser = new ParseDateString; if ($parser->isDateRange($value)) { - return $parser->parseRange($value, today(config('app.timezone'))); + return $parser->parseRange($value, $this->date); } $date = $parser->parseDate($value); diff --git a/app/Support/Search/SearchInterface.php b/app/Support/Search/SearchInterface.php index 42a2d387b4..0dcfcc0f7d 100644 --- a/app/Support/Search/SearchInterface.php +++ b/app/Support/Search/SearchInterface.php @@ -22,6 +22,7 @@ declare(strict_types=1); namespace FireflyIII\Support\Search; +use Carbon\Carbon; use FireflyIII\User; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; @@ -80,4 +81,9 @@ interface SearchInterface * @param User $user */ public function setUser(User $user); + + /** + * @param Carbon $date + */ + public function setDate(Carbon $date): void; } diff --git a/app/Support/Steam.php b/app/Support/Steam.php index ad416736e6..20b18ef04c 100644 --- a/app/Support/Steam.php +++ b/app/Support/Steam.php @@ -29,7 +29,6 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use Illuminate\Support\Collection; -use Log; use stdClass; /** @@ -79,17 +78,9 @@ class Steam ->where('transactions.transaction_currency_id', '!=', $currency->id) ->get(['transactions.foreign_amount'])->toArray(); $foreignBalance = $this->sumTransactions($transactions, 'foreign_amount'); - - // check: - Log::debug(sprintf('Steam::balance. Native balance is "%s"', $nativeBalance)); - Log::debug(sprintf('Steam::balance. Foreign balance is "%s"', $foreignBalance)); - - $balance = bcadd($nativeBalance, $foreignBalance); - $virtual = null === $account->virtual_balance ? '0' : (string)$account->virtual_balance; - - Log::debug(sprintf('Steam::balance. Virtual balance is "%s"', $virtual)); - - $balance = bcadd($balance, $virtual); + $balance = bcadd($nativeBalance, $foreignBalance); + $virtual = null === $account->virtual_balance ? '0' : (string)$account->virtual_balance; + $balance = bcadd($balance, $virtual); $cache->store($balance); diff --git a/app/Support/Twig/General.php b/app/Support/Twig/General.php index acc13aaf5e..7812e9434a 100644 --- a/app/Support/Twig/General.php +++ b/app/Support/Twig/General.php @@ -50,6 +50,7 @@ class General extends AbstractExtension $this->formatFilesize(), $this->mimeIcon(), $this->markdown(), + $this->floatval(), ]; } @@ -297,6 +298,19 @@ class General extends AbstractExtension ); } + /** + * @return TwigFilter + */ + protected function floatval(): TwigFilter + { + return new TwigFilter( + 'floatval', + static function ($value): float { + return (float)$value; + } + ); + } + /** * Show icon with attachment. * diff --git a/app/Support/Twig/TransactionGroupTwig.php b/app/Support/Twig/TransactionGroupTwig.php index 281d6b698b..934411517e 100644 --- a/app/Support/Twig/TransactionGroupTwig.php +++ b/app/Support/Twig/TransactionGroupTwig.php @@ -62,9 +62,6 @@ class TransactionGroupTwig extends AbstractExtension return new TwigFunction( 'journalGetMetaDate', static function (int $journalId, string $metaField) { - if ('testing' === config('app.env')) { - Log::warning('Twig TransactionGroup::journalGetMetaDate should NOT be called in the TEST environment!'); - } $entry = DB::table('journal_meta') ->where('name', $metaField) ->where('transaction_journal_id', $journalId) @@ -87,9 +84,6 @@ class TransactionGroupTwig extends AbstractExtension return new TwigFunction( 'journalGetMetaField', static function (int $journalId, string $metaField) { - if ('testing' === config('app.env')) { - Log::warning('Twig TransactionGroup::journalGetMetaField should NOT be called in the TEST environment!'); - } $entry = DB::table('journal_meta') ->where('name', $metaField) ->where('transaction_journal_id', $journalId) diff --git a/app/TransactionRules/Actions/ActionInterface.php b/app/TransactionRules/Actions/ActionInterface.php index 7a8f7d097c..7da3388e85 100644 --- a/app/TransactionRules/Actions/ActionInterface.php +++ b/app/TransactionRules/Actions/ActionInterface.php @@ -37,9 +37,11 @@ interface ActionInterface public function __construct(RuleAction $action); /** - * Execute the action on an array. + * Execute the action on an array. Returns "true" if the action was a success and the action + * was applied. Returns false if otherwise. * * @param array $journal + * * @return bool */ public function actOnArray(array $journal): bool; diff --git a/app/TransactionRules/Engine/RuleEngineInterface.php b/app/TransactionRules/Engine/RuleEngineInterface.php index f976038593..b61f175255 100644 --- a/app/TransactionRules/Engine/RuleEngineInterface.php +++ b/app/TransactionRules/Engine/RuleEngineInterface.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /* * RuleEngineInterface.php @@ -34,6 +55,7 @@ interface RuleEngineInterface * @param User $user */ public function setUser(User $user): void; + /** * Add rules for the engine to execute. * @@ -65,4 +87,11 @@ interface RuleEngineInterface */ public function find(): Collection; + /** + * Return the number of changed transactions from the previous "fire" action. + * + * @return int + */ + public function getResults(): int; + } diff --git a/app/TransactionRules/Engine/SearchRuleEngine.php b/app/TransactionRules/Engine/SearchRuleEngine.php index e876aa16e7..b7f1477366 100644 --- a/app/TransactionRules/Engine/SearchRuleEngine.php +++ b/app/TransactionRules/Engine/SearchRuleEngine.php @@ -1,8 +1,8 @@ . */ +declare(strict_types=1); + namespace FireflyIII\TransactionRules\Engine; +use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleGroup; use FireflyIII\Models\RuleTrigger; +use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Support\Search\SearchInterface; use FireflyIII\TransactionRules\Factory\ActionFactory; use FireflyIII\User; @@ -42,12 +46,14 @@ class SearchRuleEngine implements RuleEngineInterface private Collection $rules; private array $operators; private Collection $groups; + private array $resultCount; public function __construct() { - $this->rules = new Collection; - $this->groups = new Collection; - $this->operators = []; + $this->rules = new Collection; + $this->groups = new Collection; + $this->operators = []; + $this->resultCount = []; } /** @@ -102,6 +108,7 @@ class SearchRuleEngine implements RuleEngineInterface */ public function fire(): void { + $this->resultCount = []; Log::debug('SearchRuleEngine::fire()!'); // if rules and no rule groups, file each rule separately. @@ -146,6 +153,16 @@ class SearchRuleEngine implements RuleEngineInterface return $collection->unique(); } + /** + * Return the number of changed transactions from the previous "fire" action. + * + * @return int + */ + public function getResults(): int + { + return count($this->resultCount); + } + /** * Returns true if the rule has been triggered. * @@ -226,9 +243,28 @@ class SearchRuleEngine implements RuleEngineInterface { Log::debug(sprintf('Executing rule action "%s" with value "%s"', $ruleAction->action_type, $ruleAction->action_value)); $actionClass = ActionFactory::getAction($ruleAction); - $actionClass->actOnArray($transaction); + $result = $actionClass->actOnArray($transaction); + $journalId = $transaction['transaction_journal_id'] ?? 0; + if (true === $result) { + $this->resultCount[$journalId] = isset($this->resultCount[$journalId]) ? $this->resultCount[$journalId]++ : 1; + Log::debug( + sprintf( + 'Action "%s" on journal #%d was executed, so count a result. Updated transaction journal count is now %d.', + $ruleAction->action_type, + $transaction['transaction_journal_id'] ?? 0, + count($this->resultCount), + ) + ); + } + if (false === $result) { + Log::debug(sprintf('Action "%s" reports NO changes were made.', $ruleAction->action_type)); + } + + // pick up from the action if it actually acted or not: + + if ($ruleAction->stop_processing) { - Log::debug(sprintf('Rule action "%s" asks to break, so break!', $ruleAction->action_value)); + Log::debug(sprintf('Rule action "%s" asks to break, so break!', $ruleAction->action_type)); return true; } @@ -253,8 +289,9 @@ class SearchRuleEngine implements RuleEngineInterface Log::debug(sprintf('SearchRuleEngine:: done processing strict rule #%d', $rule->id)); $result = $collection->count() > 0; - if(true === $result) { + if (true === $result) { Log::debug(sprintf('SearchRuleEngine:: rule #%d was triggered (on %d transaction(s)).', $rule->id, $collection->count())); + return true; } Log::debug(sprintf('SearchRuleEngine:: rule #%d was not triggered (on %d transaction(s)).', $rule->id, $collection->count())); @@ -290,6 +327,7 @@ class SearchRuleEngine implements RuleEngineInterface */ private function findStrictRule(Rule $rule): Collection { + Log::debug(sprintf('Now in findStrictRule(#%d)', $rule->id ?? 0)); $searchArray = []; /** @var RuleTrigger $ruleTrigger */ foreach ($rule->ruleTriggers as $ruleTrigger) { @@ -297,31 +335,38 @@ class SearchRuleEngine implements RuleEngineInterface $needsContext = config(sprintf('firefly.search.operators.%s.needs_context', $ruleTrigger->trigger_type)) ?? true; if (false === $needsContext) { Log::debug(sprintf('SearchRuleEngine:: add a rule trigger: %s:true', $ruleTrigger->trigger_type)); - $searchArray[$ruleTrigger->trigger_type] = 'true'; + $searchArray[$ruleTrigger->trigger_type][] = 'true'; } if (true === $needsContext) { Log::debug(sprintf('SearchRuleEngine:: add a rule trigger: %s:"%s"', $ruleTrigger->trigger_type, $ruleTrigger->trigger_value)); - $searchArray[$ruleTrigger->trigger_type] = sprintf('"%s"', $ruleTrigger->trigger_value); + $searchArray[$ruleTrigger->trigger_type][] = sprintf('"%s"', $ruleTrigger->trigger_value); } } // add local operators: foreach ($this->operators as $operator) { Log::debug(sprintf('SearchRuleEngine:: add local added operator: %s:"%s"', $operator['type'], $operator['value'])); - $searchArray[$operator['type']] = sprintf('"%s"', $operator['value']); + $searchArray[$operator['type']][] = sprintf('"%s"', $operator['value']); } + $date = today(config('app.timezone')); + if ($this->hasSpecificJournalTrigger($searchArray)) { + $date = $this->setDateFromJournalTrigger($searchArray); + } + // build and run the search engine. $searchEngine = app(SearchInterface::class); $searchEngine->setUser($this->user); $searchEngine->setPage(1); $searchEngine->setLimit(31337); + $searchEngine->setDate($date); - foreach ($searchArray as $type => $value) { - $searchEngine->parseQuery(sprintf('%s:%s', $type, $value)); + foreach ($searchArray as $type => $searches) { + foreach ($searches as $value) { + $searchEngine->parseQuery(sprintf('%s:%s', $type, $value)); + } } - $result = $searchEngine->searchTransactions(); return $result->getCollection(); @@ -399,6 +444,38 @@ class SearchRuleEngine implements RuleEngineInterface return $unique; } + /** + * Search in the triggers of this particular search and if it contains + * one search operator for "journal_id" it means the date ranges + * in the search may need to be updated. + * + * @param array $array + * + * @return bool + */ + private function hasSpecificJournalTrigger(array $array): bool + { + Log::debug('Now in hasSpecificJournalTrigger.'); + $journalTrigger = false; + $dateTrigger = false; + foreach ($array as $triggerName => $values) { + if ('journal_id' === $triggerName) { + if (is_array($values) && 1 === count($values)) { + Log::debug('Found a journal_id trigger with 1 journal, true.'); + $journalTrigger = true; + } + } + if (in_array($triggerName, ['date_is', 'date', 'on', 'date_before', 'before', 'date_after', 'after'], true)) { + Log::debug('Found a date related trigger, set to true.'); + $dateTrigger = true; + } + } + $result = $journalTrigger && $dateTrigger; + Log::debug(sprintf('Result of hasSpecificJournalTrigger is %s.', var_export($result, true))); + + return $result; + } + /** * @param RuleGroup $group * @@ -424,4 +501,37 @@ class SearchRuleEngine implements RuleEngineInterface return $all; } + + /** + * @param array $array + * + * @return Carbon + */ + private function setDateFromJournalTrigger(array $array): Carbon + { + Log::debug('Now in setDateFromJournalTrigger()'); + $journalId = 0; + foreach ($array as $triggerName => $values) { + if ('journal_id' === $triggerName) { + if (is_array($values) && 1 === count($values)) { + $journalId = (int)trim(($values[0] ?? '"0"'), '"'); // follows format "123". + Log::debug(sprintf('Found journal ID #%d', $journalId)); + } + } + } + if (0 !== $journalId) { + $repository = app(JournalRepositoryInterface::class); + $repository->setUser($this->user); + $journal = $repository->findNull($journalId); + if (null !== $journal) { + $date = $journal->date; + Log::debug(sprintf('Found journal #%d with date %s.', $journal->id, $journal->date->format('Y-m-d'))); + + return $date; + } + } + Log::debug('Found no journal, return default date.'); + + return today(config('app.timezone')); + } } diff --git a/app/TransactionRules/Factory/ActionFactory.php b/app/TransactionRules/Factory/ActionFactory.php index 37a8acae7b..0f353ddc2b 100644 --- a/app/TransactionRules/Factory/ActionFactory.php +++ b/app/TransactionRules/Factory/ActionFactory.php @@ -1,4 +1,25 @@ . + */ + /** * ActionFactory.php * Copyright (c) 2019 Robert Horlings diff --git a/app/Transformers/AccountTransformer.php b/app/Transformers/AccountTransformer.php index c6c2d61bf2..711f134c9d 100644 --- a/app/Transformers/AccountTransformer.php +++ b/app/Transformers/AccountTransformer.php @@ -27,15 +27,13 @@ namespace FireflyIII\Transformers; use Carbon\Carbon; use FireflyIII\Models\Account; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use Log; /** * Class AccountTransformer */ class AccountTransformer extends AbstractTransformer { - /** @var AccountRepositoryInterface */ - protected $repository; + protected AccountRepositoryInterface $repository; /** * @@ -61,26 +59,21 @@ class AccountTransformer extends AbstractTransformer // get account type: $fullType = $account->accountType->type; - $accountType = (string) config(sprintf('firefly.shortNamesByFullName.%s', $fullType)); - $liabilityType = (string) config(sprintf('firefly.shortLiabilityNameByFullName.%s', $fullType)); - $liabilityType = '' === $liabilityType ? null : $liabilityType; + $accountType = (string)config(sprintf('firefly.shortNamesByFullName.%s', $fullType)); + $liabilityType = (string)config(sprintf('firefly.shortLiabilityNameByFullName.%s', $fullType)); + $liabilityType = '' === $liabilityType ? null : strtolower($liabilityType); // get account role (will only work if the type is asset. $accountRole = $this->getAccountRole($account, $accountType); $date = $this->getDate(); + $date->endOfDay(); [$currencyId, $currencyCode, $currencySymbol, $decimalPlaces] = $this->getCurrency($account); [$creditCardType, $monthlyPaymentDate] = $this->getCCInfo($account, $accountRole, $accountType); [$openingBalance, $openingBalanceDate] = $this->getOpeningBalance($account, $accountType); [$interest, $interestPeriod] = $this->getInterest($account, $accountType); - $openingBalance = number_format((float) $openingBalance, $decimalPlaces, '.', ''); - $liabilityAmount = null; - $liabilityStart = null; - if (null !== $liabilityType) { - $liabilityAmount = $openingBalance; - $liabilityStart = $openingBalanceDate; - } + $openingBalance = number_format((float)$openingBalance, $decimalPlaces, '.', ''); $includeNetWorth = '0' !== $this->repository->getMetaValue($account, 'include_net_worth'); $longitude = null; $latitude = null; @@ -89,22 +82,29 @@ class AccountTransformer extends AbstractTransformer if (null !== $location) { $longitude = $location->longitude; $latitude = $location->latitude; - $zoomLevel = $location->zoom_level; + $zoomLevel = (int)$location->zoom_level; } + + // no order for some accounts: + $order = (int)$account->order; + if (!in_array(strtolower($accountType), ['liability', 'liabilities', 'asset'])) { + $order = null; + } + return [ - 'id' => (int) $account->id, + 'id' => (string)$account->id, 'created_at' => $account->created_at->toAtomString(), 'updated_at' => $account->updated_at->toAtomString(), 'active' => $account->active, - 'order' => $account->order, + 'order' => $order, 'name' => $account->name, - 'type' => $accountType, + 'type' => strtolower($accountType), 'account_role' => $accountRole, 'currency_id' => $currencyId, 'currency_code' => $currencyCode, 'currency_symbol' => $currencySymbol, 'currency_decimal_places' => $decimalPlaces, - 'current_balance' => number_format((float) app('steam')->balance($account, $date), $decimalPlaces, '.', ''), + 'current_balance' => number_format((float)app('steam')->balance($account, $date), $decimalPlaces, '.', ''), 'current_balance_date' => $date->format('Y-m-d'), 'notes' => $this->repository->getNoteText($account), 'monthly_payment_date' => $monthlyPaymentDate, @@ -112,13 +112,11 @@ class AccountTransformer extends AbstractTransformer 'account_number' => $this->repository->getMetaValue($account, 'account_number'), 'iban' => '' === $account->iban ? null : $account->iban, 'bic' => $this->repository->getMetaValue($account, 'BIC'), - 'virtual_balance' => number_format((float) $account->virtual_balance, $decimalPlaces, '.', ''), + 'virtual_balance' => number_format((float)$account->virtual_balance, $decimalPlaces, '.', ''), 'opening_balance' => $openingBalance, 'opening_balance_date' => $openingBalanceDate, 'liability_type' => $liabilityType, - 'liability_amount' => $liabilityAmount, - 'liability_start_date' => $liabilityStart, - 'interest' => $interest, + 'interest' => (float)$interest, 'interest_period' => $interestPeriod, 'include_net_worth' => $includeNetWorth, 'longitude' => $longitude, @@ -143,7 +141,7 @@ class AccountTransformer extends AbstractTransformer private function getAccountRole(Account $account, string $accountType): ?string { $accountRole = $this->repository->getMetaValue($account, 'account_role'); - if ('asset' !== $accountType || '' === (string) $accountRole) { + if ('asset' !== $accountType || '' === (string)$accountRole) { $accountRole = null; } @@ -182,7 +180,7 @@ class AccountTransformer extends AbstractTransformer if (null === $currency) { $currency = app('amount')->getDefaultCurrencyByUser($account->user); } - $currencyId = (int) $currency->id; + $currencyId = (string)$currency->id; $currencyCode = $currency->code; $decimalPlaces = $currency->decimal_places; $currencySymbol = $currency->symbol; diff --git a/app/Transformers/AttachmentTransformer.php b/app/Transformers/AttachmentTransformer.php index 5dbe9193b0..34d2f4ade1 100644 --- a/app/Transformers/AttachmentTransformer.php +++ b/app/Transformers/AttachmentTransformer.php @@ -43,9 +43,6 @@ class AttachmentTransformer extends AbstractTransformer public function __construct() { $this->repository = app(AttachmentRepositoryInterface::class); - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); - } } /** @@ -60,10 +57,10 @@ class AttachmentTransformer extends AbstractTransformer $this->repository->setUser($attachment->user); return [ - 'id' => (int)$attachment->id, + 'id' => (string)$attachment->id, 'created_at' => $attachment->created_at->toAtomString(), 'updated_at' => $attachment->updated_at->toAtomString(), - 'attachable_id' => (int) $attachment->attachable_id, + 'attachable_id' => (string)$attachment->attachable_id, 'attachable_type' => str_replace('FireflyIII\\Models\\', '', $attachment->attachable_type), 'md5' => $attachment->md5, 'filename' => $attachment->filename, diff --git a/app/Transformers/AvailableBudgetTransformer.php b/app/Transformers/AvailableBudgetTransformer.php index 46bf5289f5..538a18547f 100644 --- a/app/Transformers/AvailableBudgetTransformer.php +++ b/app/Transformers/AvailableBudgetTransformer.php @@ -36,12 +36,9 @@ use Log; */ class AvailableBudgetTransformer extends AbstractTransformer { - /** @var NoBudgetRepositoryInterface */ - private $noBudgetRepository; - /** @var OperationsRepositoryInterface */ - private $opsRepository; - /** @var BudgetRepositoryInterface */ - private $repository; + private NoBudgetRepositoryInterface $noBudgetRepository; + private OperationsRepositoryInterface $opsRepository; + private BudgetRepositoryInterface $repository; /** * CurrencyTransformer constructor. @@ -53,9 +50,6 @@ class AvailableBudgetTransformer extends AbstractTransformer $this->repository = app(BudgetRepositoryInterface::class); $this->opsRepository = app(OperationsRepositoryInterface::class); $this->noBudgetRepository = app(NoBudgetRepositoryInterface::class); - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); - } } /** @@ -71,10 +65,10 @@ class AvailableBudgetTransformer extends AbstractTransformer $currency = $availableBudget->transactionCurrency; $data = [ - 'id' => (int)$availableBudget->id, + 'id' => (string)$availableBudget->id, 'created_at' => $availableBudget->created_at->toAtomString(), 'updated_at' => $availableBudget->updated_at->toAtomString(), - 'currency_id' => (int) $currency->id, + 'currency_id' => (string) $currency->id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, 'currency_decimal_places' => (int) $currency->decimal_places, diff --git a/app/Transformers/BillTransformer.php b/app/Transformers/BillTransformer.php index e09d4e93b8..1f5da82d73 100644 --- a/app/Transformers/BillTransformer.php +++ b/app/Transformers/BillTransformer.php @@ -81,7 +81,7 @@ class BillTransformer extends AbstractTransformer 'id' => (int)$bill->id, 'created_at' => $bill->created_at->toAtomString(), 'updated_at' => $bill->updated_at->toAtomString(), - 'currency_id' => (int) $bill->transaction_currency_id, + 'currency_id' => (string) $bill->transaction_currency_id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, 'currency_decimal_places' => (int) $currency->decimal_places, @@ -97,7 +97,7 @@ class BillTransformer extends AbstractTransformer 'next_expected_match' => $paidData['next_expected_match'], 'pay_dates' => $payDates, 'paid_dates' => $paidData['paid_dates'], - 'object_group_id' => $objectGroupId, + 'object_group_id' => $objectGroupId ? (string)$objectGroupId : null, 'object_group_order' => $objectGroupOrder, 'object_group_title' => $objectGroupTitle, 'links' => [ @@ -144,18 +144,13 @@ class BillTransformer extends AbstractTransformer */ protected function nextDateMatch(Bill $bill, Carbon $date): Carbon { - Log::debug(sprintf('Now in nextDateMatch(%d, %s)', $bill->id, $date->format('Y-m-d'))); + //Log::debug(sprintf('Now in nextDateMatch(%d, %s)', $bill->id, $date->format('Y-m-d'))); $start = clone $bill->date; - Log::debug(sprintf('Bill start date is %s', $start->format('Y-m-d'))); + //Log::debug(sprintf('Bill start date is %s', $start->format('Y-m-d'))); while ($start < $date) { - Log::debug( - sprintf( - '%s (bill start date) < %s (given date) so we jump ahead one period (with a skip maybe).', $start->format('Y-m-d'), $date->format('Y-m-d') - ) - ); $start = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip); } - Log::debug(sprintf('End of loop, bill start date is now %s', $start->format('Y-m-d'))); + //Log::debug(sprintf('End of loop, bill start date is now %s', $start->format('Y-m-d'))); return $start; } @@ -169,49 +164,49 @@ class BillTransformer extends AbstractTransformer */ protected function paidData(Bill $bill): array { - Log::debug(sprintf('Now in paidData for bill #%d', $bill->id)); + //Log::debug(sprintf('Now in paidData for bill #%d', $bill->id)); if (null === $this->parameters->get('start') || null === $this->parameters->get('end')) { - Log::debug('parameters are NULL, return empty array'); + // Log::debug('parameters are NULL, return empty array'); return [ 'paid_dates' => [], 'next_expected_match' => null, ]; } - Log::debug(sprintf('Parameters are start:%s end:%s', $this->parameters->get('start')->format('Y-m-d'), $this->parameters->get('end')->format('Y-m-d'))); + //Log::debug(sprintf('Parameters are start:%s end:%s', $this->parameters->get('start')->format('Y-m-d'), $this->parameters->get('end')->format('Y-m-d'))); /* * Get from database when bill was paid. */ $set = $this->repository->getPaidDatesInRange($bill, $this->parameters->get('start'), $this->parameters->get('end')); - Log::debug(sprintf('Count %d entries in getPaidDatesInRange()', $set->count())); + //Log::debug(sprintf('Count %d entries in getPaidDatesInRange()', $set->count())); /* * Grab from array the most recent payment. If none exist, fall back to the start date and pretend *that* was the last paid date. */ - Log::debug(sprintf('Grab last paid date from function, return %s if it comes up with nothing.', $this->parameters->get('start')->format('Y-m-d'))); + //Log::debug(sprintf('Grab last paid date from function, return %s if it comes up with nothing.', $this->parameters->get('start')->format('Y-m-d'))); $lastPaidDate = $this->lastPaidDate($set, $this->parameters->get('start')); - Log::debug(sprintf('Result of lastPaidDate is %s', $lastPaidDate->format('Y-m-d'))); + //Log::debug(sprintf('Result of lastPaidDate is %s', $lastPaidDate->format('Y-m-d'))); /* * The next expected match (nextMatch) is, initially, the bill's date. */ $nextMatch = clone $bill->date; - Log::debug(sprintf('Next match is %s (bill->date)', $nextMatch->format('Y-m-d'))); + //Log::debug(sprintf('Next match is %s (bill->date)', $nextMatch->format('Y-m-d'))); while ($nextMatch < $lastPaidDate) { /* * As long as this date is smaller than the last time the bill was paid, keep jumping ahead. * For example: 1 jan, 1 feb, etc. */ - Log::debug(sprintf('next match %s < last paid date %s, so add one period.', $nextMatch->format('Y-m-d'), $lastPaidDate->format('Y-m-d'))); + //Log::debug(sprintf('next match %s < last paid date %s, so add one period.', $nextMatch->format('Y-m-d'), $lastPaidDate->format('Y-m-d'))); $nextMatch = app('navigation')->addPeriod($nextMatch, $bill->repeat_freq, $bill->skip); - Log::debug(sprintf('Next match is now %s.', $nextMatch->format('Y-m-d'))); + //Log::debug(sprintf('Next match is now %s.', $nextMatch->format('Y-m-d'))); } if($nextMatch->isSameDay($lastPaidDate)) { /* * Add another period because its the same day as the last paid date. */ - Log::debug('Because the last paid date was on the same day as our next expected match, add another day.'); + //Log::debug('Because the last paid date was on the same day as our next expected match, add another day.'); $nextMatch = app('navigation')->addPeriod($nextMatch, $bill->repeat_freq, $bill->skip); } /* @@ -229,7 +224,7 @@ class BillTransformer extends AbstractTransformer 'paid_dates' => $result, 'next_expected_match' => $nextMatch->format('Y-m-d'), ]; - Log::debug('Result', $result); + //Log::debug('Result', $result); return $result; } @@ -241,40 +236,23 @@ class BillTransformer extends AbstractTransformer */ protected function payDates(Bill $bill): array { - Log::debug(sprintf('Now in payDates() for bill #%d', $bill->id)); + //Log::debug(sprintf('Now in payDates() for bill #%d', $bill->id)); if (null === $this->parameters->get('start') || null === $this->parameters->get('end')) { - Log::debug('No start or end date, give empty array.'); + //Log::debug('No start or end date, give empty array.'); return []; } - Log::debug( - sprintf( - 'Start date is %s, end is %s', $this->parameters->get('start')->format('Y-m-d'), - $this->parameters->get('end')->format('Y-m-d') - ) - ); $set = new Collection; $currentStart = clone $this->parameters->get('start'); $loop = 0; while ($currentStart <= $this->parameters->get('end')) { - Log::debug( - sprintf( - 'In loop #%d, where %s (start param) <= %s (end param).', $loop, $currentStart->format('Y-m-d'), - $this->parameters->get('end')->format('Y-m-d') - ) - ); $nextExpectedMatch = $this->nextDateMatch($bill, $currentStart); - Log::debug(sprintf('Next expected match is %s', $nextExpectedMatch->format('Y-m-d'))); // If nextExpectedMatch is after end, we continue: if ($nextExpectedMatch > $this->parameters->get('end')) { - Log::debug( - sprintf('%s is > %s, so were not going to use it.', $nextExpectedMatch->format('Y-m-d'), $this->parameters->get('end')->format('Y-m-d')) - ); break; } // add to set $set->push(clone $nextExpectedMatch); - Log::debug(sprintf('Add next expected match (%s) to set because its in the current start/end range, which now contains %d item(s)', $nextExpectedMatch->format('Y-m-d'), $set->count())); $nextExpectedMatch->addDay(); $currentStart = clone $nextExpectedMatch; $loop++; @@ -285,7 +263,6 @@ class BillTransformer extends AbstractTransformer } ); $array = $simple->toArray(); - Log::debug(sprintf('Loop has ended after %d loops', $loop), $array); return $array; } diff --git a/app/Transformers/BudgetLimitTransformer.php b/app/Transformers/BudgetLimitTransformer.php index 3183ac1066..0e81a6dc2a 100644 --- a/app/Transformers/BudgetLimitTransformer.php +++ b/app/Transformers/BudgetLimitTransformer.php @@ -84,20 +84,19 @@ class BudgetLimitTransformer extends AbstractTransformer $amount = number_format((float)$amount, $currencyDecimalPlaces, '.', ''); return [ - 'id' => (int)$budgetLimit->id, + 'id' => (string) $budgetLimit->id, 'created_at' => $budgetLimit->created_at->toAtomString(), 'updated_at' => $budgetLimit->updated_at->toAtomString(), 'start' => $budgetLimit->start_date->format('Y-m-d'), 'end' => $budgetLimit->end_date->format('Y-m-d'), - 'budget_id' => (int)$budgetLimit->budget_id, - 'currency_id' => $currencyId, + 'budget_id' => (string)$budgetLimit->budget_id, + 'currency_id' => (string) $currencyId, 'currency_code' => $currencyCode, 'currency_name' => $currencyName, - 'currency_decimal_places' => $currencyName, + 'currency_decimal_places' => $currencyDecimalPlaces, 'currency_symbol' => $currencySymbol, 'amount' => $amount, 'period' => $budgetLimit->period, - 'auto_budget' => $budgetLimit->auto_budget, 'spent' => $expenses[$currencyId]['sum'] ?? '0', 'links' => [ [ diff --git a/app/Transformers/BudgetTransformer.php b/app/Transformers/BudgetTransformer.php index cc0cd4705b..4b32c539ff 100644 --- a/app/Transformers/BudgetTransformer.php +++ b/app/Transformers/BudgetTransformer.php @@ -81,15 +81,14 @@ class BudgetTransformer extends AbstractTransformer ]; if (null !== $autoBudget) { - $abCurrencyId = (int)$autoBudget->transactionCurrency->id; + $abCurrencyId = (string)$autoBudget->transactionCurrency->id; $abCurrencyCode = $autoBudget->transactionCurrency->code; $abType = $types[$autoBudget->auto_budget_type]; $abAmount = number_format((float)$autoBudget->amount, $autoBudget->transactionCurrency->decimal_places, '.', ''); $abPeriod = $autoBudget->period; } - return [ - 'id' => (int)$budget->id, + 'id' => (string)$budget->id, 'created_at' => $budget->created_at->toAtomString(), 'updated_at' => $budget->updated_at->toAtomString(), 'active' => $budget->active, diff --git a/app/Transformers/ObjectGroupTransformer.php b/app/Transformers/ObjectGroupTransformer.php index 517b607303..7641fb341a 100644 --- a/app/Transformers/ObjectGroupTransformer.php +++ b/app/Transformers/ObjectGroupTransformer.php @@ -43,10 +43,6 @@ class ObjectGroupTransformer extends AbstractTransformer */ public function __construct() { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); - } - $this->repository = app(ObjectGroupRepositoryInterface::class); } @@ -62,15 +58,15 @@ class ObjectGroupTransformer extends AbstractTransformer $this->repository->setUser($objectGroup->user); return [ - 'id' => (int) $objectGroup->id, + 'id' => (string) $objectGroup->id, 'created_at' => $objectGroup->created_at->toAtomString(), 'updated_at' => $objectGroup->updated_at->toAtomString(), 'title' => $objectGroup->title, - 'order' => $objectGroup->order, + 'order' => (int) $objectGroup->order, 'links' => [ [ 'rel' => 'self', - 'uri' => '/groups/' . $objectGroup->id, + 'uri' => '/object_groups/' . $objectGroup->id, ], ], ]; diff --git a/app/Transformers/PiggyBankEventTransformer.php b/app/Transformers/PiggyBankEventTransformer.php index 2aac818f20..e34e4ae714 100644 --- a/app/Transformers/PiggyBankEventTransformer.php +++ b/app/Transformers/PiggyBankEventTransformer.php @@ -34,12 +34,9 @@ use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; */ class PiggyBankEventTransformer extends AbstractTransformer { - /** @var CurrencyRepositoryInterface */ - private $currencyRepos; - /** @var PiggyBankRepositoryInterface */ - private $piggyRepos; - /** @var AccountRepositoryInterface */ - private $repository; + private CurrencyRepositoryInterface $currencyRepos; + private PiggyBankRepositoryInterface $piggyRepos; + private AccountRepositoryInterface $repository; /** * PiggyBankEventTransformer constructor. @@ -76,21 +73,22 @@ class PiggyBankEventTransformer extends AbstractTransformer // get associated journal and transaction, if any: $journalId = $event->transaction_journal_id; $groupId = null; - if (0 !== (int) $journalId) { - $groupId = (int) $event->transactionJournal->transaction_group_id; - $journalId = (int) $journalId; + if (0 !== (int)$journalId) { + $groupId = (int)$event->transactionJournal->transaction_group_id; + $journalId = (int)$journalId; } + return [ - 'id' => (int) $event->id, + 'id' => (string)$event->id, 'created_at' => $event->created_at->toAtomString(), 'updated_at' => $event->updated_at->toAtomString(), - 'amount' => number_format((float) $event->amount, $currency->decimal_places, '.', ''), - 'currency_id' => (int) $currency->id, + 'amount' => number_format((float)$event->amount, $currency->decimal_places, '.', ''), + 'currency_id' => (string)$currency->id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => (int) $currency->decimal_places, - 'transaction_journal_id' => $journalId, - 'transaction_group_id' => $groupId, + 'currency_decimal_places' => (int)$currency->decimal_places, + 'transaction_journal_id' => $journalId ? (string)$journalId : null, + 'transaction_group_id' => $groupId ? (string)$groupId: null, 'links' => [ [ 'rel' => 'self', diff --git a/app/Transformers/PiggyBankTransformer.php b/app/Transformers/PiggyBankTransformer.php index c1f6cdc5cf..6f07503aa1 100644 --- a/app/Transformers/PiggyBankTransformer.php +++ b/app/Transformers/PiggyBankTransformer.php @@ -36,12 +36,9 @@ use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; */ class PiggyBankTransformer extends AbstractTransformer { - /** @var AccountRepositoryInterface */ - private $accountRepos; - /** @var CurrencyRepositoryInterface */ - private $currencyRepos; - /** @var PiggyBankRepositoryInterface */ - private $piggyRepos; + private AccountRepositoryInterface $accountRepos; + private CurrencyRepositoryInterface $currencyRepos; + private PiggyBankRepositoryInterface $piggyRepos; /** * PiggyBankTransformer constructor. @@ -105,13 +102,13 @@ class PiggyBankTransformer extends AbstractTransformer $targetAmount = 1 === bccomp('0.01', (string) $targetAmount) ? '0.01' : $targetAmount; $percentage = (int) (0 !== bccomp('0', $currentAmountStr) ? $currentAmountStr / $targetAmount * 100 : 0); return [ - 'id' => (int) $piggyBank->id, + 'id' => (string) $piggyBank->id, 'created_at' => $piggyBank->created_at->toAtomString(), 'updated_at' => $piggyBank->updated_at->toAtomString(), - 'account_id' => (int) $piggyBank->account_id, + 'account_id' => (string) $piggyBank->account_id, 'account_name' => $piggyBank->account->name, 'name' => $piggyBank->name, - 'currency_id' => (int) $currency->id, + 'currency_id' => (string) $currency->id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, 'currency_decimal_places' => (int) $currency->decimal_places, @@ -125,7 +122,7 @@ class PiggyBankTransformer extends AbstractTransformer 'order' => (int) $piggyBank->order, 'active' => true, 'notes' => $notes, - 'object_group_id' => $objectGroupId, + 'object_group_id' => $objectGroupId ? (string)$objectGroupId : null, 'object_group_order' => $objectGroupOrder, 'object_group_title' => $objectGroupTitle, 'links' => [ diff --git a/app/Transformers/PreferenceTransformer.php b/app/Transformers/PreferenceTransformer.php index 04d11310b3..89c6db70e8 100644 --- a/app/Transformers/PreferenceTransformer.php +++ b/app/Transformers/PreferenceTransformer.php @@ -32,19 +32,6 @@ use Log; */ class PreferenceTransformer extends AbstractTransformer { - - /** - * PreferenceTransformer constructor. - * - * @codeCoverageIgnore - */ - public function __construct() - { - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); - } - } - /** * Transform the preference * diff --git a/app/Transformers/RecurrenceTransformer.php b/app/Transformers/RecurrenceTransformer.php index 7ac33c0847..6f794758e9 100644 --- a/app/Transformers/RecurrenceTransformer.php +++ b/app/Transformers/RecurrenceTransformer.php @@ -42,14 +42,10 @@ use Log; */ class RecurrenceTransformer extends AbstractTransformer { - /** @var BudgetRepositoryInterface */ - private $budgetRepos; - /** @var CategoryFactory */ - private $factory; - /** @var PiggyBankRepositoryInterface */ - private $piggyRepos; - /** @var RecurringRepositoryInterface */ - private $repository; + private BudgetRepositoryInterface $budgetRepos; + private CategoryFactory $factory; + private PiggyBankRepositoryInterface $piggyRepos; + private RecurringRepositoryInterface $repository; /** * RecurrenceTransformer constructor. @@ -86,9 +82,10 @@ class RecurrenceTransformer extends AbstractTransformer $notes = $this->repository->getNoteText($recurrence); $reps = 0 === (int)$recurrence->repetitions ? null : (int)$recurrence->repetitions; Log::debug('Get basic data.'); + // basic data. return [ - 'id' => (int)$recurrence->id, + 'id' => (string)$recurrence->id, 'created_at' => $recurrence->created_at->toAtomString(), 'updated_at' => $recurrence->updated_at->toAtomString(), 'type' => $shortType, @@ -127,13 +124,13 @@ class RecurrenceTransformer extends AbstractTransformer /** @var RecurrenceRepetition $repetition */ foreach ($recurrence->recurrenceRepetitions as $repetition) { $repetitionArray = [ - 'id' => (int) $repetition->id, + 'id' => (string)$repetition->id, 'created_at' => $repetition->created_at->toAtomString(), 'updated_at' => $repetition->updated_at->toAtomString(), 'type' => $repetition->repetition_type, 'moment' => $repetition->repetition_moment, - 'skip' => (int) $repetition->repetition_skip, - 'weekend' => (int) $repetition->weekend, + 'skip' => (int)$repetition->repetition_skip, + 'weekend' => (int)$repetition->weekend, 'description' => $this->repository->repetitionDescription($repetition), 'occurrences' => [], ]; @@ -182,21 +179,21 @@ class RecurrenceTransformer extends AbstractTransformer case 'piggy_bank_id': $piggy = $this->piggyRepos->findNull((int)$transactionMeta->value); if (null !== $piggy) { - $array['piggy_bank_id'] = (int) $piggy->id; + $array['piggy_bank_id'] = (string)$piggy->id; $array['piggy_bank_name'] = $piggy->name; } break; case 'category_name': $category = $this->factory->findOrCreate(null, $transactionMeta->value); if (null !== $category) { - $array['category_id'] = (int) $category->id; + $array['category_id'] = (string)$category->id; $array['category_name'] = $category->name; } break; case 'budget_id': $budget = $this->budgetRepos->findNull((int)$transactionMeta->value); if (null !== $budget) { - $array['budget_id'] = (int) $budget->id; + $array['budget_id'] = (string)$budget->id; $array['budget_name'] = $budget->name; } break; @@ -227,10 +224,10 @@ class RecurrenceTransformer extends AbstractTransformer $foreignCurrencyDp = null; $foreignCurrencyId = null; if (null !== $transaction->foreign_currency_id) { - $foreignCurrencyId = (int) $transaction->foreign_currency_id; + $foreignCurrencyId = (int)$transaction->foreign_currency_id; $foreignCurrencyCode = $transaction->foreignCurrency->code; $foreignCurrencySymbol = $transaction->foreignCurrency->symbol; - $foreignCurrencyDp = (int) $transaction->foreignCurrency->decimal_places; + $foreignCurrencyDp = (int)$transaction->foreignCurrency->decimal_places; } // source info: @@ -240,7 +237,7 @@ class RecurrenceTransformer extends AbstractTransformer $sourceIban = null; if (null !== $sourceAccount) { $sourceName = $sourceAccount->name; - $sourceId = (int) $sourceAccount->id; + $sourceId = (int)$sourceAccount->id; $sourceType = $sourceAccount->accountType->type; $sourceIban = $sourceAccount->iban; } @@ -250,31 +247,31 @@ class RecurrenceTransformer extends AbstractTransformer $destinationIban = null; if (null !== $destinationAccount) { $destinationName = $destinationAccount->name; - $destinationId = (int) $destinationAccount->id; + $destinationId = (int)$destinationAccount->id; $destinationType = $destinationAccount->accountType->type; $destinationIban = $destinationAccount->iban; } - $amount = number_format((float) $transaction->amount, $transaction->transactionCurrency->decimal_places, '.', ''); + $amount = number_format((float)$transaction->amount, $transaction->transactionCurrency->decimal_places, '.', ''); $foreignAmount = null; if (null !== $transaction->foreign_currency_id && null !== $transaction->foreign_amount) { - $foreignAmount = number_format((float) $transaction->foreign_amount, $foreignCurrencyDp, '.', ''); + $foreignAmount = number_format((float)$transaction->foreign_amount, $foreignCurrencyDp, '.', ''); } $transactionArray = [ - 'currency_id' => (int) $transaction->transaction_currency_id, + 'currency_id' => (string)$transaction->transaction_currency_id, 'currency_code' => $transaction->transactionCurrency->code, 'currency_symbol' => $transaction->transactionCurrency->symbol, - 'currency_decimal_places' => (int) $transaction->transactionCurrency->decimal_places, - 'foreign_currency_id' => $foreignCurrencyId, + 'currency_decimal_places' => (int)$transaction->transactionCurrency->decimal_places, + 'foreign_currency_id' => null === $foreignCurrencyId ? null : (string)$foreignCurrencyId, 'foreign_currency_code' => $foreignCurrencyCode, 'foreign_currency_symbol' => $foreignCurrencySymbol, 'foreign_currency_decimal_places' => $foreignCurrencyDp, - 'source_id' => $sourceId, + 'source_id' => (string)$sourceId, 'source_name' => $sourceName, 'source_iban' => $sourceIban, 'source_type' => $sourceType, - 'destination_id' => $destinationId, + 'destination_id' => (string)$destinationId, 'destination_name' => $destinationName, 'destination_iban' => $destinationIban, 'destination_type' => $destinationType, diff --git a/app/Transformers/RuleTransformer.php b/app/Transformers/RuleTransformer.php index 69654099d6..c715d8570f 100644 --- a/app/Transformers/RuleTransformer.php +++ b/app/Transformers/RuleTransformer.php @@ -61,10 +61,10 @@ class RuleTransformer extends AbstractTransformer $this->ruleRepository->setUser($rule->user); return [ - 'id' => (int)$rule->id, + 'id' => (string)$rule->id, 'created_at' => $rule->created_at->toAtomString(), 'updated_at' => $rule->updated_at->toAtomString(), - 'rule_group_id' => (int)$rule->rule_group_id, + 'rule_group_id' => (string) $rule->rule_group_id, 'title' => $rule->title, 'description' => $rule->description, 'order' => (int)$rule->order, @@ -95,7 +95,7 @@ class RuleTransformer extends AbstractTransformer /** @var RuleAction $ruleAction */ foreach ($actions as $ruleAction) { $result[] = [ - 'id' => (int)$ruleAction->id, + 'id' => (string)$ruleAction->id, 'created_at' => $ruleAction->created_at->toAtomString(), 'updated_at' => $ruleAction->updated_at->toAtomString(), 'type' => $ruleAction->action_type, @@ -147,7 +147,7 @@ class RuleTransformer extends AbstractTransformer continue; } $result[] = [ - 'id' => (int)$ruleTrigger->id, + 'id' => (string)$ruleTrigger->id, 'created_at' => $ruleTrigger->created_at->toAtomString(), 'updated_at' => $ruleTrigger->updated_at->toAtomString(), 'type' => $ruleTrigger->trigger_type, diff --git a/app/Transformers/TagTransformer.php b/app/Transformers/TagTransformer.php index cf5ff6af2f..8f5c6114e3 100644 --- a/app/Transformers/TagTransformer.php +++ b/app/Transformers/TagTransformer.php @@ -53,7 +53,7 @@ class TagTransformer extends AbstractTransformer if (null !== $location) { $latitude = $location->latitude; $longitude = $location->longitude; - $zoomLevel = $location->zoom_level; + $zoomLevel = (int)$location->zoom_level; } return [ 'id' => (int)$tag->id, diff --git a/app/Transformers/TransactionGroupTransformer.php b/app/Transformers/TransactionGroupTransformer.php index 52eb1e5e66..92f3ebf5c6 100644 --- a/app/Transformers/TransactionGroupTransformer.php +++ b/app/Transformers/TransactionGroupTransformer.php @@ -27,6 +27,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Bill; use FireflyIII\Models\Budget; use FireflyIII\Models\Category; +use FireflyIII\Models\Location; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionGroup; @@ -70,13 +71,14 @@ class TransactionGroupTransformer extends AbstractTransformer */ public function transform(array $group): array { - $data = new NullArrayObject($group); - $first = new NullArrayObject(reset($group['transactions'])); + $data = new NullArrayObject($group); + $first = new NullArrayObject(reset($group['transactions'])); + return [ - 'id' => (int) $first['transaction_group_id'], + 'id' => (int)$first['transaction_group_id'], 'created_at' => $first['created_at']->toAtomString(), 'updated_at' => $first['updated_at']->toAtomString(), - 'user' => (int) $data['user_id'], + 'user' => (string)$data['user_id'], 'group_title' => $data['title'], 'transactions' => $this->transformTransactions($data), 'links' => [ @@ -98,10 +100,10 @@ class TransactionGroupTransformer extends AbstractTransformer { try { $result = [ - 'id' => (int) $group->id, + 'id' => (int)$group->id, 'created_at' => $group->created_at->toAtomString(), 'updated_at' => $group->updated_at->toAtomString(), - 'user' => (int) $group->user_id, + 'user' => (int)$group->user_id, 'group_title' => $group->title, 'transactions' => $this->transformJournals($group->transactionJournals), 'links' => [ @@ -152,7 +154,7 @@ class TransactionGroupTransformer extends AbstractTransformer if (null === $bill) { return $array; } - $array['id'] = (int) $bill->id; + $array['id'] = (string)$bill->id; $array['name'] = $bill->name; return $array; @@ -172,7 +174,7 @@ class TransactionGroupTransformer extends AbstractTransformer if (null === $budget) { return $array; } - $array['id'] = (int) $budget->id; + $array['id'] = (int)$budget->id; $array['name'] = $budget->name; return $array; @@ -192,7 +194,7 @@ class TransactionGroupTransformer extends AbstractTransformer if (null === $category) { return $array; } - $array['id'] = (int) $category->id; + $array['id'] = (int)$category->id; $array['name'] = $category->name; return $array; @@ -234,7 +236,7 @@ class TransactionGroupTransformer extends AbstractTransformer { $result = $journal->transactions->first( static function (Transaction $transaction) { - return (float) $transaction->amount > 0; + return (float)$transaction->amount > 0; } ); if (null === $result) { @@ -276,10 +278,10 @@ class TransactionGroupTransformer extends AbstractTransformer if (null === $currency) { return $array; } - $array['id'] = (int) $currency->id; + $array['id'] = (int)$currency->id; $array['code'] = $currency->code; $array['symbol'] = $currency->symbol; - $array['decimal_places'] = (int) $currency->decimal_places; + $array['decimal_places'] = (int)$currency->decimal_places; return $array; } @@ -294,7 +296,7 @@ class TransactionGroupTransformer extends AbstractTransformer { $result = $journal->transactions->first( static function (Transaction $transaction) { - return (float) $transaction->amount < 0; + return (float)$transaction->amount < 0; } ); if (null === $result) { @@ -326,37 +328,47 @@ class TransactionGroupTransformer extends AbstractTransformer $bill = $this->getBill($journal->bill); if (null !== $foreignAmount && null !== $foreignCurrency) { - $foreignAmount = number_format((float) $foreignAmount, $foreignCurrency['decimal_places'], '.', ''); + $foreignAmount = number_format((float)$foreignAmount, $foreignCurrency['decimal_places'], '.', ''); + } + + $longitude = null; + $latitude = null; + $zoomLevel = null; + $location = $this->getLocation($journal); + if (null !== $location) { + $longitude = $location->longitude; + $latitude = $location->latitude; + $zoomLevel = $location->zoom_level; } return [ - 'user' => (int) $journal->user_id, - 'transaction_journal_id' => (int) $journal->id, + 'user' => (int)$journal->user_id, + 'transaction_journal_id' => (int)$journal->id, 'type' => strtolower($type), 'date' => $journal->date->toAtomString(), 'order' => $journal->order, - 'currency_id' => (int) $currency->id, + 'currency_id' => (int)$currency->id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => (int) $currency->decimal_places, + 'currency_decimal_places' => (int)$currency->decimal_places, 'foreign_currency_id' => $foreignCurrency['id'], 'foreign_currency_code' => $foreignCurrency['code'], 'foreign_currency_symbol' => $foreignCurrency['symbol'], 'foreign_currency_decimal_places' => $foreignCurrency['decimal_places'], - 'amount' => number_format((float) $amount, $currency->decimal_places, '.', ''), + 'amount' => number_format((float)$amount, $currency->decimal_places, '.', ''), 'foreign_amount' => $foreignAmount, 'description' => $journal->description, - 'source_id' => (int) $source->account_id, + 'source_id' => (int)$source->account_id, 'source_name' => $source->account->name, 'source_iban' => $source->account->iban, 'source_type' => $source->account->accountType->type, - 'destination_id' => (int) $destination->account_id, + 'destination_id' => (int)$destination->account_id, 'destination_name' => $destination->account->name, 'destination_iban' => $destination->account->iban, 'destination_type' => $destination->account->accountType->type, @@ -396,6 +408,11 @@ class TransactionGroupTransformer extends AbstractTransformer 'due_date' => $metaDates['due_date'], 'payment_date' => $metaDates['payment_date'], 'invoice_date' => $metaDates['invoice_date'], + + // location data + 'longitude' => $longitude, + 'latitude' => $latitude, + 'zoom_level' => $zoomLevel, ]; } @@ -428,11 +445,13 @@ class TransactionGroupTransformer extends AbstractTransformer foreach ($transactions as $transaction) { $result[] = $this->transformTransaction($transaction); } + return $result; } /** * @param array $transaction + * * @return array */ private function transformTransaction(array $transaction): array @@ -447,23 +466,33 @@ class TransactionGroupTransformer extends AbstractTransformer $foreignAmount = app('steam')->positive($row['foreign_amount']); } - $metaFieldData = $this->groupRepos->getMetaFields((int) $row['transaction_journal_id'], $this->metaFields); - $metaDateData = $this->groupRepos->getMetaDateFields((int) $row['transaction_journal_id'], $this->metaDateFields); + $metaFieldData = $this->groupRepos->getMetaFields((int)$row['transaction_journal_id'], $this->metaFields); + $metaDateData = $this->groupRepos->getMetaDateFields((int)$row['transaction_journal_id'], $this->metaDateFields); + + $longitude = null; + $latitude = null; + $zoomLevel = null; + $location = $this->getLocationById((int)$row['transaction_journal_id']); + if (null !== $location) { + $longitude = $location->longitude; + $latitude = $location->latitude; + $zoomLevel = $location->zoom_level; + } return [ - 'user' => (int) $row['user_id'], - 'transaction_journal_id' => (int) $row['transaction_journal_id'], + 'user' => (string)$row['user_id'], + 'transaction_journal_id' => (int)$row['transaction_journal_id'], 'type' => strtolower($type), 'date' => $row['date']->toAtomString(), 'order' => $row['order'], - 'currency_id' => (int) $row['currency_id'], + 'currency_id' => (string)$row['currency_id'], 'currency_code' => $row['currency_code'], 'currency_name' => $row['currency_name'], 'currency_symbol' => $row['currency_symbol'], - 'currency_decimal_places' => (int) $row['currency_decimal_places'], + 'currency_decimal_places' => (int)$row['currency_decimal_places'], - 'foreign_currency_id' => $this->integerFromArray($transaction, 'foreign_currency_id'), + 'foreign_currency_id' => $this->stringFromArray($transaction, 'foreign_currency_id', null), 'foreign_currency_code' => $row['foreign_currency_code'], 'foreign_currency_symbol' => $row['foreign_currency_symbol'], 'foreign_currency_decimal_places' => $row['foreign_currency_decimal_places'], @@ -473,33 +502,33 @@ class TransactionGroupTransformer extends AbstractTransformer 'description' => $row['description'], - 'source_id' => (int) $row['source_account_id'], + 'source_id' => (string)$row['source_account_id'], 'source_name' => $row['source_account_name'], 'source_iban' => $row['source_account_iban'], 'source_type' => $row['source_account_type'], - 'destination_id' => (int) $row['destination_account_id'], + 'destination_id' => (string)$row['destination_account_id'], 'destination_name' => $row['destination_account_name'], 'destination_iban' => $row['destination_account_iban'], 'destination_type' => $row['destination_account_type'], - 'budget_id' => $this->integerFromArray($transaction, 'budget_id'), + 'budget_id' => $this->stringFromArray($transaction, 'budget_id', null), 'budget_name' => $row['budget_name'], - 'category_id' => $this->integerFromArray($transaction, 'category_id'), + 'category_id' => $this->stringFromArray($transaction, 'category_id', null), 'category_name' => $row['category_name'], - 'bill_id' => $this->integerFromArray($transaction, 'bill_id'), + 'bill_id' => $this->stringFromArray($transaction, 'bill_id', null), 'bill_name' => $row['bill_name'], 'reconciled' => $row['reconciled'], - 'notes' => $this->groupRepos->getNoteText((int) $row['transaction_journal_id']), - 'tags' => $this->groupRepos->getTags((int) $row['transaction_journal_id']), + 'notes' => $this->groupRepos->getNoteText((int)$row['transaction_journal_id']), + 'tags' => $this->groupRepos->getTags((int)$row['transaction_journal_id']), 'internal_reference' => $metaFieldData['internal_reference'], 'external_id' => $metaFieldData['external_id'], 'original_source' => $metaFieldData['original_source'], - 'recurrence_id' => $this->integerFromArray($metaFieldData->getArrayCopy(), 'recurrence_id'), + 'recurrence_id' => $this->stringFromArray($metaFieldData->getArrayCopy(), 'recurrence_id', null), 'recurrence_total' => $this->integerFromArray($metaFieldData->getArrayCopy(), 'recurrence_total'), 'recurrence_count' => $this->integerFromArray($metaFieldData->getArrayCopy(), 'recurrence_count'), 'bunq_payment_id' => $metaFieldData['bunq_payment_id'], @@ -509,7 +538,7 @@ class TransactionGroupTransformer extends AbstractTransformer 'sepa_cc' => $metaFieldData['sepa_cc'], 'sepa_ct_op' => $metaFieldData['sepa_ct_op'], 'sepa_ct_id' => $metaFieldData['sepa_ct_id'], - 'sepa_db' => $metaFieldData['sepa_ddb'], + 'sepa_db' => $metaFieldData['sepa_db'], 'sepa_country' => $metaFieldData['sepa_country'], 'sepa_ep' => $metaFieldData['sepa_ep'], 'sepa_ci' => $metaFieldData['sepa_ci'], @@ -521,6 +550,11 @@ class TransactionGroupTransformer extends AbstractTransformer 'due_date' => $this->dateFromArray($metaDateData, 'due_date'), 'payment_date' => $this->dateFromArray($metaDateData, 'payment_date'), 'invoice_date' => $this->dateFromArray($metaDateData, 'invoice_date'), + + // location data + 'longitude' => $longitude, + 'latitude' => $latitude, + 'zoom_level' => $zoomLevel, ]; } @@ -528,35 +562,44 @@ class TransactionGroupTransformer extends AbstractTransformer * @param array $array * @param string $key * @param string|null $default + * * @return string|null */ private function stringFromArray(array $array, string $key, ?string $default): ?string { - if (array_key_exists($key, $array)) { - return $array[$key]; + if (array_key_exists($key, $array) && null === $array[$key]) { + return null; } + if (array_key_exists($key, $array) && null !== $array[$key]) { + return (string) $array[$key]; + } + if (null !== $default) { - return $default; + return (string) $default; } + return null; } /** * @param array $array * @param string $key + * * @return int|null */ private function integerFromArray(array $array, string $key): ?int { if (array_key_exists($key, $array)) { - return (int) $array[$key]; + return (int)$array[$key]; } + return null; } /** * @param NullArrayObject $object * @param string $key + * * @return string|null */ private function dateFromArray(NullArrayObject $object, string $key): ?string @@ -567,4 +610,24 @@ class TransactionGroupTransformer extends AbstractTransformer return $object[$key]->toAtomString(); } + + /** + * @param TransactionJournal $journal + * + * @return Location|null + */ + private function getLocation(TransactionJournal $journal): ?Location + { + return $journal->locations()->first(); + } + + /** + * @param int $journalId + * + * @return Location|null + */ + private function getLocationById(int $journalId): ?Location + { + return $this->groupRepos->getLocation($journalId); + } } diff --git a/app/Transformers/TransactionLinkTransformer.php b/app/Transformers/TransactionLinkTransformer.php index c4c57e810e..7cdb4dd2ae 100644 --- a/app/Transformers/TransactionLinkTransformer.php +++ b/app/Transformers/TransactionLinkTransformer.php @@ -54,13 +54,14 @@ class TransactionLinkTransformer extends AbstractTransformer public function transform(TransactionJournalLink $link): array { $notes = $this->repository->getLinkNoteText($link); + return [ - 'id' => (int)$link->id, + 'id' => (string)$link->id, 'created_at' => $link->created_at->toAtomString(), 'updated_at' => $link->updated_at->toAtomString(), - 'inward_id' => (int) $link->source_id, - 'outward_id' => (int) $link->destination_id, - 'link_type_id' => (int) $link->link_type_id, + 'inward_id' => (string)$link->source_id, + 'outward_id' => (string)$link->destination_id, + 'link_type_id' => (string)$link->link_type_id, 'notes' => '' === $notes ? null : $notes, 'links' => [ [ diff --git a/app/Transformers/WebhookAttemptTransformer.php b/app/Transformers/WebhookAttemptTransformer.php new file mode 100644 index 0000000000..dd91501787 --- /dev/null +++ b/app/Transformers/WebhookAttemptTransformer.php @@ -0,0 +1,54 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Transformers; + + +use FireflyIII\Models\WebhookAttempt; + +/** + * Class WebhookAttemptTransformer + */ +class WebhookAttemptTransformer extends AbstractTransformer +{ + /** + * Transform the preference + * + * @param WebhookAttempt $attempt + * + * @return array + */ + public function transform(WebhookAttempt $attempt): array + { + return [ + 'id' => (string)$attempt->id, + 'created_at' => $attempt->created_at->toAtomString(), + 'updated_at' => $attempt->updated_at->toAtomString(), + 'webhook_message_id' => (string)$attempt->webhook_message_id, + 'status_code' => (int)$attempt->status_code, + 'logs' => $attempt->logs, + 'response' => $attempt->response, + ]; + } + +} diff --git a/app/Transformers/WebhookMessageTransformer.php b/app/Transformers/WebhookMessageTransformer.php new file mode 100644 index 0000000000..e4f25d938f --- /dev/null +++ b/app/Transformers/WebhookMessageTransformer.php @@ -0,0 +1,64 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Transformers; + +use FireflyIII\Models\WebhookMessage; +use Jsonexception; +use Log; + +/** + * Class WebhookMessageTransformer + */ +class WebhookMessageTransformer extends AbstractTransformer +{ + /** + * Transform the preference + * + * @param WebhookMessage $message + * + * @return array + */ + public function transform(WebhookMessage $message): array + { + + $json = '{}'; + try { + $json = json_encode($message->message, JSON_THROW_ON_ERROR); + } catch (JsonException $e) { + Log::error(sprintf('Could not encode webhook message #%d: %s', $message->id, $e->getMessage())); + } + + return [ + 'id' => (string)$message->id, + 'created_at' => $message->created_at->toAtomString(), + 'updated_at' => $message->updated_at->toAtomString(), + 'sent' => $message->sent, + 'errored' => $message->errored, + 'webhook_id' => (string)$message->webhook_id, + 'uuid' => $message->uuid, + 'message' => $json, + ]; + } + +} diff --git a/app/Transformers/WebhookTransformer.php b/app/Transformers/WebhookTransformer.php index 8ee39231e3..730fa61a89 100644 --- a/app/Transformers/WebhookTransformer.php +++ b/app/Transformers/WebhookTransformer.php @@ -1,8 +1,8 @@ . */ +declare(strict_types=1); + namespace FireflyIII\Transformers; use FireflyIII\Models\Webhook; diff --git a/app/Validation/Account/AccountValidatorProperties.php b/app/Validation/Account/AccountValidatorProperties.php index 1a1001746f..172fea5212 100644 --- a/app/Validation/Account/AccountValidatorProperties.php +++ b/app/Validation/Account/AccountValidatorProperties.php @@ -35,13 +35,5 @@ use FireflyIII\User; */ trait AccountValidatorProperties { - public bool $createMode; - public string $destError; - public Account $destination; - public Account $source; - public string $sourceError; - private AccountRepositoryInterface $accountRepository; - private array $combinations; - private string $transactionType; - private User $user; + } diff --git a/app/Validation/Account/WithdrawalValidation.php b/app/Validation/Account/WithdrawalValidation.php index 07c2e09f5b..6b41fb9c51 100644 --- a/app/Validation/Account/WithdrawalValidation.php +++ b/app/Validation/Account/WithdrawalValidation.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Validation\Account; use FireflyIII\Models\Account; +use FireflyIII\Models\AccountType; use Log; /** @@ -117,4 +118,38 @@ trait WithdrawalValidation return true; } + + /** + * @param int|null $accountId + * @param string|null $accountName + * + * @return bool + */ + protected function validateGenericSource(?int $accountId, ?string $accountName): bool + { + Log::debug(sprintf('Now in validateGenericSource(%d, "%s")', $accountId, $accountName)); + // source can be any of the following types. + $validTypes = [AccountType::ASSET, AccountType::REVENUE, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]; + if (null === $accountId && null === $accountName && false === $this->canCreateTypes($validTypes)) { + // if both values are NULL we return TRUE + // because we assume the user doesnt want to submit / change anything. + $this->sourceError = (string) trans('validation.withdrawal_source_need_data'); + Log::warning('Not a valid source. Need more data.'); + + return false; + } + + // otherwise try to find the account: + $search = $this->findExistingAccount($validTypes, (int) $accountId, (string) $accountName); + if (null === $search) { + $this->sourceError = (string) trans('validation.withdrawal_source_bad_data', ['id' => $accountId, 'name' => $accountName]); + Log::warning('Not a valid source. Cant find it.', $validTypes); + + return false; + } + $this->source = $search; + Log::debug('Valid source account!'); + + return true; + } } diff --git a/app/Validation/AccountValidator.php b/app/Validation/AccountValidator.php index 4af7069303..c544da6e8e 100644 --- a/app/Validation/AccountValidator.php +++ b/app/Validation/AccountValidator.php @@ -43,6 +43,16 @@ class AccountValidator { use AccountValidatorProperties, WithdrawalValidation, DepositValidation, TransferValidation, ReconciliationValidation, OBValidation; + public bool $createMode; + public string $destError; + public ?Account $destination; + public ?Account $source; + public string $sourceError; + private AccountRepositoryInterface $accountRepository; + private array $combinations; + private string $transactionType; + private User $user; + /** * AccountValidator constructor. */ @@ -52,13 +62,11 @@ class AccountValidator $this->destError = 'No error yet.'; $this->sourceError = 'No error yet.'; $this->combinations = config('firefly.source_dests'); + $this->source = null; + $this->destination = null; /** @var AccountRepositoryInterface accountRepository */ $this->accountRepository = app(AccountRepositoryInterface::class); - - if ('testing' === config('app.env')) { - Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); - } } /** @@ -66,7 +74,7 @@ class AccountValidator */ public function setTransactionType(string $transactionType): void { - Log::debug(sprintf('Transaction type for validator is now %s', ucfirst($transactionType))); + Log::debug(sprintf('Transaction type for validator is now "%s".', ucfirst($transactionType))); $this->transactionType = ucfirst($transactionType); } @@ -135,9 +143,8 @@ class AccountValidator Log::debug(sprintf('Now in AccountValidator::validateSource(%d, "%s", "%s")', $accountId, $accountName, $accountIban)); switch ($this->transactionType) { default: - $result = false; - $this->sourceError = trans('validation.invalid_account_info'); - Log::error(sprintf('AccountValidator::validateSource cannot handle "%s", so it will always return false.', $this->transactionType)); + Log::error(sprintf('AccountValidator::validateSource cannot handle "%s", so it will do a generic check.', $this->transactionType)); + $result = $this->validateGenericSource($accountId, $accountName); break; case TransactionType::WITHDRAWAL: $result = $this->validateWithdrawalSource($accountId, $accountName); @@ -197,8 +204,8 @@ class AccountValidator } /** - * @param array $validTypes - * @param int $accountId + * @param array $validTypes + * @param int $accountId * @param string $accountName * * @return Account|null @@ -221,5 +228,12 @@ class AccountValidator return null; } + /** + * @return Account|null + */ + public function getSource(): ?Account + { + return $this->source; + } } diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index 67de90d5ee..dda899058a 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -634,17 +634,33 @@ class FireflyValidator extends Validator public function validateUniqueExistingWebhook($value, $parameters, $something): bool { $existingId = (int)($something[0] ?? 0); - + $trigger = 0; + $response = 0; + $delivery = 0; + $triggers = array_flip(config('firefly.webhooks.triggers')); + $responses = array_flip(config('firefly.webhooks.responses')); + $deliveries = array_flip(config('firefly.webhooks.deliveries')); if (auth()->check()) { - // possible values - $triggers = array_flip(config('firefly.webhooks.triggers')); - $responses = array_flip(config('firefly.webhooks.responses')); - $deliveries = array_flip(config('firefly.webhooks.deliveries')); + // get existing webhook value: + if(0!== $existingId) { + /** @var Webhook $webhook */ + $webhook = auth()->user()->webhooks()->find($existingId); + if(null === $webhook) { + return false; + } + // set triggers etc. + $trigger = $triggers[$webhook->trigger] ?? 0; + $response = $responses[$webhook->response] ?? 0; + $delivery = $deliveries[$webhook->delivery] ?? 0; + } + if(0=== $existingId) { + $trigger = $triggers[$this->data['trigger']] ?? 0; + $response = $responses[$this->data['response']] ?? 0; + $delivery = $deliveries[$this->data['delivery']] ?? 0; + } + + - // integers - $trigger = $triggers[$this->data['trigger']] ?? 0; - $response = $responses[$this->data['response']] ?? 0; - $delivery = $deliveries[$this->data['delivery']] ?? 0; $url = $this->data['url']; $userId = auth()->user()->id; diff --git a/app/Validation/GroupValidation.php b/app/Validation/GroupValidation.php index 5cbc109b99..0b6013bd40 100644 --- a/app/Validation/GroupValidation.php +++ b/app/Validation/GroupValidation.php @@ -57,6 +57,7 @@ trait GroupValidation if (count($transactions) < 2) { // no need for validation. + Log::debug(sprintf('%d transaction(s) in submission, can skip this check.', count($transactions))); return; } // check each array: @@ -104,12 +105,15 @@ trait GroupValidation * * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - private function validateJournalid(Validator $validator, int $index, array $transaction, TransactionGroup $transactionGroup): void + private function validateJournalId(Validator $validator, int $index, array $transaction, TransactionGroup $transactionGroup): void { $journalId = $transaction['transaction_journal_id'] ?? null; + Log::debug(sprintf('Now in validateJournalId(%d, %d)', $index, $journalId)); + $journalId = null === $journalId ? null : (int) $journalId; $count = $transactionGroup->transactionJournals()->where('id', $journalId)->count(); if (null === $journalId || (null !== $journalId && 0 !== $journalId && 0 === $count)) { + Log::warning('Invalid submission: Each split must have transaction_journal_id (either valid ID or 0).'); $validator->errors()->add(sprintf('transactions.%d.source_name', $index), (string) trans('validation.need_id_in_edit')); } } diff --git a/app/Validation/RecurrenceValidation.php b/app/Validation/RecurrenceValidation.php index 83d6b8fd79..a58d4d2033 100644 --- a/app/Validation/RecurrenceValidation.php +++ b/app/Validation/RecurrenceValidation.php @@ -36,7 +36,22 @@ use Log; */ trait RecurrenceValidation { + public function validateRecurringConfig(Validator $validator) { + $data = $validator->getData(); + $reps = array_key_exists('nr_of_repetitions', $data) ? (int)$data['nr_of_repetitions'] : null; + $repeatUntil = array_key_exists('repeat_until', $data) ? new Carbon($data['repeat_until']) : null; + if(null === $reps && null === $repeatUntil) { + $validator->errors()->add('nr_of_repetitions', trans('validation.require_repeat_until')); + $validator->errors()->add('repeat_until', trans('validation.require_repeat_until')); + return; + } + if($reps > 0 && null !== $repeatUntil) { + $validator->errors()->add('nr_of_repetitions', trans('validation.require_repeat_until')); + $validator->errors()->add('repeat_until', trans('validation.require_repeat_until')); + return; + } + } /** * Validate account information input for recurrences which are being updated. @@ -96,7 +111,7 @@ trait RecurrenceValidation $data = $validator->getData(); $repetitions = $data['repetitions'] ?? []; // need at least one transaction - if (0 === count($repetitions)) { + if (!is_countable($repetitions) || 0 === count($repetitions)) { $validator->errors()->add('repetitions', (string)trans('validation.at_least_one_repetition')); } } @@ -144,11 +159,23 @@ trait RecurrenceValidation { $data = $validator->getData(); $repetitions = $data['repetitions'] ?? []; + if (!is_array($repetitions)) { + $validator->errors()->add(sprintf('repetitions.%d.type', 0), (string)trans('validation.valid_recurrence_rep_type')); + + return; + } /** * @var int $index * @var array $repetition */ foreach ($repetitions as $index => $repetition) { + if (!array_key_exists('moment', $repetition)) { + continue; + } + if (null === $repetition['moment']) { + $repetition['moment'] = ''; + } + $repetition['moment'] = $repetition['moment'] ?? 'invalid'; switch ($repetition['type'] ?? 'empty') { default: $validator->errors()->add(sprintf('repetitions.%d.type', $index), (string)trans('validation.valid_recurrence_rep_type')); diff --git a/app/Validation/TransactionValidation.php b/app/Validation/TransactionValidation.php index 6b7d3e2394..28010f45f6 100644 --- a/app/Validation/TransactionValidation.php +++ b/app/Validation/TransactionValidation.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Validation; +use FireflyIII\Models\Account; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; @@ -53,7 +54,7 @@ trait TransactionValidation * @var array $transaction */ foreach ($transactions as $index => $transaction) { - if(!is_int($index)) { + if (!is_int($index)) { continue; } $this->validateSingleAccount($validator, $index, $transactionType, $transaction); @@ -75,9 +76,9 @@ trait TransactionValidation $accountValidator->setTransactionType($transactionType); // validate source account. - $sourceId = isset($transaction['source_id']) ? (int) $transaction['source_id'] : null; - $sourceName = isset($transaction['source_name']) ? (string) $transaction['source_name'] : null; - $sourceIban = isset($transaction['source_iban']) ? (string) $transaction['source_iban'] : null; + $sourceId = isset($transaction['source_id']) ? (int)$transaction['source_id'] : null; + $sourceName = isset($transaction['source_name']) ? (string)$transaction['source_name'] : null; + $sourceIban = isset($transaction['source_iban']) ? (string)$transaction['source_iban'] : null; $validSource = $accountValidator->validateSource($sourceId, $sourceName, $sourceIban); // do something with result: @@ -88,9 +89,9 @@ trait TransactionValidation return; } // validate destination account - $destinationId = isset($transaction['destination_id']) ? (int) $transaction['destination_id'] : null; - $destinationName = isset($transaction['destination_name']) ? (string) $transaction['destination_name'] : null; - $destinationIban = isset($transaction['destination_iban']) ? (string) $transaction['destination_iban'] : null; + $destinationId = isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null; + $destinationName = isset($transaction['destination_name']) ? (string)$transaction['destination_name'] : null; + $destinationIban = isset($transaction['destination_iban']) ? (string)$transaction['destination_iban'] : null; $validDestination = $accountValidator->validateDestination($destinationId, $destinationName, $destinationIban); // do something with result: if (false === $validDestination) { @@ -102,9 +103,10 @@ trait TransactionValidation /** * Validates the given account information. Switches on given transaction type. * - * @param Validator $validator + * @param Validator $validator + * @param TransactionGroup $transactionGroup */ - public function validateAccountInformationUpdate(Validator $validator): void + public function validateAccountInformationUpdate(Validator $validator, TransactionGroup $transactionGroup): void { Log::debug('Now in validateAccountInformationUpdate()'); $transactions = $this->getTransactionsArray($validator); @@ -114,53 +116,81 @@ trait TransactionValidation * @var array $transaction */ foreach ($transactions as $index => $transaction) { - $this->validateSingleUpdate($validator, $index, $transaction); + $this->validateSingleUpdate($validator, $index, $transaction, $transactionGroup); } } /** - * @param Validator $validator - * @param int $index - * @param array $transaction + * @param Validator $validator + * @param int $index + * @param array $transaction + * @param TransactionGroup $transactionGroup */ - protected function validateSingleUpdate(Validator $validator, int $index, array $transaction): void + protected function validateSingleUpdate(Validator $validator, int $index, array $transaction, TransactionGroup $transactionGroup): void { - /** @var AccountValidator $accountValidator */ - $accountValidator = app(AccountValidator::class); - $originalType = $this->getOriginalType((int) ($transaction['transaction_journal_id'] ?? 0)); - $originalData = $this->getOriginalData((int) ($transaction['transaction_journal_id'] ?? 0)); - $transactionType = $transaction['type'] ?? $originalType; - $accountValidator->setTransactionType($transactionType); + Log::debug('Now validating single account update in validateSingleUpdate()'); // if no account types are given, just skip the check. if (!isset($transaction['source_id']) && !isset($transaction['source_name']) && !isset($transaction['destination_id']) && !isset($transaction['destination_name'])) { - return; - } - - // validate source account. - $sourceId = isset($transaction['source_id']) ? (int) $transaction['source_id'] : $originalData['source_id']; - $sourceName = $transaction['source_name'] ?? $originalData['source_name']; - $validSource = $accountValidator->validateSource($sourceId, $sourceName, null); - - // do something with result: - if (false === $validSource) { - $validator->errors()->add(sprintf('transactions.%d.source_id', $index), $accountValidator->sourceError); - $validator->errors()->add(sprintf('transactions.%d.source_name', $index), $accountValidator->sourceError); + Log::debug('No account data has been submitted so will not validating account info.'); return; } - // validate destination account - $destinationId = isset($transaction['destination_id']) ? (int) $transaction['destination_id'] : $originalData['destination_id']; - $destinationName = $transaction['destination_name'] ?? $originalData['destination_name']; - $validDestination = $accountValidator->validateDestination($destinationId, $destinationName, null); - // do something with result: - if (false === $validDestination) { - $validator->errors()->add(sprintf('transactions.%d.destination_id', $index), $accountValidator->destError); - $validator->errors()->add(sprintf('transactions.%d.destination_name', $index), $accountValidator->destError); + // create validator: + /** @var AccountValidator $accountValidator */ + $accountValidator = app(AccountValidator::class); + + // get the transaction type using the original transaction group: + $accountValidator->setTransactionType($this->getTransactionType($transactionGroup, [])); + + // validate if the submitted source and / or name are valid + if (array_key_exists('source_id', $transaction) || array_key_exists('source_name', $transaction)) { + Log::debug('Will try to validate source account information.'); + $sourceId = (int)($transaction['source_id'] ?? 0); + $sourceName = $transaction['source_name'] ?? null; + $validSource = $accountValidator->validateSource($sourceId, $sourceName, null); + + // do something with result: + if (false === $validSource) { + Log::warning('Looks like the source account is not valid so complain to the user about it.'); + $validator->errors()->add(sprintf('transactions.%d.source_id', $index), $accountValidator->sourceError); + $validator->errors()->add(sprintf('transactions.%d.source_name', $index), $accountValidator->sourceError); + + return; + } + Log::debug('Source account info is valid.'); } + + if (array_key_exists('destination_id', $transaction) || array_key_exists('destination_name', $transaction)) { + Log::debug('Will try to validate destination account information.'); + // at this point the validator may not have a source account, because it was never submitted for validation. + // must add it ourselves or the validator can never check if the destination is correct. + // the $transaction array must have a journal id or it's just one, this was validated before. + if (null === $accountValidator->source) { + Log::debug('Account validator has no source account, must find it.'); + $source = $this->getOriginalSource($transaction, $transactionGroup); + if (null !== $source) { + Log::debug('Found a source!'); + $accountValidator->source = $source; + } + } + + + $destinationId = (int)($transaction['destination_id'] ?? 0); + $destinationName = $transaction['destination_name'] ?? null; + $validDestination = $accountValidator->validateDestination($destinationId, $destinationName, null); + // do something with result: + if (false === $validDestination) { + Log::warning('Looks like the destination account is not valid so complain to the user about it.'); + $validator->errors()->add(sprintf('transactions.%d.destination_id', $index), $accountValidator->destError); + $validator->errors()->add(sprintf('transactions.%d.destination_name', $index), $accountValidator->destError); + } + Log::debug('Destination account info is valid.'); + } + Log::debug('Done with validateSingleUpdate().'); } /** @@ -175,19 +205,21 @@ trait TransactionValidation // need at least one transaction if (0 === count($transactions)) { - $validator->errors()->add('transactions', (string) trans('validation.at_least_one_transaction')); + $validator->errors()->add('transactions', (string)trans('validation.at_least_one_transaction')); } } /** * @param Validator $validator */ - public function validateTransactionArray(Validator $validator): void { + public function validateTransactionArray(Validator $validator): void + { $transactions = $this->getTransactionsArray($validator); - foreach($transactions as $key => $value) { - if(!is_int($key)) { - $validator->errors()->add('transactions.0.description', (string) trans('validation.at_least_one_transaction')); + foreach ($transactions as $key => $value) { + if (!is_int($key)) { + $validator->errors()->add('transactions.0.description', (string)trans('validation.at_least_one_transaction')); Log::debug('Added error: at_least_one_transaction.'); + return; } } @@ -204,8 +236,9 @@ trait TransactionValidation $transactions = $this->getTransactionsArray($validator); // need at least one transaction if (0 === count($transactions)) { - $validator->errors()->add('transactions.0.description', (string) trans('validation.at_least_one_transaction')); + $validator->errors()->add('transactions.0.description', (string)trans('validation.at_least_one_transaction')); Log::debug('Added error: at_least_one_transaction.'); + return; } Log::debug('Added NO errors.'); @@ -227,35 +260,40 @@ trait TransactionValidation } $unique = array_unique($types); if (count($unique) > 1) { - $validator->errors()->add('transactions.0.type', (string) trans('validation.transaction_types_equal')); + $validator->errors()->add('transactions.0.type', (string)trans('validation.transaction_types_equal')); return; } $first = $unique[0] ?? 'invalid'; if ('invalid' === $first) { - $validator->errors()->add('transactions.0.type', (string) trans('validation.invalid_transaction_type')); + $validator->errors()->add('transactions.0.type', (string)trans('validation.invalid_transaction_type')); } } /** * All types of splits must be equal. * - * @param Validator $validator + * @param Validator $validator + * @param TransactionGroup $transactionGroup */ - public function validateTransactionTypesForUpdate(Validator $validator): void + public function validateTransactionTypesForUpdate(Validator $validator, TransactionGroup $transactionGroup): void { Log::debug('Now in validateTransactionTypesForUpdate()'); $transactions = $this->getTransactionsArray($validator); $types = []; foreach ($transactions as $transaction) { - $originalType = $this->getOriginalType((int) ($transaction['transaction_journal_id'] ?? 0)); + $originalType = $this->getOriginalType((int)($transaction['transaction_journal_id'] ?? 0)); // if type is not set, fall back to the type of the journal, if one is given. $types[] = $transaction['type'] ?? $originalType; } $unique = array_unique($types); if (count($unique) > 1) { - $validator->errors()->add('transactions.0.type', (string) trans('validation.transaction_types_equal')); + Log::warning('Add error for mismatch transaction types.'); + $validator->errors()->add('transactions.0.type', (string)trans('validation.transaction_types_equal')); + + return; } + Log::debug('No errors in validateTransactionTypesForUpdate()'); } /** @@ -368,18 +406,18 @@ trait TransactionValidation default: case 'withdrawal': if (count($sources) > 1) { - $validator->errors()->add('transactions.0.source_id', (string) trans('validation.all_accounts_equal')); + $validator->errors()->add('transactions.0.source_id', (string)trans('validation.all_accounts_equal')); } break; case 'deposit': if (count($dests) > 1) { - $validator->errors()->add('transactions.0.destination_id', (string) trans('validation.all_accounts_equal')); + $validator->errors()->add('transactions.0.destination_id', (string)trans('validation.all_accounts_equal')); } break; case'transfer': if (count($sources) > 1 || count($dests) > 1) { - $validator->errors()->add('transactions.0.source_id', (string) trans('validation.all_accounts_equal')); - $validator->errors()->add('transactions.0.destination_id', (string) trans('validation.all_accounts_equal')); + $validator->errors()->add('transactions.0.source_id', (string)trans('validation.all_accounts_equal')); + $validator->errors()->add('transactions.0.destination_id', (string)trans('validation.all_accounts_equal')); } break; } @@ -388,6 +426,7 @@ trait TransactionValidation /** * @param TransactionGroup $group * @param array $transactions + * * @return string */ private function getTransactionType(TransactionGroup $group, array $transactions): string @@ -397,6 +436,7 @@ trait TransactionValidation /** * @param array $transactions + * * @return array */ private function collectComparisonData(array $transactions): array @@ -408,18 +448,20 @@ trait TransactionValidation /** @var array $transaction */ foreach ($transactions as $transaction) { // source or destination may be omitted. If this is the case, use the original source / destination name + ID. - $originalData = $this->getOriginalData((int) ($transaction['transaction_journal_id'] ?? 0)); + $originalData = $this->getOriginalData((int)($transaction['transaction_journal_id'] ?? 0)); // get field. $comparison[$field][] = $transaction[$field] ?? $originalData[$field]; } } + return $comparison; } /** * @param string $type * @param array $comparison + * * @return bool */ private function compareAccountData(string $type, array $comparison): bool @@ -437,6 +479,7 @@ trait TransactionValidation /** * @param array $comparison + * * @return bool */ private function compareAccountDataTransfer(array $comparison): bool @@ -457,11 +500,13 @@ trait TransactionValidation // destination names are equal, return void. return true; } + return false; } /** * @param array $comparison + * * @return bool */ private function compareAccountDataWithdrawal(array $comparison): bool @@ -474,11 +519,13 @@ trait TransactionValidation // source names are equal, return void. return true; } + return false; } /** * @param array $comparison + * * @return bool */ private function compareAccountDataDeposit(array $comparison): bool @@ -491,6 +538,7 @@ trait TransactionValidation // destination names are equal, return void. return true; } + return false; } @@ -504,6 +552,8 @@ trait TransactionValidation $transactions = $this->getTransactionsArray($validator); if (2 !== count($transactions)) { + Log::debug('Less than 2 transactions, do nothing.'); + return; } $type = $this->getTransactionType($transactionGroup, $transactions); @@ -517,15 +567,42 @@ trait TransactionValidation $result = $this->compareAccountData($type, $comparison); if (false === $result) { if ('withdrawal' === $type) { - $validator->errors()->add('transactions.0.source_id', (string) trans('validation.all_accounts_equal')); + $validator->errors()->add('transactions.0.source_id', (string)trans('validation.all_accounts_equal')); } if ('deposit' === $type) { - $validator->errors()->add('transactions.0.destination_id', (string) trans('validation.all_accounts_equal')); + $validator->errors()->add('transactions.0.destination_id', (string)trans('validation.all_accounts_equal')); } if ('transfer' === $type) { - $validator->errors()->add('transactions.0.source_id', (string) trans('validation.all_accounts_equal')); - $validator->errors()->add('transactions.0.destination_id', (string) trans('validation.all_accounts_equal')); + $validator->errors()->add('transactions.0.source_id', (string)trans('validation.all_accounts_equal')); + $validator->errors()->add('transactions.0.destination_id', (string)trans('validation.all_accounts_equal')); + } + Log::warning('Add error about equal accounts.'); + + return; + } + Log::debug('No errors found in validateEqualAccountsForUpdate'); + } + + /** + * @param array $transaction + * @param TransactionGroup $transactionGroup + * + * @return Account|null + */ + private function getOriginalSource(array $transaction, TransactionGroup $transactionGroup): ?Account + { + if (1 === $transactionGroup->transactionJournals->count()) { + $journal = $transactionGroup->transactionJournals->first(); + + return $journal->transactions()->where('amount', '<', 0)->first()->account; + } + /** @var TransactionJournal $journal */ + foreach ($transactionGroup->transactionJournals as $journal) { + if ((int)$journal->id === (int)$transaction['transaction_journal_id']) { + return $journal->transactions()->where('amount', '<', 0)->first()->account; } } + + return null; } } diff --git a/bootstrap/app.php b/bootstrap/app.php index 54e123593d..f7a7e500df 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -21,6 +21,7 @@ declare(strict_types=1); +use Illuminate\Contracts\View\Factory as ViewFactory; /* |-------------------------------------------------------------------------- | Create The Application @@ -56,6 +57,7 @@ if (!function_exists('str_is_equal')) { /** * @param string $left * @param string $right + * * @return bool */ function str_is_equal(string $left, string $right): bool @@ -64,6 +66,43 @@ if (!function_exists('str_is_equal')) { } } +if (!function_exists('prefixView')) { + /** + * Get the evaluated view contents for the given view. + * + * @param string|null $view + * @param \Illuminate\Contracts\Support\Arrayable|array $data + * @param array $mergeData + * + * @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory + */ + function prefixView($view = null, $data = [], $mergeData = []) + { + $factory = app(ViewFactory::class); + + if (func_num_args() === 0) { + return $factory; + } + // original view: + $prefixView = $view; + + // try to find the view file first: + if(!$factory->exists($prefixView)) { + // prepend it with the view in the layout: + $layout = env('FIREFLY_III_LAYOUT', 'v1'); + $prefixView = sprintf('%s.%s', $layout, $view); + + // try again: + if(!$factory->exists($prefixView)) { + // if does not exist, force v1 and just continue. + $prefixView = sprintf('%s.%s', 'v1', $view); + } + } + + return $factory->make($prefixView, $data, $mergeData); + } +} + $app = new Illuminate\Foundation\Application( realpath(__DIR__ . '/../') ); diff --git a/changelog.md b/changelog.md index 5c5757aefd..ba3a246a86 100644 --- a/changelog.md +++ b/changelog.md @@ -7,12 +7,25 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## 5.5.0 (API 1.5.0) 2021-xx-xx ### Added -- [Webhooks](https://docs.firefly-iii.org/firefly-iii/pages-and-features/webhooks/) -- [Issue 3717](https://github.com/firefly-iii/firefly-iii/issues/3717) Allow exporting in the CSV all the optional metadata -- Can search for transactions using `id:123`. +- [Issue 3717](https://github.com/firefly-iii/firefly-iii/issues/3717) Also export all optional metadata in the CSV files. +- [Issue 4007](https://github.com/firefly-iii/firefly-iii/issues/4007) Whe updating transactions using a rule, the message will return the number of changed transactions. +- [Issue 4334](https://github.com/firefly-iii/firefly-iii/issues/4334) Added the Portuguese language. +- [Issue 4338](https://github.com/firefly-iii/firefly-iii/issues/4338) The recurring transactions calendar was off by one day. +- [Issue 4339](https://github.com/firefly-iii/firefly-iii/issues/4339) When deleting recurring transactions you would be redirected to the deleted recurring transaction. +- [Issue 4340](https://github.com/firefly-iii/firefly-iii/issues/4340) When running rules, any date related actions and triggers will pick up the correct date. +- [Issue 4406](https://github.com/firefly-iii/firefly-iii/issues/4406) SQL errors when submitting large amounts to the budget overview. +- [Issue 4412](https://github.com/firefly-iii/firefly-iii/issues/4412) During the cron job a NULL pointer could pop up. +- [Issue 4488](https://github.com/firefly-iii/firefly-iii/issues/4488) The Japanese Yen was corrected to zero decimals. +- [Issue 4503](https://github.com/firefly-iii/firefly-iii/issues/4503) When bills skip a moment the amounts would be off. +- Firefly III now supports [webhooks](https://docs.firefly-iii.org/firefly-iii/pages-and-features/webhooks/). +- The search now also supports searching for transactions using `id:123`. ### Changed -- OAuth is visible for LDAP users. +- OAuth settings are visible for LDAP users. +- If you set `FIREFLY_III_LAYOUT=v2`, Firefly III will show you the new layout on pages where it's available. + +### Deprecated +- The current layout will no longer receive fixes and changes. ### Fixed - [Issue 4045](https://github.com/firefly-iii/firefly-iii/issues/4045) Error message for "Amount Missing" doesn't have a look up value @@ -37,15 +50,17 @@ This project adheres to [Semantic Versioning](http://semver.org/). - PHP configs that have "MB" as size indicator would be parsed badly. ### API + +*Lots of API changes, make sure you read [the documentation](https://api-docs.firefly-iii.org/).* + - [Issue 4050](https://github.com/firefly-iii/firefly-iii/issues/4050) Updated Transaction Search API to set limit from user preferences - [Issue 4113](https://github.com/firefly-iii/firefly-iii/issues/4113) Piggy Bank API Deletes Some Piggy Metadata - [Issue 4122](https://github.com/firefly-iii/firefly-iii/issues/4122) Remove reconciliation accounts from autocomplete - [Issue 4195](https://github.com/firefly-iii/firefly-iii/issues/4195) User endpoint broken - [Issue 4199](https://github.com/firefly-iii/firefly-iii/issues/4199) Unable to update tags using API -- API endpoint for budget limits applicable to a date range now has budget info. -- Add period and auto generated to budget limit -- Add spent to budget limit. - +- [Issue 4394](https://github.com/firefly-iii/firefly-iii/issues/4394) Storing budgets works again. +- [Issue 4426](https://github.com/firefly-iii/firefly-iii/issues/4426) Storing accounts would lead to bad capitalization in liability type. +- [Issue 4435](https://github.com/firefly-iii/firefly-iii/issues/4435) Storing piggy banks with object group information would fail. ## 5.4.6 (API 1.4.0) - 2020-10-07 diff --git a/composer.json b/composer.json index ac51d9d8a8..c324c9f230 100644 --- a/composer.json +++ b/composer.json @@ -100,7 +100,8 @@ "pragmarx/google2fa": "^8.0", "predis/predis": "^1.1", "ramsey/uuid": "^4.1", - "rcrowe/twigbridge": "^0.12.1" + "rcrowe/twigbridge": "^0.12.1", + "spatie/data-transfer-object": "^2.8" }, "require-dev": { "barryvdh/laravel-debugbar": "^3.3", @@ -108,9 +109,8 @@ "ergebnis/phpstan-rules": "^0.15.0", "filp/whoops": "2.*", "fakerphp/faker": "1.*", - "johnkary/phpunit-speedtrap": "^3.1", "mockery/mockery": "1.*", - "nunomaduro/larastan": "^0.6.2", + "nunomaduro/larastan": "^0.7.0", "phpstan/phpstan": "^0.12.34", "phpstan/phpstan-deprecation-rules": "^0.12.5", "phpunit/phpunit": "^9.2", @@ -125,6 +125,7 @@ "autoload": { "psr-4": { "FireflyIII\\": "app/", + "Domain\\": "domain/", "Database\\Factories\\": "database/factories/", "Database\\Seeders\\": "database/seeders/" } @@ -170,6 +171,7 @@ "@php artisan firefly-iii:rename-account-meta", "@php artisan firefly-iii:migrate-recurrence-meta", "@php artisan firefly-iii:migrate-tag-locations", + "@php artisan firefly-iii:migrate-recurrence-type", "@php artisan firefly-iii:fix-piggies", "@php artisan firefly-iii:create-link-types", diff --git a/composer.lock b/composer.lock index ed373885ce..131f865731 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7953995996e0ce075775b75313194cf6", + "content-hash": "897bc804b3a43ff012021d9a363d9e53", "packages": [ { "name": "bacon/bacon-qr-code", @@ -1095,16 +1095,16 @@ }, { "name": "firebase/php-jwt", - "version": "v5.2.0", + "version": "v5.2.1", "source": { "type": "git", "url": "https://github.com/firebase/php-jwt.git", - "reference": "feb0e820b8436873675fd3aca04f3728eb2185cb" + "reference": "f42c9110abe98dd6cfe9053c49bc86acc70b2d23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/firebase/php-jwt/zipball/feb0e820b8436873675fd3aca04f3728eb2185cb", - "reference": "feb0e820b8436873675fd3aca04f3728eb2185cb", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/f42c9110abe98dd6cfe9053c49bc86acc70b2d23", + "reference": "f42c9110abe98dd6cfe9053c49bc86acc70b2d23", "shasum": "" }, "require": { @@ -1143,9 +1143,9 @@ ], "support": { "issues": "https://github.com/firebase/php-jwt/issues", - "source": "https://github.com/firebase/php-jwt/tree/master" + "source": "https://github.com/firebase/php-jwt/tree/v5.2.1" }, - "time": "2020-03-25T18:49:23+00:00" + "time": "2021-02-12T00:02:00+00:00" }, { "name": "gdbots/query-parser", @@ -1356,16 +1356,16 @@ }, { "name": "guzzlehttp/promises", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "60d379c243457e073cff02bc323a2a86cb355631" + "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/60d379c243457e073cff02bc323a2a86cb355631", - "reference": "60d379c243457e073cff02bc323a2a86cb355631", + "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d", + "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d", "shasum": "" }, "require": { @@ -1405,9 +1405,9 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.4.0" + "source": "https://github.com/guzzle/promises/tree/1.4.1" }, - "time": "2020-09-30T07:37:28+00:00" + "time": "2021-03-07T09:25:29+00:00" }, { "name": "guzzlehttp/psr7", @@ -1641,16 +1641,16 @@ }, { "name": "laravel/framework", - "version": "v8.25.0", + "version": "v8.32.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "05da44d6823c2923597519ac10151f5827a24f80" + "reference": "7c37b64f8153c16b6406f5c28cf37828ebbe8846" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/05da44d6823c2923597519ac10151f5827a24f80", - "reference": "05da44d6823c2923597519ac10151f5827a24f80", + "url": "https://api.github.com/repos/laravel/framework/zipball/7c37b64f8153c16b6406f5c28cf37828ebbe8846", + "reference": "7c37b64f8153c16b6406f5c28cf37828ebbe8846", "shasum": "" }, "require": { @@ -1758,7 +1758,7 @@ "phpunit/phpunit": "Required to use assertions and run tests (^8.5.8|^9.3.3).", "predis/predis": "Required to use the predis connector (^1.1.2).", "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", - "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0).", + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0|^5.0).", "symfony/cache": "Required to PSR-6 cache bridge (^5.1.4).", "symfony/filesystem": "Required to enable support for relative symbolic links (^5.1.4).", "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0).", @@ -1805,20 +1805,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2021-01-26T14:40:21+00:00" + "time": "2021-03-09T15:37:45+00:00" }, { "name": "laravel/passport", - "version": "v10.1.0", + "version": "v10.1.2", "source": { "type": "git", "url": "https://github.com/laravel/passport.git", - "reference": "c2b93a7d8d93cf303bb1eefbfa5610f084f9bdd4" + "reference": "9f1a5d56eb609250104afc38cf407f7c2520cda3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/passport/zipball/c2b93a7d8d93cf303bb1eefbfa5610f084f9bdd4", - "reference": "c2b93a7d8d93cf303bb1eefbfa5610f084f9bdd4", + "url": "https://api.github.com/repos/laravel/passport/zipball/9f1a5d56eb609250104afc38cf407f7c2520cda3", + "reference": "9f1a5d56eb609250104afc38cf407f7c2520cda3", "shasum": "" }, "require": { @@ -1837,7 +1837,7 @@ "league/oauth2-server": "^8.2", "nyholm/psr7": "^1.3", "php": "^7.3|^8.0", - "phpseclib/phpseclib": "^2.0", + "phpseclib/phpseclib": "^2.0|^3.0", "symfony/psr-http-message-bridge": "^2.0" }, "require-dev": { @@ -1882,7 +1882,7 @@ "issues": "https://github.com/laravel/passport/issues", "source": "https://github.com/laravel/passport" }, - "time": "2020-11-26T07:57:30+00:00" + "time": "2021-03-02T16:40:00+00:00" }, { "name": "laravel/ui", @@ -2077,16 +2077,16 @@ }, { "name": "lcobucci/jwt", - "version": "4.1.0", + "version": "4.1.2", "source": { "type": "git", "url": "https://github.com/lcobucci/jwt.git", - "reference": "2f533837091d0b76a89a059e7ed2b2732b2f459e" + "reference": "c544710aa18e079baf0027ca4c8236913f46945b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/jwt/zipball/2f533837091d0b76a89a059e7ed2b2732b2f459e", - "reference": "2f533837091d0b76a89a059e7ed2b2732b2f459e", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/c544710aa18e079baf0027ca4c8236913f46945b", + "reference": "c544710aa18e079baf0027ca4c8236913f46945b", "shasum": "" }, "require": { @@ -2135,7 +2135,7 @@ ], "support": { "issues": "https://github.com/lcobucci/jwt/issues", - "source": "https://github.com/lcobucci/jwt/tree/4.1.0" + "source": "https://github.com/lcobucci/jwt/tree/4.1.2" }, "funding": [ { @@ -2147,7 +2147,7 @@ "type": "patreon" } ], - "time": "2021-01-28T00:57:26+00:00" + "time": "2021-02-19T19:37:15+00:00" }, { "name": "league/commonmark", @@ -2792,16 +2792,16 @@ }, { "name": "nesbot/carbon", - "version": "2.44.0", + "version": "2.46.0", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "e6ef33cb1f67a4bed831ed6d0f7e156739a5d8cd" + "reference": "2fd2c4a77d58a4e95234c8a61c5df1f157a91bf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/e6ef33cb1f67a4bed831ed6d0f7e156739a5d8cd", - "reference": "e6ef33cb1f67a4bed831ed6d0f7e156739a5d8cd", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/2fd2c4a77d58a4e95234c8a61c5df1f157a91bf4", + "reference": "2fd2c4a77d58a4e95234c8a61c5df1f157a91bf4", "shasum": "" }, "require": { @@ -2881,20 +2881,20 @@ "type": "tidelift" } ], - "time": "2021-01-26T20:46:41+00:00" + "time": "2021-02-24T17:30:44+00:00" }, { "name": "nyholm/psr7", - "version": "1.3.2", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/Nyholm/psr7.git", - "reference": "a272953743c454ac4af9626634daaf5ab3ce1173" + "reference": "23ae1f00fbc6a886cbe3062ca682391b9cc7c37b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Nyholm/psr7/zipball/a272953743c454ac4af9626634daaf5ab3ce1173", - "reference": "a272953743c454ac4af9626634daaf5ab3ce1173", + "url": "https://api.github.com/repos/Nyholm/psr7/zipball/23ae1f00fbc6a886cbe3062ca682391b9cc7c37b", + "reference": "23ae1f00fbc6a886cbe3062ca682391b9cc7c37b", "shasum": "" }, "require": { @@ -2916,7 +2916,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "1.4-dev" } }, "autoload": { @@ -2946,7 +2946,7 @@ ], "support": { "issues": "https://github.com/Nyholm/psr7/issues", - "source": "https://github.com/Nyholm/psr7/tree/1.3.2" + "source": "https://github.com/Nyholm/psr7/tree/1.4.0" }, "funding": [ { @@ -2958,7 +2958,7 @@ "type": "github" } ], - "time": "2020-11-14T17:35:34+00:00" + "time": "2021-02-18T15:41:32+00:00" }, { "name": "opis/closure", @@ -3267,24 +3267,26 @@ }, { "name": "phpseclib/phpseclib", - "version": "2.0.30", + "version": "3.0.6", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "136b9ca7eebef78be14abf90d65c5e57b6bc5d36" + "reference": "906a5fafabe5e6ba51ef3dc65b2722a677908837" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/136b9ca7eebef78be14abf90d65c5e57b6bc5d36", - "reference": "136b9ca7eebef78be14abf90d65c5e57b6bc5d36", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/906a5fafabe5e6ba51ef3dc65b2722a677908837", + "reference": "906a5fafabe5e6ba51ef3dc65b2722a677908837", "shasum": "" }, "require": { - "php": ">=5.3.3" + "paragonie/constant_time_encoding": "^1|^2", + "paragonie/random_compat": "^1.4|^2.0|^9.99.99", + "php": ">=5.6.1" }, "require-dev": { "phing/phing": "~2.7", - "phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4", + "phpunit/phpunit": "^5.7|^6.0|^9.4", "squizlabs/php_codesniffer": "~2.0" }, "suggest": { @@ -3299,7 +3301,7 @@ "phpseclib/bootstrap.php" ], "psr-4": { - "phpseclib\\": "phpseclib/" + "phpseclib3\\": "phpseclib/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3356,7 +3358,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/2.0.30" + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.6" }, "funding": [ { @@ -3372,7 +3374,7 @@ "type": "tidelift" } ], - "time": "2020-12-17T05:42:04+00:00" + "time": "2021-03-10T13:58:31+00:00" }, { "name": "pragmarx/google2fa", @@ -3636,27 +3638,22 @@ }, { "name": "psr/container", - "version": "1.0.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=7.2.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -3669,7 +3666,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common Container Interface (PHP FIG PSR-11)", @@ -3683,9 +3680,9 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/master" + "source": "https://github.com/php-fig/container/tree/1.1.1" }, - "time": "2017-02-14T16:28:37+00:00" + "time": "2021-03-05T17:36:06+00:00" }, { "name": "psr/event-dispatcher", @@ -4289,21 +4286,80 @@ "time": "2020-10-14T18:14:32+00:00" }, { - "name": "swiftmailer/swiftmailer", - "version": "v6.2.5", + "name": "spatie/data-transfer-object", + "version": "2.8.3", "source": { "type": "git", - "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "698a6a9f54d7eb321274de3ad19863802c879fb7" + "url": "https://github.com/spatie/data-transfer-object.git", + "reference": "2625a59566804ec63f01947d85947336237cbda6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/698a6a9f54d7eb321274de3ad19863802c879fb7", - "reference": "698a6a9f54d7eb321274de3ad19863802c879fb7", + "url": "https://api.github.com/repos/spatie/data-transfer-object/zipball/2625a59566804ec63f01947d85947336237cbda6", + "reference": "2625a59566804ec63f01947d85947336237cbda6", "shasum": "" }, "require": { - "egulias/email-validator": "^2.0", + "php": "^7.4|^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "suggest": { + "phpstan/phpstan": "Take advantage of checkUninitializedProperties with \\Spatie\\DataTransferObject\\PHPstan\\PropertiesAreAlwaysInitializedExtension" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\DataTransferObject\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brent Roose", + "email": "brent@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Data transfer objects with batteries included", + "homepage": "https://github.com/spatie/data-transfer-object", + "keywords": [ + "data-transfer-object", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/data-transfer-object/issues", + "source": "https://github.com/spatie/data-transfer-object/tree/2.8.3" + }, + "funding": [ + { + "url": "https://www.patreon.com/spatie", + "type": "patreon" + } + ], + "time": "2021-02-12T08:46:52+00:00" + }, + { + "name": "swiftmailer/swiftmailer", + "version": "v6.2.7", + "source": { + "type": "git", + "url": "https://github.com/swiftmailer/swiftmailer.git", + "reference": "15f7faf8508e04471f666633addacf54c0ab5933" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/15f7faf8508e04471f666633addacf54c0ab5933", + "reference": "15f7faf8508e04471f666633addacf54c0ab5933", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.0|^3.1", "php": ">=7.0.0", "symfony/polyfill-iconv": "^1.0", "symfony/polyfill-intl-idn": "^1.10", @@ -4349,7 +4405,7 @@ ], "support": { "issues": "https://github.com/swiftmailer/swiftmailer/issues", - "source": "https://github.com/swiftmailer/swiftmailer/tree/v6.2.5" + "source": "https://github.com/swiftmailer/swiftmailer/tree/v6.2.7" }, "funding": [ { @@ -4361,20 +4417,20 @@ "type": "tidelift" } ], - "time": "2021-01-12T09:35:59+00:00" + "time": "2021-03-09T12:30:35+00:00" }, { "name": "symfony/console", - "version": "v5.2.2", + "version": "v5.2.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "d62ec79478b55036f65e2602e282822b8eaaff0a" + "reference": "938ebbadae1b0a9c9d1ec313f87f9708609f1b79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/d62ec79478b55036f65e2602e282822b8eaaff0a", - "reference": "d62ec79478b55036f65e2602e282822b8eaaff0a", + "url": "https://api.github.com/repos/symfony/console/zipball/938ebbadae1b0a9c9d1ec313f87f9708609f1b79", + "reference": "938ebbadae1b0a9c9d1ec313f87f9708609f1b79", "shasum": "" }, "require": { @@ -4442,7 +4498,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.2.2" + "source": "https://github.com/symfony/console/tree/v5.2.5" }, "funding": [ { @@ -4458,11 +4514,11 @@ "type": "tidelift" } ], - "time": "2021-01-27T10:15:41+00:00" + "time": "2021-03-06T13:42:15+00:00" }, { "name": "symfony/css-selector", - "version": "v5.2.2", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -4507,7 +4563,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v5.2.2" + "source": "https://github.com/symfony/css-selector/tree/v5.2.4" }, "funding": [ { @@ -4594,16 +4650,16 @@ }, { "name": "symfony/error-handler", - "version": "v5.2.2", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "4fd4a377f7b7ec7c3f3b40346a1411e0a83f9d40" + "reference": "b547d3babcab5c31e01de59ee33e9d9c1421d7d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/4fd4a377f7b7ec7c3f3b40346a1411e0a83f9d40", - "reference": "4fd4a377f7b7ec7c3f3b40346a1411e0a83f9d40", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/b547d3babcab5c31e01de59ee33e9d9c1421d7d0", + "reference": "b547d3babcab5c31e01de59ee33e9d9c1421d7d0", "shasum": "" }, "require": { @@ -4643,7 +4699,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v5.2.2" + "source": "https://github.com/symfony/error-handler/tree/v5.2.4" }, "funding": [ { @@ -4659,20 +4715,20 @@ "type": "tidelift" } ], - "time": "2021-01-27T10:15:41+00:00" + "time": "2021-02-11T08:21:20+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v5.2.2", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "4f9760f8074978ad82e2ce854dff79a71fe45367" + "reference": "d08d6ec121a425897951900ab692b612a61d6240" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/4f9760f8074978ad82e2ce854dff79a71fe45367", - "reference": "4f9760f8074978ad82e2ce854dff79a71fe45367", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d08d6ec121a425897951900ab692b612a61d6240", + "reference": "d08d6ec121a425897951900ab692b612a61d6240", "shasum": "" }, "require": { @@ -4728,7 +4784,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v5.2.2" + "source": "https://github.com/symfony/event-dispatcher/tree/v5.2.4" }, "funding": [ { @@ -4744,7 +4800,7 @@ "type": "tidelift" } ], - "time": "2021-01-27T10:36:42+00:00" + "time": "2021-02-18T17:12:37+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -4827,16 +4883,16 @@ }, { "name": "symfony/finder", - "version": "v5.2.2", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "196f45723b5e618bf0e23b97e96d11652696ea9e" + "reference": "0d639a0943822626290d169965804f79400e6a04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/196f45723b5e618bf0e23b97e96d11652696ea9e", - "reference": "196f45723b5e618bf0e23b97e96d11652696ea9e", + "url": "https://api.github.com/repos/symfony/finder/zipball/0d639a0943822626290d169965804f79400e6a04", + "reference": "0d639a0943822626290d169965804f79400e6a04", "shasum": "" }, "require": { @@ -4868,7 +4924,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.2.2" + "source": "https://github.com/symfony/finder/tree/v5.2.4" }, "funding": [ { @@ -4884,7 +4940,7 @@ "type": "tidelift" } ], - "time": "2021-01-27T10:01:46+00:00" + "time": "2021-02-15T18:55:04+00:00" }, { "name": "symfony/http-client-contracts", @@ -4967,16 +5023,16 @@ }, { "name": "symfony/http-foundation", - "version": "v5.2.2", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "16dfa5acf8103f0394d447f8eea3ea49f9e50855" + "reference": "54499baea7f7418bce7b5ec92770fd0799e8e9bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/16dfa5acf8103f0394d447f8eea3ea49f9e50855", - "reference": "16dfa5acf8103f0394d447f8eea3ea49f9e50855", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/54499baea7f7418bce7b5ec92770fd0799e8e9bf", + "reference": "54499baea7f7418bce7b5ec92770fd0799e8e9bf", "shasum": "" }, "require": { @@ -5020,7 +5076,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v5.2.2" + "source": "https://github.com/symfony/http-foundation/tree/v5.2.4" }, "funding": [ { @@ -5036,20 +5092,20 @@ "type": "tidelift" } ], - "time": "2021-01-27T11:19:04+00:00" + "time": "2021-02-25T17:16:57+00:00" }, { "name": "symfony/http-kernel", - "version": "v5.2.2", + "version": "v5.2.5", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "831b51e9370ece0febd0950dd819c63f996721c7" + "reference": "b8c63ef63c2364e174c3b3e0ba0bf83455f97f73" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/831b51e9370ece0febd0950dd819c63f996721c7", - "reference": "831b51e9370ece0febd0950dd819c63f996721c7", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b8c63ef63c2364e174c3b3e0ba0bf83455f97f73", + "reference": "b8c63ef63c2364e174c3b3e0ba0bf83455f97f73", "shasum": "" }, "require": { @@ -5084,7 +5140,7 @@ "psr/log-implementation": "1.0" }, "require-dev": { - "psr/cache": "~1.0", + "psr/cache": "^1.0|^2.0|^3.0", "symfony/browser-kit": "^4.4|^5.0", "symfony/config": "^5.0", "symfony/console": "^4.4|^5.0", @@ -5132,7 +5188,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v5.2.2" + "source": "https://github.com/symfony/http-kernel/tree/v5.2.5" }, "funding": [ { @@ -5148,20 +5204,20 @@ "type": "tidelift" } ], - "time": "2021-01-27T14:45:46+00:00" + "time": "2021-03-10T17:07:35+00:00" }, { "name": "symfony/mime", - "version": "v5.2.2", + "version": "v5.2.5", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "37bade585ea100d235c031b258eff93b5b6bb9a9" + "reference": "554ba128f1955038b45db5e1fa7e93bfc683b139" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/37bade585ea100d235c031b258eff93b5b6bb9a9", - "reference": "37bade585ea100d235c031b258eff93b5b6bb9a9", + "url": "https://api.github.com/repos/symfony/mime/zipball/554ba128f1955038b45db5e1fa7e93bfc683b139", + "reference": "554ba128f1955038b45db5e1fa7e93bfc683b139", "shasum": "" }, "require": { @@ -5172,12 +5228,13 @@ "symfony/polyfill-php80": "^1.15" }, "conflict": { + "egulias/email-validator": "~3.0.0", "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", "symfony/mailer": "<4.4" }, "require-dev": { - "egulias/email-validator": "^2.1.10", + "egulias/email-validator": "^2.1.10|^3.1", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", "symfony/dependency-injection": "^4.4|^5.0", "symfony/property-access": "^4.4|^5.1", @@ -5214,7 +5271,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v5.2.2" + "source": "https://github.com/symfony/mime/tree/v5.2.5" }, "funding": [ { @@ -5230,11 +5287,11 @@ "type": "tidelift" } ], - "time": "2021-01-25T14:08:25+00:00" + "time": "2021-03-07T16:08:20+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.22.0", + "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -5293,7 +5350,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.1" }, "funding": [ { @@ -5313,16 +5370,16 @@ }, { "name": "symfony/polyfill-iconv", - "version": "v1.22.0", + "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-iconv.git", - "reference": "b34bfb8c4c22650ac080d2662ae3502e5f2f4ae6" + "reference": "06fb361659649bcfd6a208a0f1fcaf4e827ad342" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/b34bfb8c4c22650ac080d2662ae3502e5f2f4ae6", - "reference": "b34bfb8c4c22650ac080d2662ae3502e5f2f4ae6", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/06fb361659649bcfd6a208a0f1fcaf4e827ad342", + "reference": "06fb361659649bcfd6a208a0f1fcaf4e827ad342", "shasum": "" }, "require": { @@ -5373,7 +5430,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-iconv/tree/v1.22.0" + "source": "https://github.com/symfony/polyfill-iconv/tree/v1.22.1" }, "funding": [ { @@ -5389,20 +5446,20 @@ "type": "tidelift" } ], - "time": "2021-01-07T16:49:33+00:00" + "time": "2021-01-22T09:19:47+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.22.0", + "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "267a9adeb8ecb8071040a740930e077cdfb987af" + "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/267a9adeb8ecb8071040a740930e077cdfb987af", - "reference": "267a9adeb8ecb8071040a740930e077cdfb987af", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/5601e09b69f26c1828b13b6bb87cb07cddba3170", + "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170", "shasum": "" }, "require": { @@ -5454,7 +5511,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.22.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.22.1" }, "funding": [ { @@ -5470,20 +5527,20 @@ "type": "tidelift" } ], - "time": "2021-01-07T16:49:33+00:00" + "time": "2021-01-22T09:19:47+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.22.0", + "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "0eb8293dbbcd6ef6bf81404c9ce7d95bcdf34f44" + "reference": "2d63434d922daf7da8dd863e7907e67ee3031483" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/0eb8293dbbcd6ef6bf81404c9ce7d95bcdf34f44", - "reference": "0eb8293dbbcd6ef6bf81404c9ce7d95bcdf34f44", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/2d63434d922daf7da8dd863e7907e67ee3031483", + "reference": "2d63434d922daf7da8dd863e7907e67ee3031483", "shasum": "" }, "require": { @@ -5541,7 +5598,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.22.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.22.1" }, "funding": [ { @@ -5557,20 +5614,20 @@ "type": "tidelift" } ], - "time": "2021-01-07T16:49:33+00:00" + "time": "2021-01-22T09:19:47+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.22.0", + "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "6e971c891537eb617a00bb07a43d182a6915faba" + "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/6e971c891537eb617a00bb07a43d182a6915faba", - "reference": "6e971c891537eb617a00bb07a43d182a6915faba", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248", + "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248", "shasum": "" }, "require": { @@ -5625,7 +5682,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.1" }, "funding": [ { @@ -5641,20 +5698,20 @@ "type": "tidelift" } ], - "time": "2021-01-07T17:09:11+00:00" + "time": "2021-01-22T09:19:47+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.22.0", + "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13" + "reference": "5232de97ee3b75b0360528dae24e73db49566ab1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f377a3dd1fde44d37b9831d68dc8dea3ffd28e13", - "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1", + "reference": "5232de97ee3b75b0360528dae24e73db49566ab1", "shasum": "" }, "require": { @@ -5705,7 +5762,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.1" }, "funding": [ { @@ -5721,11 +5778,11 @@ "type": "tidelift" } ], - "time": "2021-01-07T16:49:33+00:00" + "time": "2021-01-22T09:19:47+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.22.0", + "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", @@ -5781,7 +5838,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.22.0" + "source": "https://github.com/symfony/polyfill-php72/tree/v1.22.1" }, "funding": [ { @@ -5801,7 +5858,7 @@ }, { "name": "symfony/polyfill-php73", - "version": "v1.22.0", + "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", @@ -5860,7 +5917,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.22.0" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.22.1" }, "funding": [ { @@ -5880,7 +5937,7 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.22.0", + "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -5943,7 +6000,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.1" }, "funding": [ { @@ -5963,7 +6020,7 @@ }, { "name": "symfony/process", - "version": "v5.2.2", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", @@ -6005,7 +6062,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.2.2" + "source": "https://github.com/symfony/process/tree/v5.2.4" }, "funding": [ { @@ -6025,16 +6082,16 @@ }, { "name": "symfony/psr-http-message-bridge", - "version": "v2.0.2", + "version": "v2.1.0", "source": { "type": "git", "url": "https://github.com/symfony/psr-http-message-bridge.git", - "reference": "51a21cb3ba3927d4b4bf8f25cc55763351af5f2e" + "reference": "81db2d4ae86e9f0049828d9343a72b9523884e5d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/51a21cb3ba3927d4b4bf8f25cc55763351af5f2e", - "reference": "51a21cb3ba3927d4b4bf8f25cc55763351af5f2e", + "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/81db2d4ae86e9f0049828d9343a72b9523884e5d", + "reference": "81db2d4ae86e9f0049828d9343a72b9523884e5d", "shasum": "" }, "require": { @@ -6044,7 +6101,13 @@ }, "require-dev": { "nyholm/psr7": "^1.1", - "symfony/phpunit-bridge": "^4.4 || ^5.0" + "psr/log": "^1.1", + "symfony/browser-kit": "^4.4 || ^5.0", + "symfony/config": "^4.4 || ^5.0", + "symfony/event-dispatcher": "^4.4 || ^5.0", + "symfony/framework-bundle": "^4.4 || ^5.0", + "symfony/http-kernel": "^4.4 || ^5.0", + "symfony/phpunit-bridge": "^4.4.19 || ^5.2" }, "suggest": { "nyholm/psr7": "For a super lightweight PSR-7/17 implementation" @@ -6052,7 +6115,7 @@ "type": "symfony-bridge", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "2.1-dev" } }, "autoload": { @@ -6087,7 +6150,7 @@ ], "support": { "issues": "https://github.com/symfony/psr-http-message-bridge/issues", - "source": "https://github.com/symfony/psr-http-message-bridge/tree/v2.0.2" + "source": "https://github.com/symfony/psr-http-message-bridge/tree/v2.1.0" }, "funding": [ { @@ -6103,20 +6166,20 @@ "type": "tidelift" } ], - "time": "2020-09-29T08:17:46+00:00" + "time": "2021-02-17T10:35:25+00:00" }, { "name": "symfony/routing", - "version": "v5.2.2", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "348b5917e56546c6d96adbf21d7f92c9ef563661" + "reference": "cafa138128dfd6ab6be1abf6279169957b34f662" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/348b5917e56546c6d96adbf21d7f92c9ef563661", - "reference": "348b5917e56546c6d96adbf21d7f92c9ef563661", + "url": "https://api.github.com/repos/symfony/routing/zipball/cafa138128dfd6ab6be1abf6279169957b34f662", + "reference": "cafa138128dfd6ab6be1abf6279169957b34f662", "shasum": "" }, "require": { @@ -6177,7 +6240,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v5.2.2" + "source": "https://github.com/symfony/routing/tree/v5.2.4" }, "funding": [ { @@ -6193,7 +6256,7 @@ "type": "tidelift" } ], - "time": "2021-01-27T10:15:41+00:00" + "time": "2021-02-22T15:48:39+00:00" }, { "name": "symfony/service-contracts", @@ -6276,16 +6339,16 @@ }, { "name": "symfony/string", - "version": "v5.2.2", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "c95468897f408dd0aca2ff582074423dd0455122" + "reference": "4e78d7d47061fa183639927ec40d607973699609" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/c95468897f408dd0aca2ff582074423dd0455122", - "reference": "c95468897f408dd0aca2ff582074423dd0455122", + "url": "https://api.github.com/repos/symfony/string/zipball/4e78d7d47061fa183639927ec40d607973699609", + "reference": "4e78d7d47061fa183639927ec40d607973699609", "shasum": "" }, "require": { @@ -6339,7 +6402,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.2.2" + "source": "https://github.com/symfony/string/tree/v5.2.4" }, "funding": [ { @@ -6355,20 +6418,20 @@ "type": "tidelift" } ], - "time": "2021-01-25T15:14:59+00:00" + "time": "2021-02-16T10:20:28+00:00" }, { "name": "symfony/translation", - "version": "v5.2.2", + "version": "v5.2.5", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "c021864d4354ee55160ddcfd31dc477a1bc77949" + "reference": "0947ab1e3aabd22a6bef393874b2555d2bb976da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/c021864d4354ee55160ddcfd31dc477a1bc77949", - "reference": "c021864d4354ee55160ddcfd31dc477a1bc77949", + "url": "https://api.github.com/repos/symfony/translation/zipball/0947ab1e3aabd22a6bef393874b2555d2bb976da", + "reference": "0947ab1e3aabd22a6bef393874b2555d2bb976da", "shasum": "" }, "require": { @@ -6385,7 +6448,7 @@ "symfony/yaml": "<4.4" }, "provide": { - "symfony/translation-implementation": "2.0" + "symfony/translation-implementation": "2.3" }, "require-dev": { "psr/log": "~1.0", @@ -6432,7 +6495,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v5.2.2" + "source": "https://github.com/symfony/translation/tree/v5.2.5" }, "funding": [ { @@ -6448,7 +6511,7 @@ "type": "tidelift" } ], - "time": "2021-01-27T10:15:41+00:00" + "time": "2021-03-06T07:59:01+00:00" }, { "name": "symfony/translation-contracts", @@ -6530,16 +6593,16 @@ }, { "name": "symfony/var-dumper", - "version": "v5.2.2", + "version": "v5.2.5", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "72ca213014a92223a5d18651ce79ef441c12b694" + "reference": "002ab5a36702adf0c9a11e6d8836623253e9045e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/72ca213014a92223a5d18651ce79ef441c12b694", - "reference": "72ca213014a92223a5d18651ce79ef441c12b694", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/002ab5a36702adf0c9a11e6d8836623253e9045e", + "reference": "002ab5a36702adf0c9a11e6d8836623253e9045e", "shasum": "" }, "require": { @@ -6598,7 +6661,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.2.2" + "source": "https://github.com/symfony/var-dumper/tree/v5.2.5" }, "funding": [ { @@ -6614,7 +6677,7 @@ "type": "tidelift" } ], - "time": "2021-01-27T10:15:41+00:00" + "time": "2021-03-06T07:59:01+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -6671,16 +6734,16 @@ }, { "name": "twig/twig", - "version": "v2.14.3", + "version": "v2.14.4", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "8bc568d460d88b25c00c046256ec14a787ea60d9" + "reference": "0b4ba691fb99ec7952d25deb36c0a83061b93bbf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/8bc568d460d88b25c00c046256ec14a787ea60d9", - "reference": "8bc568d460d88b25c00c046256ec14a787ea60d9", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/0b4ba691fb99ec7952d25deb36c0a83061b93bbf", + "reference": "0b4ba691fb99ec7952d25deb36c0a83061b93bbf", "shasum": "" }, "require": { @@ -6734,7 +6797,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v2.14.3" + "source": "https://github.com/twigphp/Twig/tree/v2.14.4" }, "funding": [ { @@ -6746,7 +6809,7 @@ "type": "tidelift" } ], - "time": "2021-01-05T15:34:33+00:00" + "time": "2021-03-10T10:05:55+00:00" }, { "name": "vlucas/phpdotenv", @@ -7414,16 +7477,16 @@ }, { "name": "composer/composer", - "version": "2.0.9", + "version": "2.0.11", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "591c2c155cac0d2d7f34af41d3b1e29bcbfc685e" + "reference": "a5a5632da0b1c2d6fa9a3b65f1f4e90d1f04abb9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/591c2c155cac0d2d7f34af41d3b1e29bcbfc685e", - "reference": "591c2c155cac0d2d7f34af41d3b1e29bcbfc685e", + "url": "https://api.github.com/repos/composer/composer/zipball/a5a5632da0b1c2d6fa9a3b65f1f4e90d1f04abb9", + "reference": "a5a5632da0b1c2d6fa9a3b65f1f4e90d1f04abb9", "shasum": "" }, "require": { @@ -7491,7 +7554,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/composer/issues", - "source": "https://github.com/composer/composer/tree/2.0.9" + "source": "https://github.com/composer/composer/tree/2.0.11" }, "funding": [ { @@ -7507,7 +7570,7 @@ "type": "tidelift" } ], - "time": "2021-01-27T15:09:27+00:00" + "time": "2021-02-24T13:57:23+00:00" }, { "name": "composer/semver", @@ -8014,16 +8077,16 @@ }, { "name": "felixfbecker/language-server-protocol", - "version": "v1.5.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/felixfbecker/php-language-server-protocol.git", - "reference": "85e83cacd2ed573238678c6875f8f0d7ec699541" + "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/85e83cacd2ed573238678c6875f8f0d7ec699541", - "reference": "85e83cacd2ed573238678c6875f8f0d7ec699541", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/9d846d1f5cf101deee7a61c8ba7caa0a975cd730", + "reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730", "shasum": "" }, "require": { @@ -8064,9 +8127,9 @@ ], "support": { "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", - "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.0" + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/1.5.1" }, - "time": "2020-10-23T13:55:30+00:00" + "time": "2021-02-22T14:02:09+00:00" }, { "name": "filp/whoops", @@ -8190,58 +8253,6 @@ }, "time": "2020-07-09T08:09:16+00:00" }, - { - "name": "johnkary/phpunit-speedtrap", - "version": "v3.3.0", - "source": { - "type": "git", - "url": "https://github.com/johnkary/phpunit-speedtrap.git", - "reference": "9ba81d42676da31366c85d3ff8c10a8352d02030" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/johnkary/phpunit-speedtrap/zipball/9ba81d42676da31366c85d3ff8c10a8352d02030", - "reference": "9ba81d42676da31366c85d3ff8c10a8352d02030", - "shasum": "" - }, - "require": { - "php": ">=7.1", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "psr-4": { - "JohnKary\\PHPUnit\\Listener\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "John Kary", - "email": "john@johnkary.net" - } - ], - "description": "Find and report on slow tests in your PHPUnit test suite", - "homepage": "https://github.com/johnkary/phpunit-speedtrap", - "keywords": [ - "phpunit", - "profile", - "slow" - ], - "support": { - "issues": "https://github.com/johnkary/phpunit-speedtrap/issues", - "source": "https://github.com/johnkary/phpunit-speedtrap/tree/v3.3.0" - }, - "time": "2020-12-18T16:20:16+00:00" - }, { "name": "justinrainbow/json-schema", "version": "5.2.10", @@ -8379,16 +8390,16 @@ }, { "name": "mockery/mockery", - "version": "1.4.2", + "version": "1.4.3", "source": { "type": "git", "url": "https://github.com/mockery/mockery.git", - "reference": "20cab678faed06fac225193be281ea0fddb43b93" + "reference": "d1339f64479af1bee0e82a0413813fe5345a54ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/20cab678faed06fac225193be281ea0fddb43b93", - "reference": "20cab678faed06fac225193be281ea0fddb43b93", + "url": "https://api.github.com/repos/mockery/mockery/zipball/d1339f64479af1bee0e82a0413813fe5345a54ea", + "reference": "d1339f64479af1bee0e82a0413813fe5345a54ea", "shasum": "" }, "require": { @@ -8445,9 +8456,9 @@ ], "support": { "issues": "https://github.com/mockery/mockery/issues", - "source": "https://github.com/mockery/mockery/tree/master" + "source": "https://github.com/mockery/mockery/tree/1.4.3" }, - "time": "2020-08-11T18:10:13+00:00" + "time": "2021-02-24T09:51:49+00:00" }, { "name": "myclabs/deep-copy", @@ -8616,16 +8627,16 @@ }, { "name": "nunomaduro/larastan", - "version": "v0.6.13", + "version": "v0.7.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/larastan.git", - "reference": "7a047f7974e6e16d04ee038d86e2c5e6c59e9dfe" + "reference": "179d7565674d4e264e60296d1b5b75413b7461b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/larastan/zipball/7a047f7974e6e16d04ee038d86e2c5e6c59e9dfe", - "reference": "7a047f7974e6e16d04ee038d86e2c5e6c59e9dfe", + "url": "https://api.github.com/repos/nunomaduro/larastan/zipball/179d7565674d4e264e60296d1b5b75413b7461b6", + "reference": "179d7565674d4e264e60296d1b5b75413b7461b6", "shasum": "" }, "require": { @@ -8640,7 +8651,7 @@ "illuminate/support": "^6.0 || ^7.0 || ^8.0 || ^9.0", "mockery/mockery": "^0.9 || ^1.0", "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^0.12.65", + "phpstan/phpstan": "^0.12.70", "symfony/process": "^4.3 || ^5.0" }, "require-dev": { @@ -8689,7 +8700,7 @@ ], "support": { "issues": "https://github.com/nunomaduro/larastan/issues", - "source": "https://github.com/nunomaduro/larastan/tree/v0.6.13" + "source": "https://github.com/nunomaduro/larastan/tree/v0.7.0" }, "funding": [ { @@ -8709,7 +8720,7 @@ "type": "patreon" } ], - "time": "2021-01-22T12:51:26+00:00" + "time": "2021-02-01T22:08:11+00:00" }, { "name": "openlss/lib-array2xml", @@ -8826,16 +8837,16 @@ }, { "name": "phar-io/version", - "version": "3.0.4", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "e4782611070e50613683d2b9a57730e9a3ba5451" + "reference": "bae7c545bef187884426f042434e561ab1ddb182" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/e4782611070e50613683d2b9a57730e9a3ba5451", - "reference": "e4782611070e50613683d2b9a57730e9a3ba5451", + "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182", + "reference": "bae7c545bef187884426f042434e561ab1ddb182", "shasum": "" }, "require": { @@ -8871,9 +8882,9 @@ "description": "Library for handling version information and constraints", "support": { "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.0.4" + "source": "https://github.com/phar-io/version/tree/3.1.0" }, - "time": "2020-12-13T23:18:30+00:00" + "time": "2021-02-23T14:00:09+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -9102,16 +9113,16 @@ }, { "name": "phpstan/phpstan", - "version": "0.12.70", + "version": "0.12.81", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "07f0ef37f5f876e8cee44cc8ea0ec3fe80d499ee" + "reference": "0dd5b0ebeff568f7000022ea5f04aa86ad3124b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/07f0ef37f5f876e8cee44cc8ea0ec3fe80d499ee", - "reference": "07f0ef37f5f876e8cee44cc8ea0ec3fe80d499ee", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/0dd5b0ebeff568f7000022ea5f04aa86ad3124b8", + "reference": "0dd5b0ebeff568f7000022ea5f04aa86ad3124b8", "shasum": "" }, "require": { @@ -9142,7 +9153,7 @@ "description": "PHPStan - PHP Static Analysis Tool", "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/0.12.70" + "source": "https://github.com/phpstan/phpstan/tree/0.12.81" }, "funding": [ { @@ -9158,7 +9169,7 @@ "type": "tidelift" } ], - "time": "2021-01-27T17:06:47+00:00" + "time": "2021-03-08T22:03:02+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -9531,16 +9542,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.1", + "version": "9.5.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "e7bdf4085de85a825f4424eae52c99a1cec2f360" + "reference": "f661659747f2f87f9e72095bb207bceb0f151cb4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e7bdf4085de85a825f4424eae52c99a1cec2f360", - "reference": "e7bdf4085de85a825f4424eae52c99a1cec2f360", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f661659747f2f87f9e72095bb207bceb0f151cb4", + "reference": "f661659747f2f87f9e72095bb207bceb0f151cb4", "shasum": "" }, "require": { @@ -9618,7 +9629,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.1" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.2" }, "funding": [ { @@ -9630,7 +9641,7 @@ "type": "github" } ], - "time": "2021-01-17T07:42:25+00:00" + "time": "2021-02-02T14:45:58+00:00" }, { "name": "react/promise", @@ -9688,12 +9699,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "7d7032b003f4c36edbf059dd039db72656df6323" + "reference": "43a315341710475e88006a0e55864b348d3d781c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/7d7032b003f4c36edbf059dd039db72656df6323", - "reference": "7d7032b003f4c36edbf059dd039db72656df6323", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/43a315341710475e88006a0e55864b348d3d781c", + "reference": "43a315341710475e88006a0e55864b348d3d781c", "shasum": "" }, "conflict": { @@ -9711,6 +9722,7 @@ "barrelstrength/sprout-forms": "<3.9", "baserproject/basercms": ">=4,<=4.3.6|>=4.4,<4.4.1", "bolt/bolt": "<3.7.1", + "bolt/core": "<4.1.13", "brightlocal/phpwhois": "<=4.2.5", "buddypress/buddypress": "<5.1.2", "bugsnag/bugsnag-laravel": ">=2,<2.0.2", @@ -9752,13 +9764,17 @@ "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6", "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1", "ezsystems/ezplatform-kernel": ">=1,<1.0.2.1", + "ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<=1.3.1", "ezsystems/ezplatform-user": ">=1,<1.0.1", - "ezsystems/ezpublish-kernel": ">=5.3,<5.3.12.1|>=5.4,<5.4.14.2|>=6,<6.7.9.1|>=6.8,<6.13.6.3|>=7,<7.2.4.1|>=7.3,<7.3.2.1|>=7.5,<7.5.7.1", + "ezsystems/ezpublish-kernel": ">=5.3,<5.3.12.1|>=5.4,<5.4.14.2|>=6,<6.7.9.1|>=6.8,<=6.13.8|>=7,<7.2.4.1|>=7.3,<7.3.2.1|>=7.5,<=7.5.15", "ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.6|>=5.4,<5.4.14.2|>=2011,<2017.12.7.3|>=2018.6,<2018.6.1.4|>=2018.9,<2018.9.1.3|>=2019.3,<2019.3.5.1", "ezsystems/platform-ui-assets-bundle": ">=4.2,<4.2.3", "ezsystems/repository-forms": ">=2.3,<2.3.2.1", "ezyang/htmlpurifier": "<4.1.1", + "facade/ignition": "<=2.5.1,>=2.0|<=1.16.13", "firebase/php-jwt": "<2", + "flarum/sticky": ">=0.1-beta.14,<=0.1-beta.15", + "flarum/tags": "<=0.1-beta.13", "fooman/tcpdf": "<6.2.22", "fossar/tcpdf-parser": "<6.2.22", "friendsofsymfony/oauth2-php": "<1.3", @@ -9775,18 +9791,20 @@ "guzzlehttp/guzzle": ">=4-rc.2,<4.2.4|>=5,<5.3.1|>=6,<6.2.1", "illuminate/auth": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.10", "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<=4.1.99999|>=4.2,<=4.2.99999|>=5,<=5.0.99999|>=5.1,<=5.1.99999|>=5.2,<=5.2.99999|>=5.3,<=5.3.99999|>=5.4,<=5.4.99999|>=5.5,<=5.5.49|>=5.6,<=5.6.99999|>=5.7,<=5.7.99999|>=5.8,<=5.8.99999|>=6,<6.18.31|>=7,<7.22.4", - "illuminate/database": "<6.20.12|>=7,<7.30.3|>=8,<8.22.1", + "illuminate/database": "<6.20.14|>=7,<7.30.4|>=8,<8.24", "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", "illuminate/view": ">=7,<7.1.2", + "impresscms/impresscms": "<=1.4.2", "ivankristianto/phpwhois": "<=4.3", "james-heinrich/getid3": "<1.9.9", + "joomla/archive": "<1.1.10", "joomla/session": "<1.3.1", "jsmitty12/phpwhois": "<5.1", "kazist/phpwhois": "<=4.2.6", "kitodo/presentation": "<3.1.2", "kreait/firebase-php": ">=3.2,<3.8.1", "la-haute-societe/tcpdf": "<6.2.22", - "laravel/framework": "<6.20.12|>=7,<7.30.3|>=8,<8.22.1", + "laravel/framework": "<6.20.14|>=7,<7.30.4|>=8,<8.24", "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", "league/commonmark": "<0.18.3", "librenms/librenms": "<1.53", @@ -9805,10 +9823,10 @@ "nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13", "nystudio107/craft-seomatic": "<3.3", "nzo/url-encryptor-bundle": ">=4,<4.3.2|>=5,<5.0.1", - "october/backend": ">=1.0.319,<1.0.470", + "october/backend": "<1.1.2", "october/cms": "= 1.0.469|>=1.0.319,<1.0.469", "october/october": ">=1.0.319,<1.0.466", - "october/rain": ">=1.0.319,<1.0.468", + "october/rain": "<1.0.472|>=1.1,<1.1.2", "onelogin/php-saml": "<2.10.4", "oneup/uploader-bundle": "<1.9.3|>=2,<2.1.5", "openid/php-openid": "<2.3", @@ -9832,7 +9850,7 @@ "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3", "phpwhois/phpwhois": "<=4.2.5", "phpxmlrpc/extras": "<0.6.1", - "pimcore/pimcore": "<6.3", + "pimcore/pimcore": "<6.8.8", "pocketmine/pocketmine-mp": "<3.15.4", "prestashop/autoupgrade": ">=4,<4.10.1", "prestashop/contactform": ">1.0.1,<4.3", @@ -9852,7 +9870,7 @@ "sensiolabs/connect": "<4.2.3", "serluck/phpwhois": "<=4.2.6", "shopware/core": "<=6.3.4", - "shopware/platform": "<=6.3.4", + "shopware/platform": "<=6.3.5.1", "shopware/shopware": "<5.6.9", "silverstripe/admin": ">=1.0.3,<1.0.4|>=1.1,<1.1.1", "silverstripe/assets": ">=1,<1.4.7|>=1.5,<1.5.2", @@ -9872,7 +9890,7 @@ "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", "simplito/elliptic-php": "<1.0.6", "slim/slim": "<2.6", - "smarty/smarty": "<3.1.33", + "smarty/smarty": "<3.1.39", "socalnick/scn-social-auth": "<1.15.2", "socialiteproviders/steam": "<1.1", "spoonity/tcpdf": "<6.2.22", @@ -9933,6 +9951,7 @@ "ua-parser/uap-php": "<3.8", "usmanhalalit/pixie": "<1.0.3|>=2,<2.0.2", "verot/class.upload.php": "<=1.0.3|>=2,<=2.0.4", + "vrana/adminer": "<4.7.9", "wallabag/tcpdf": "<6.2.22", "willdurand/js-translation-bundle": "<2.1.1", "yii2mod/yii2-cms": "<1.9.2", @@ -10003,7 +10022,7 @@ "type": "tidelift" } ], - "time": "2021-01-29T06:02:40+00:00" + "time": "2021-03-13T00:05:05+00:00" }, { "name": "sebastian/cli-parser", @@ -11082,16 +11101,16 @@ }, { "name": "symfony/debug", - "version": "v4.4.19", + "version": "v4.4.20", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "af4987aa4a5630e9615be9d9c3ed1b0f24ca449c" + "reference": "157bbec4fd773bae53c5483c50951a5530a2cc16" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/af4987aa4a5630e9615be9d9c3ed1b0f24ca449c", - "reference": "af4987aa4a5630e9615be9d9c3ed1b0f24ca449c", + "url": "https://api.github.com/repos/symfony/debug/zipball/157bbec4fd773bae53c5483c50951a5530a2cc16", + "reference": "157bbec4fd773bae53c5483c50951a5530a2cc16", "shasum": "" }, "require": { @@ -11131,7 +11150,7 @@ "description": "Provides tools to ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/debug/tree/v4.4.19" + "source": "https://github.com/symfony/debug/tree/v4.4.20" }, "funding": [ { @@ -11147,20 +11166,20 @@ "type": "tidelift" } ], - "time": "2021-01-27T09:09:26+00:00" + "time": "2021-01-28T16:54:48+00:00" }, { "name": "symfony/filesystem", - "version": "v5.2.2", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "262d033b57c73e8b59cd6e68a45c528318b15038" + "reference": "710d364200997a5afde34d9fe57bd52f3cc1e108" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/262d033b57c73e8b59cd6e68a45c528318b15038", - "reference": "262d033b57c73e8b59cd6e68a45c528318b15038", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/710d364200997a5afde34d9fe57bd52f3cc1e108", + "reference": "710d364200997a5afde34d9fe57bd52f3cc1e108", "shasum": "" }, "require": { @@ -11193,7 +11212,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.2.2" + "source": "https://github.com/symfony/filesystem/tree/v5.2.4" }, "funding": [ { @@ -11209,7 +11228,7 @@ "type": "tidelift" } ], - "time": "2021-01-27T10:01:46+00:00" + "time": "2021-02-12T10:38:38+00:00" }, { "name": "thecodingmachine/phpstan-strict-rules", @@ -11318,16 +11337,16 @@ }, { "name": "vimeo/psalm", - "version": "4.4.1", + "version": "4.6.2", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "9fd7a7d885b3a216cff8dec9d8c21a132f275224" + "reference": "bca09d74adc704c4eaee36a3c3e9d379e290fc3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/9fd7a7d885b3a216cff8dec9d8c21a132f275224", - "reference": "9fd7a7d885b3a216cff8dec9d8c21a132f275224", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/bca09d74adc704c4eaee36a3c3e9d379e290fc3b", + "reference": "bca09d74adc704c4eaee36a3c3e9d379e290fc3b", "shasum": "" }, "require": { @@ -11344,8 +11363,8 @@ "ext-simplexml": "*", "ext-tokenizer": "*", "felixfbecker/advanced-json-rpc": "^3.0.3", - "felixfbecker/language-server-protocol": "^1.4", - "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0", + "felixfbecker/language-server-protocol": "^1.5", + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", "nikic/php-parser": "^4.10.1", "openlss/lib-array2xml": "^1.0", "php": "^7.1|^8", @@ -11361,6 +11380,7 @@ "bamarni/composer-bin-plugin": "^1.2", "brianium/paratest": "^4.0||^6.0", "ext-curl": "*", + "php-parallel-lint/php-parallel-lint": "^1.2", "phpdocumentor/reflection-docblock": "^5", "phpmyadmin/sql-parser": "5.1.0||dev-master", "phpspec/prophecy": ">=1.9.0", @@ -11416,9 +11436,9 @@ ], "support": { "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/4.4.1" + "source": "https://github.com/vimeo/psalm/tree/4.6.2" }, - "time": "2021-01-14T21:44:29+00:00" + "time": "2021-02-26T02:24:18+00:00" }, { "name": "webmozart/path-util", diff --git a/config/breadcrumbs.php b/config/breadcrumbs.php index 1764fcd984..f0c0008a81 100644 --- a/config/breadcrumbs.php +++ b/config/breadcrumbs.php @@ -39,7 +39,7 @@ return [ | */ - 'view' => 'partials/layout/breadcrumbs', + 'view' => sprintf('%s/partials/layout/breadcrumbs', env('FIREFLY_III_LAYOUT', 'v1')), /* |-------------------------------------------------------------------------- diff --git a/config/firefly.php b/config/firefly.php index 874382ab0c..3b2ebd778e 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -45,13 +45,16 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournalLink; use FireflyIII\Models\TransactionType as TransactionTypeModel; use FireflyIII\Models\Webhook; +use FireflyIII\Models\WebhookAttempt; +use FireflyIII\Models\WebhookMessage; use FireflyIII\Support\Binder\AccountList; use FireflyIII\Support\Binder\BudgetList; use FireflyIII\Support\Binder\CategoryList; use FireflyIII\Support\Binder\CLIToken; -use FireflyIII\Support\Binder\ConfigurationName; use FireflyIII\Support\Binder\CurrencyCode; use FireflyIII\Support\Binder\Date; +use FireflyIII\Support\Binder\DynamicConfigKey; +use FireflyIII\Support\Binder\EitherConfigKey; use FireflyIII\Support\Binder\JournalList; use FireflyIII\Support\Binder\TagList; use FireflyIII\Support\Binder\TagOrId; @@ -96,9 +99,9 @@ return [ 'webhooks' => false, ], - 'version' => '5.5.0-beta.1', + 'version' => '5.5.0-beta.2', 'api_version' => '1.5.0', - 'db_version' => 15, + 'db_version' => 16, 'maxUploadSize' => 1073741824, // 1 GB 'send_error_message' => env('SEND_ERROR_MESSAGE', true), 'site_owner' => env('SITE_OWNER', ''), @@ -255,6 +258,9 @@ return [ 'initial' => [AccountType::INITIAL_BALANCE], 'import' => [AccountType::IMPORT], 'reconcile' => [AccountType::RECONCILIATION], + 'loan' => [AccountType::LOAN], + 'debt' => [AccountType::DEBT], + 'mortgage' => [AccountType::MORTGAGE], 'liabilities' => [AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE, AccountType::CREDITCARD], 'liability' => [AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE, AccountType::CREDITCARD], ], @@ -308,7 +314,7 @@ return [ 'nl_NL' => ['name_locale' => 'Nederlands', 'name_english' => 'Dutch'], 'pl_PL' => ['name_locale' => 'Polski', 'name_english' => 'Polish '], 'pt_BR' => ['name_locale' => 'Português do Brasil', 'name_english' => 'Portuguese (Brazil)'], - // 'pt_PT' => ['name_locale' => 'Portuguese', 'name_english' => 'Portuguese'], + 'pt_PT' => ['name_locale' => 'Português', 'name_english' => 'Portuguese'], 'ro_RO' => ['name_locale' => 'Română', 'name_english' => 'Romanian'], 'ru_RU' => ['name_locale' => 'Русский', 'name_english' => 'Russian'], // 'si_LK' => ['name_locale' => 'සිංහල', 'name_english' => 'Sinhala (Sri Lanka)'], @@ -379,6 +385,8 @@ return [ 'transactionGroup' => TransactionGroup::class, 'user' => User::class, 'webhook' => Webhook::class, + 'webhookMessage' => WebhookMessage::class, + 'webhookAttempt' => WebhookAttempt::class, // strings 'currency_code' => CurrencyCode::class, @@ -401,7 +409,8 @@ return [ 'toCurrencyCode' => CurrencyCode::class, 'cliToken' => CLIToken::class, 'tagOrId' => TagOrId::class, - 'configName' => ConfigurationName::class, + 'dynamicConfigKey' => DynamicConfigKey::class, + 'eitherConfigKey' => EitherConfigKey::class, ], 'rule-actions' => [ diff --git a/config/google2fa.php b/config/google2fa.php index 34e719a2e1..98f6da2e85 100644 --- a/config/google2fa.php +++ b/config/google2fa.php @@ -76,7 +76,7 @@ return [ /* * One Time Password View */ - 'view' => 'auth.mfa', + 'view' => sprintf('%s.auth.mfa', env('FIREFLY_III_LAYOUT', 'v1')), /* * One Time Password error message diff --git a/config/logging.php b/config/logging.php index 472c02caad..1acddbcdc4 100644 --- a/config/logging.php +++ b/config/logging.php @@ -92,7 +92,7 @@ return [ 'driver' => 'single', 'path' => 'php://stdout', 'tap' => [AuditLogger::class], - 'level' => envNonEmpty('APP_LOG_LEVEL', 'info'), + 'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'), ], 'dailytest' => [ 'driver' => 'daily', diff --git a/config/queue.php b/config/queue.php index d6637fd655..0950149af1 100644 --- a/config/queue.php +++ b/config/queue.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); return [ diff --git a/config/session.php b/config/session.php index 09becf087b..f3cb244e20 100644 --- a/config/session.php +++ b/config/session.php @@ -36,5 +36,5 @@ return [ 'domain' => env('COOKIE_DOMAIN', null), 'secure' => env('COOKIE_SECURE', null), 'http_only' => true, - 'same_site' => null, + 'same_site' => env('COOKIE_SAMESITE','lax'), ]; diff --git a/config/twigbridge.php b/config/twigbridge.php index 14742bd55c..cb94103aac 100644 --- a/config/twigbridge.php +++ b/config/twigbridge.php @@ -1,5 +1,26 @@ . + */ + declare(strict_types=1); /** diff --git a/config/view.php b/config/view.php index 88dcd694b2..309f703e82 100644 --- a/config/view.php +++ b/config/view.php @@ -21,13 +21,6 @@ declare(strict_types=1); -// simple hack to force v2. Used for demo until next release. -$layout = env('FIREFLY_III_LAYOUT', 'v1'); -if (isset($_GET['layout']) && 'v2' === $_GET['layout'] && 'demo@firefly' === env('DEMO_USERNAME')) { - $layout = 'v2'; -} - - return [ /* |-------------------------------------------------------------------------- @@ -41,7 +34,7 @@ return [ */ 'paths' => [ - realpath(base_path(sprintf('resources/views/%s', $layout))), + realpath(base_path('resources/views')), ], /* @@ -55,6 +48,6 @@ return [ | */ - 'compiled' => realpath(storage_path(sprintf('framework/views/%s', $layout))), + 'compiled' => realpath(storage_path('framework/views')), ]; diff --git a/database/factories/EmptyFactory.php b/database/factories/EmptyFactory.php index 0c4adc7fc9..115ac4cd9e 100644 --- a/database/factories/EmptyFactory.php +++ b/database/factories/EmptyFactory.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); /** @var \Illuminate\Database\Eloquent\Factory $factory */ diff --git a/database/migrations/2016_06_16_000000_create_support_tables.php b/database/migrations/2016_06_16_000000_create_support_tables.php index 077eddc433..4d158e268f 100644 --- a/database/migrations/2016_06_16_000000_create_support_tables.php +++ b/database/migrations/2016_06_16_000000_create_support_tables.php @@ -25,6 +25,7 @@ use Illuminate\Database\Schema\Blueprint; /** * Class CreateSupportTables. + * * @codeCoverageIgnore */ class CreateSupportTables extends Migration @@ -82,23 +83,6 @@ class CreateSupportTables extends Migration } } - private function createConfigurationTable(): void - { - if (!Schema::hasTable('configuration')) { - Schema::create( - 'configuration', - static function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->string('name', 50); - $table->text('data'); - $table->unique(['name']); - } - ); - } - } - private function createCurrencyTable(): void { if (!Schema::hasTable('transaction_currencies')) { @@ -119,6 +103,24 @@ class CreateSupportTables extends Migration } } + private function createTransactionTypeTable(): void + { + if (!Schema::hasTable('transaction_types')) { + Schema::create( + 'transaction_types', + static function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->string('type', 50); + + // type must be unique. + $table->unique(['type']); + } + ); + } + } + private function createJobsTable(): void { if (!Schema::hasTable('jobs')) { @@ -155,24 +157,6 @@ class CreateSupportTables extends Migration } } - private function createPermissionRoleTable(): void - { - if (!Schema::hasTable('permission_role')) { - Schema::create( - 'permission_role', - static function (Blueprint $table) { - $table->integer('permission_id')->unsigned(); - $table->integer('role_id')->unsigned(); - - $table->foreign('permission_id')->references('id')->on('permissions')->onUpdate('cascade')->onDelete('cascade'); - $table->foreign('role_id')->references('id')->on('roles')->onUpdate('cascade')->onDelete('cascade'); - - $table->primary(['permission_id', 'role_id']); - } - ); - } - } - private function createPermissionsTable(): void { if (!Schema::hasTable('permissions')) { @@ -205,6 +189,24 @@ class CreateSupportTables extends Migration } } + private function createPermissionRoleTable(): void + { + if (!Schema::hasTable('permission_role')) { + Schema::create( + 'permission_role', + static function (Blueprint $table) { + $table->integer('permission_id')->unsigned(); + $table->integer('role_id')->unsigned(); + + $table->foreign('permission_id')->references('id')->on('permissions')->onUpdate('cascade')->onDelete('cascade'); + $table->foreign('role_id')->references('id')->on('roles')->onUpdate('cascade')->onDelete('cascade'); + + $table->primary(['permission_id', 'role_id']); + } + ); + } + } + private function createSessionsTable(): void { if (!Schema::hasTable('sessions')) { @@ -222,19 +224,18 @@ class CreateSupportTables extends Migration } } - private function createTransactionTypeTable(): void + private function createConfigurationTable(): void { - if (!Schema::hasTable('transaction_types')) { + if (!Schema::hasTable('configuration')) { Schema::create( - 'transaction_types', + 'configuration', static function (Blueprint $table) { $table->increments('id'); $table->timestamps(); $table->softDeletes(); - $table->string('type', 50); - - // type must be unique. - $table->unique(['type']); + $table->string('name', 50); + $table->text('data'); + $table->unique(['name']); } ); } diff --git a/database/migrations/2016_06_16_000001_create_users_table.php b/database/migrations/2016_06_16_000001_create_users_table.php index 412593602d..51202b645f 100644 --- a/database/migrations/2016_06_16_000001_create_users_table.php +++ b/database/migrations/2016_06_16_000001_create_users_table.php @@ -25,6 +25,7 @@ use Illuminate\Database\Schema\Blueprint; /** * Class CreateUsersTable. + * * @codeCoverageIgnore */ class CreateUsersTable extends Migration diff --git a/database/migrations/2016_06_16_000002_create_main_tables.php b/database/migrations/2016_06_16_000002_create_main_tables.php index f0ab7205b9..9fb67c46ac 100644 --- a/database/migrations/2016_06_16_000002_create_main_tables.php +++ b/database/migrations/2016_06_16_000002_create_main_tables.php @@ -25,6 +25,7 @@ use Illuminate\Database\Schema\Blueprint; /** * Class CreateMainTables. + * * @codeCoverageIgnore */ class CreateMainTables extends Migration @@ -122,6 +123,44 @@ class CreateMainTables extends Migration } } + private function createPiggyBanksTable(): void + { + if (!Schema::hasTable('piggy_banks')) { + Schema::create( + 'piggy_banks', + static function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('account_id', false, true); + $table->string('name', 1024); + $table->decimal('targetamount', 22, 12); + $table->date('startdate')->nullable(); + $table->date('targetdate')->nullable(); + $table->integer('order', false, true)->default(0); + $table->boolean('active')->default(0); + $table->boolean('encrypted')->default(1); + $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); + } + ); + } + + if (!Schema::hasTable('piggy_bank_repetitions')) { + Schema::create( + 'piggy_bank_repetitions', + static function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->integer('piggy_bank_id', false, true); + $table->date('startdate')->nullable(); + $table->date('targetdate')->nullable(); + $table->decimal('currentamount', 22, 12); + $table->foreign('piggy_bank_id')->references('id')->on('piggy_banks')->onDelete('cascade'); + } + ); + } + } + private function createAttachmentsTable(): void { if (!Schema::hasTable('attachments')) { @@ -283,44 +322,6 @@ class CreateMainTables extends Migration } } - private function createPiggyBanksTable(): void - { - if (!Schema::hasTable('piggy_banks')) { - Schema::create( - 'piggy_banks', - static function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->integer('account_id', false, true); - $table->string('name', 1024); - $table->decimal('targetamount', 22, 12); - $table->date('startdate')->nullable(); - $table->date('targetdate')->nullable(); - $table->integer('order', false, true)->default(0); - $table->boolean('active')->default(0); - $table->boolean('encrypted')->default(1); - $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); - } - ); - } - - if (!Schema::hasTable('piggy_bank_repetitions')) { - Schema::create( - 'piggy_bank_repetitions', - static function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->integer('piggy_bank_id', false, true); - $table->date('startdate')->nullable(); - $table->date('targetdate')->nullable(); - $table->decimal('currentamount', 22, 12); - $table->foreign('piggy_bank_id')->references('id')->on('piggy_banks')->onDelete('cascade'); - } - ); - } - } - private function createPreferencesTable(): void { if (!Schema::hasTable('preferences')) { 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 2314ef38c4..237821a2c4 100644 --- a/database/migrations/2016_08_25_091522_changes_for_3101.php +++ b/database/migrations/2016_08_25_091522_changes_for_3101.php @@ -25,6 +25,7 @@ use Illuminate\Database\Schema\Blueprint; /** * Class ChangesFor3101. + * * @codeCoverageIgnore */ class ChangesFor3101 extends Migration diff --git a/database/migrations/2016_09_12_121359_fix_nullables.php b/database/migrations/2016_09_12_121359_fix_nullables.php index 05cd49e550..34c857cddb 100644 --- a/database/migrations/2016_09_12_121359_fix_nullables.php +++ b/database/migrations/2016_09_12_121359_fix_nullables.php @@ -25,6 +25,7 @@ use Illuminate\Database\Schema\Blueprint; /** * Class FixNullables. + * * @codeCoverageIgnore */ class FixNullables extends Migration diff --git a/database/migrations/2016_10_09_150037_expand_transactions_table.php b/database/migrations/2016_10_09_150037_expand_transactions_table.php index 0d4274a947..162da9df5e 100644 --- a/database/migrations/2016_10_09_150037_expand_transactions_table.php +++ b/database/migrations/2016_10_09_150037_expand_transactions_table.php @@ -25,6 +25,7 @@ use Illuminate\Database\Schema\Blueprint; /** * Class ExpandTransactionsTable. + * * @codeCoverageIgnore */ class ExpandTransactionsTable extends Migration diff --git a/database/migrations/2016_10_22_075804_changes_for_v410.php b/database/migrations/2016_10_22_075804_changes_for_v410.php index 73ee4de023..5d53fc84e3 100644 --- a/database/migrations/2016_10_22_075804_changes_for_v410.php +++ b/database/migrations/2016_10_22_075804_changes_for_v410.php @@ -25,6 +25,7 @@ use Illuminate\Database\Schema\Blueprint; /** * Class ChangesForV410. + * * @codeCoverageIgnore */ class ChangesForV410 extends Migration diff --git a/database/migrations/2016_11_24_210552_changes_for_v420.php b/database/migrations/2016_11_24_210552_changes_for_v420.php index 1d80ba5742..3e52c1578b 100644 --- a/database/migrations/2016_11_24_210552_changes_for_v420.php +++ b/database/migrations/2016_11_24_210552_changes_for_v420.php @@ -25,6 +25,7 @@ use Illuminate\Database\Schema\Blueprint; /** * Class ChangesForV420. + * * @codeCoverageIgnore */ class ChangesForV420 extends Migration diff --git a/database/migrations/2016_12_22_150431_changes_for_v430.php b/database/migrations/2016_12_22_150431_changes_for_v430.php index 34c7995796..7466b224d7 100644 --- a/database/migrations/2016_12_22_150431_changes_for_v430.php +++ b/database/migrations/2016_12_22_150431_changes_for_v430.php @@ -25,6 +25,7 @@ use Illuminate\Database\Schema\Blueprint; /** * Class ChangesForV430. + * * @codeCoverageIgnore */ class ChangesForV430 extends Migration diff --git a/database/migrations/2016_12_28_203205_changes_for_v431.php b/database/migrations/2016_12_28_203205_changes_for_v431.php index f07d68c129..57eac036c3 100644 --- a/database/migrations/2016_12_28_203205_changes_for_v431.php +++ b/database/migrations/2016_12_28_203205_changes_for_v431.php @@ -25,6 +25,7 @@ use Illuminate\Database\Schema\Blueprint; /** * Class ChangesForV431. + * * @codeCoverageIgnore */ class ChangesForV431 extends Migration diff --git a/database/migrations/2017_04_13_163623_changes_for_v440.php b/database/migrations/2017_04_13_163623_changes_for_v440.php index 63b3818b70..772ab5b783 100644 --- a/database/migrations/2017_04_13_163623_changes_for_v440.php +++ b/database/migrations/2017_04_13_163623_changes_for_v440.php @@ -25,6 +25,7 @@ use Illuminate\Database\Schema\Blueprint; /** * Class ChangesForV440. + * * @codeCoverageIgnore */ class ChangesForV440 extends Migration diff --git a/database/migrations/2017_06_02_105232_changes_for_v450.php b/database/migrations/2017_06_02_105232_changes_for_v450.php index 5fb701d70c..351324af06 100644 --- a/database/migrations/2017_06_02_105232_changes_for_v450.php +++ b/database/migrations/2017_06_02_105232_changes_for_v450.php @@ -25,6 +25,7 @@ use Illuminate\Database\Schema\Blueprint; /** * Class ChangesForV450. + * * @codeCoverageIgnore */ class ChangesForV450 extends Migration diff --git a/database/migrations/2017_08_20_062014_changes_for_v470.php b/database/migrations/2017_08_20_062014_changes_for_v470.php index e6ca3225f5..0c2471d80c 100644 --- a/database/migrations/2017_08_20_062014_changes_for_v470.php +++ b/database/migrations/2017_08_20_062014_changes_for_v470.php @@ -26,6 +26,7 @@ use Illuminate\Support\Facades\Schema; /** * Class ChangesForV470. + * * @codeCoverageIgnore */ class ChangesForV470 extends Migration diff --git a/database/migrations/2017_11_04_170844_changes_for_v470a.php b/database/migrations/2017_11_04_170844_changes_for_v470a.php index 97ff6d47ec..94fab9c06d 100644 --- a/database/migrations/2017_11_04_170844_changes_for_v470a.php +++ b/database/migrations/2017_11_04_170844_changes_for_v470a.php @@ -26,6 +26,7 @@ use Illuminate\Support\Facades\Schema; /** * Class ChangesForV470a. + * * @codeCoverageIgnore */ class ChangesForV470a extends Migration diff --git a/database/migrations/2018_01_01_000001_create_oauth_auth_codes_table.php b/database/migrations/2018_01_01_000001_create_oauth_auth_codes_table.php index c742de5ac5..9936125826 100644 --- a/database/migrations/2018_01_01_000001_create_oauth_auth_codes_table.php +++ b/database/migrations/2018_01_01_000001_create_oauth_auth_codes_table.php @@ -28,6 +28,7 @@ use Illuminate\Support\Facades\Schema; /** * Class CreateOauthAuthCodesTable. + * * @codeCoverageIgnore */ class CreateOauthAuthCodesTable extends Migration @@ -48,13 +49,13 @@ class CreateOauthAuthCodesTable extends Migration { Schema::create( 'oauth_auth_codes', static function (Blueprint $table) { - $table->string('id', 100)->primary(); - $table->integer('user_id'); - $table->integer('client_id'); - $table->text('scopes')->nullable(); - $table->boolean('revoked'); - $table->dateTime('expires_at')->nullable(); - } + $table->string('id', 100)->primary(); + $table->integer('user_id'); + $table->integer('client_id'); + $table->text('scopes')->nullable(); + $table->boolean('revoked'); + $table->dateTime('expires_at')->nullable(); + } ); } } diff --git a/database/migrations/2018_01_01_000002_create_oauth_access_tokens_table.php b/database/migrations/2018_01_01_000002_create_oauth_access_tokens_table.php index fa1dd8a426..a80077fde9 100644 --- a/database/migrations/2018_01_01_000002_create_oauth_access_tokens_table.php +++ b/database/migrations/2018_01_01_000002_create_oauth_access_tokens_table.php @@ -28,6 +28,7 @@ use Illuminate\Support\Facades\Schema; /** * Class CreateOauthAccessTokensTable. + * * @codeCoverageIgnore */ class CreateOauthAccessTokensTable extends Migration @@ -48,15 +49,15 @@ class CreateOauthAccessTokensTable extends Migration { Schema::create( 'oauth_access_tokens', static function (Blueprint $table) { - $table->string('id', 100)->primary(); - $table->integer('user_id')->index()->nullable(); - $table->integer('client_id'); - $table->string('name')->nullable(); - $table->text('scopes')->nullable(); - $table->boolean('revoked'); - $table->timestamps(); - $table->dateTime('expires_at')->nullable(); - } + $table->string('id', 100)->primary(); + $table->integer('user_id')->index()->nullable(); + $table->integer('client_id'); + $table->string('name')->nullable(); + $table->text('scopes')->nullable(); + $table->boolean('revoked'); + $table->timestamps(); + $table->dateTime('expires_at')->nullable(); + } ); } } diff --git a/database/migrations/2018_01_01_000003_create_oauth_refresh_tokens_table.php b/database/migrations/2018_01_01_000003_create_oauth_refresh_tokens_table.php index 8be1de2134..945fcd37b2 100644 --- a/database/migrations/2018_01_01_000003_create_oauth_refresh_tokens_table.php +++ b/database/migrations/2018_01_01_000003_create_oauth_refresh_tokens_table.php @@ -28,6 +28,7 @@ use Illuminate\Support\Facades\Schema; /** * Class CreateOauthRefreshTokensTable. + * * @codeCoverageIgnore */ class CreateOauthRefreshTokensTable extends Migration @@ -48,11 +49,11 @@ class CreateOauthRefreshTokensTable extends Migration { Schema::create( 'oauth_refresh_tokens', static function (Blueprint $table) { - $table->string('id', 100)->primary(); - $table->string('access_token_id', 100)->index(); - $table->boolean('revoked'); - $table->dateTime('expires_at')->nullable(); - } + $table->string('id', 100)->primary(); + $table->string('access_token_id', 100)->index(); + $table->boolean('revoked'); + $table->dateTime('expires_at')->nullable(); + } ); } } diff --git a/database/migrations/2018_01_01_000004_create_oauth_clients_table.php b/database/migrations/2018_01_01_000004_create_oauth_clients_table.php index ebac9eef8d..a98019c89b 100644 --- a/database/migrations/2018_01_01_000004_create_oauth_clients_table.php +++ b/database/migrations/2018_01_01_000004_create_oauth_clients_table.php @@ -28,6 +28,7 @@ use Illuminate\Support\Facades\Schema; /** * Class CreateOauthClientsTable. + * * @codeCoverageIgnore */ class CreateOauthClientsTable extends Migration @@ -48,16 +49,16 @@ class CreateOauthClientsTable extends Migration { Schema::create( 'oauth_clients', static function (Blueprint $table) { - $table->increments('id'); - $table->integer('user_id')->index()->nullable(); - $table->string('name'); - $table->string('secret', 100); - $table->text('redirect'); - $table->boolean('personal_access_client'); - $table->boolean('password_client'); - $table->boolean('revoked'); - $table->timestamps(); - } + $table->increments('id'); + $table->integer('user_id')->index()->nullable(); + $table->string('name'); + $table->string('secret', 100); + $table->text('redirect'); + $table->boolean('personal_access_client'); + $table->boolean('password_client'); + $table->boolean('revoked'); + $table->timestamps(); + } ); } } diff --git a/database/migrations/2018_01_01_000005_create_oauth_personal_access_clients_table.php b/database/migrations/2018_01_01_000005_create_oauth_personal_access_clients_table.php index b8dce06bb1..95b52f4e1f 100644 --- a/database/migrations/2018_01_01_000005_create_oauth_personal_access_clients_table.php +++ b/database/migrations/2018_01_01_000005_create_oauth_personal_access_clients_table.php @@ -28,6 +28,7 @@ use Illuminate\Support\Facades\Schema; /** * Class CreateOauthPersonalAccessClientsTable. + * * @codeCoverageIgnore */ class CreateOauthPersonalAccessClientsTable extends Migration @@ -48,10 +49,10 @@ class CreateOauthPersonalAccessClientsTable extends Migration { Schema::create( 'oauth_personal_access_clients', static function (Blueprint $table) { - $table->increments('id'); - $table->integer('client_id')->index(); - $table->timestamps(); - } + $table->increments('id'); + $table->integer('client_id')->index(); + $table->timestamps(); + } ); } } diff --git a/database/migrations/2018_03_19_141348_changes_for_v472.php b/database/migrations/2018_03_19_141348_changes_for_v472.php index 66a87c26d3..060d83cf4b 100644 --- a/database/migrations/2018_03_19_141348_changes_for_v472.php +++ b/database/migrations/2018_03_19_141348_changes_for_v472.php @@ -27,6 +27,7 @@ use Illuminate\Database\Schema\Blueprint; /** * Class ChangesForV472. + * * @codeCoverageIgnore */ class ChangesForV472 extends Migration diff --git a/database/migrations/2018_04_07_210913_changes_for_v473.php b/database/migrations/2018_04_07_210913_changes_for_v473.php index 62aca6b071..d659877176 100644 --- a/database/migrations/2018_04_07_210913_changes_for_v473.php +++ b/database/migrations/2018_04_07_210913_changes_for_v473.php @@ -28,6 +28,7 @@ use Illuminate\Support\Facades\Schema; /** * Class ChangesForV473. + * * @codeCoverageIgnore */ class ChangesForV473 extends Migration diff --git a/database/migrations/2018_04_29_174524_changes_for_v474.php b/database/migrations/2018_04_29_174524_changes_for_v474.php index 5d055ce2f5..b1e9088b78 100644 --- a/database/migrations/2018_04_29_174524_changes_for_v474.php +++ b/database/migrations/2018_04_29_174524_changes_for_v474.php @@ -27,6 +27,7 @@ use Illuminate\Database\Schema\Blueprint; /** * Class ChangesForV474. + * * @codeCoverageIgnore */ class ChangesForV474 extends Migration diff --git a/database/migrations/2018_06_08_200526_changes_for_v475.php b/database/migrations/2018_06_08_200526_changes_for_v475.php index dce990d133..166d6dfbcf 100644 --- a/database/migrations/2018_06_08_200526_changes_for_v475.php +++ b/database/migrations/2018_06_08_200526_changes_for_v475.php @@ -27,6 +27,7 @@ use Illuminate\Database\Schema\Blueprint; /** * Class ChangesForV475. + * * @codeCoverageIgnore */ class ChangesForV475 extends Migration @@ -56,92 +57,92 @@ class ChangesForV475 extends Migration { Schema::create( 'recurrences', static function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->integer('user_id', false, true); - $table->integer('transaction_type_id', false, true); + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('user_id', false, true); + $table->integer('transaction_type_id', false, true); - $table->string('title', 1024); - $table->text('description'); + $table->string('title', 1024); + $table->text('description'); - $table->date('first_date'); - $table->date('repeat_until')->nullable(); - $table->date('latest_date')->nullable(); - $table->smallInteger('repetitions', false, true); + $table->date('first_date'); + $table->date('repeat_until')->nullable(); + $table->date('latest_date')->nullable(); + $table->smallInteger('repetitions', false, true); - $table->boolean('apply_rules')->default(true); - $table->boolean('active')->default(true); + $table->boolean('apply_rules')->default(true); + $table->boolean('active')->default(true); - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - $table->foreign('transaction_type_id')->references('id')->on('transaction_types')->onDelete('cascade'); - } + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + $table->foreign('transaction_type_id')->references('id')->on('transaction_types')->onDelete('cascade'); + } ); Schema::create( 'recurrences_transactions', static function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->integer('recurrence_id', false, true); - $table->integer('transaction_currency_id', false, true); - $table->integer('foreign_currency_id', false, true)->nullable(); - $table->integer('source_id', false, true); - $table->integer('destination_id', false, true); + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('recurrence_id', false, true); + $table->integer('transaction_currency_id', false, true); + $table->integer('foreign_currency_id', false, true)->nullable(); + $table->integer('source_id', false, true); + $table->integer('destination_id', false, true); - $table->decimal('amount', 22, 12); - $table->decimal('foreign_amount', 22, 12)->nullable(); - $table->string('description', 1024); + $table->decimal('amount', 22, 12); + $table->decimal('foreign_amount', 22, 12)->nullable(); + $table->string('description', 1024); - $table->foreign('recurrence_id')->references('id')->on('recurrences')->onDelete('cascade'); - $table->foreign('transaction_currency_id')->references('id')->on('transaction_currencies')->onDelete('cascade'); - $table->foreign('foreign_currency_id')->references('id')->on('transaction_currencies')->onDelete('set null'); - $table->foreign('source_id')->references('id')->on('accounts')->onDelete('cascade'); - $table->foreign('destination_id')->references('id')->on('accounts')->onDelete('cascade'); - } + $table->foreign('recurrence_id')->references('id')->on('recurrences')->onDelete('cascade'); + $table->foreign('transaction_currency_id')->references('id')->on('transaction_currencies')->onDelete('cascade'); + $table->foreign('foreign_currency_id')->references('id')->on('transaction_currencies')->onDelete('set null'); + $table->foreign('source_id')->references('id')->on('accounts')->onDelete('cascade'); + $table->foreign('destination_id')->references('id')->on('accounts')->onDelete('cascade'); + } ); Schema::create( 'recurrences_repetitions', static function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->integer('recurrence_id', false, true); - $table->string('repetition_type', 50); - $table->string('repetition_moment', 50); - $table->smallInteger('repetition_skip', false, true); - $table->smallInteger('weekend', false, true); + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('recurrence_id', false, true); + $table->string('repetition_type', 50); + $table->string('repetition_moment', 50); + $table->smallInteger('repetition_skip', false, true); + $table->smallInteger('weekend', false, true); - $table->foreign('recurrence_id')->references('id')->on('recurrences')->onDelete('cascade'); - } + $table->foreign('recurrence_id')->references('id')->on('recurrences')->onDelete('cascade'); + } ); Schema::create( 'recurrences_meta', static function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->integer('recurrence_id', false, true); + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('recurrence_id', false, true); - $table->string('name', 50); - $table->text('value'); + $table->string('name', 50); + $table->text('value'); - $table->foreign('recurrence_id')->references('id')->on('recurrences')->onDelete('cascade'); - } + $table->foreign('recurrence_id')->references('id')->on('recurrences')->onDelete('cascade'); + } ); Schema::create( 'rt_meta', static function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->integer('rt_id', false, true); + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('rt_id', false, true); - $table->string('name', 50); - $table->text('value'); + $table->string('name', 50); + $table->text('value'); - $table->foreign('rt_id')->references('id')->on('recurrences_transactions')->onDelete('cascade'); - } + $table->foreign('rt_id')->references('id')->on('recurrences_transactions')->onDelete('cascade'); + } ); } } diff --git a/database/migrations/2018_09_05_195147_changes_for_v477.php b/database/migrations/2018_09_05_195147_changes_for_v477.php index 5a49e474bd..d9f9678942 100644 --- a/database/migrations/2018_09_05_195147_changes_for_v477.php +++ b/database/migrations/2018_09_05_195147_changes_for_v477.php @@ -27,6 +27,7 @@ use Illuminate\Database\Schema\Blueprint; /** * Class ChangesForV477. + * * @codeCoverageIgnore */ class ChangesForV477 extends Migration @@ -42,12 +43,12 @@ class ChangesForV477 extends Migration 'budget_limits', static function (Blueprint $table) { // cannot drop foreign keys in SQLite: - if ('sqlite' !== config('database.default')) { - $table->dropForeign('budget_limits_transaction_currency_id_foreign'); - } - - $table->dropColumn(['transaction_currency_id']); + if ('sqlite' !== config('database.default')) { + $table->dropForeign('budget_limits_transaction_currency_id_foreign'); } + + $table->dropColumn(['transaction_currency_id']); + } ); } diff --git a/database/migrations/2018_11_06_172532_changes_for_v479.php b/database/migrations/2018_11_06_172532_changes_for_v479.php index 90df8e2499..3001eea2d3 100644 --- a/database/migrations/2018_11_06_172532_changes_for_v479.php +++ b/database/migrations/2018_11_06_172532_changes_for_v479.php @@ -27,6 +27,7 @@ use Illuminate\Database\Schema\Blueprint; /** * Class ChangesForV479. + * * @codeCoverageIgnore */ class ChangesForV479 extends Migration @@ -40,8 +41,8 @@ class ChangesForV479 extends Migration { Schema::table( 'transaction_currencies', static function (Blueprint $table) { - $table->dropColumn(['enabled']); - } + $table->dropColumn(['enabled']); + } ); } diff --git a/database/migrations/2019_01_28_193833_changes_for_v4710.php b/database/migrations/2019_01_28_193833_changes_for_v4710.php index c32624a974..dfda36398d 100644 --- a/database/migrations/2019_01_28_193833_changes_for_v4710.php +++ b/database/migrations/2019_01_28_193833_changes_for_v4710.php @@ -28,6 +28,7 @@ use Illuminate\Support\Facades\Schema; /** * Class ChangesForV4710 + * * @codeCoverageIgnore */ class ChangesForV4710 extends Migration @@ -54,14 +55,14 @@ class ChangesForV4710 extends Migration if (!Schema::hasTable('transaction_groups')) { Schema::create( 'transaction_groups', static function (Blueprint $table) { - $table->increments('id'); - $table->timestamps(); - $table->softDeletes(); - $table->integer('user_id', false, true); - $table->string('title', 1024)->nullable(); + $table->increments('id'); + $table->timestamps(); + $table->softDeletes(); + $table->integer('user_id', false, true); + $table->string('title', 1024)->nullable(); - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - } + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + } ); } diff --git a/database/migrations/2019_02_05_055516_changes_for_v4711.php b/database/migrations/2019_02_05_055516_changes_for_v4711.php index 9cfc3f200c..1829ede413 100644 --- a/database/migrations/2019_02_05_055516_changes_for_v4711.php +++ b/database/migrations/2019_02_05_055516_changes_for_v4711.php @@ -28,6 +28,7 @@ use Illuminate\Support\Facades\Schema; /** * Class ChangesForV4711 + * * @codeCoverageIgnore */ class ChangesForV4711 extends Migration @@ -60,14 +61,14 @@ class ChangesForV4711 extends Migration */ Schema::table( 'transaction_journals', static function (Blueprint $table) { - $table->dateTime('date')->change(); - } + $table->dateTime('date')->change(); + } ); Schema::table( 'preferences', static function (Blueprint $table) { - $table->text('data')->nullable()->change(); - } + $table->text('data')->nullable()->change(); + } ); } } diff --git a/database/migrations/2019_02_11_170529_changes_for_v4712.php b/database/migrations/2019_02_11_170529_changes_for_v4712.php index 24af68eb6f..8d235a6b4e 100644 --- a/database/migrations/2019_02_11_170529_changes_for_v4712.php +++ b/database/migrations/2019_02_11_170529_changes_for_v4712.php @@ -27,6 +27,7 @@ use Illuminate\Support\Facades\Schema; /** * Class ChangesForV4712. + * * @codeCoverageIgnore */ class ChangesForV4712 extends Migration @@ -59,8 +60,8 @@ class ChangesForV4712 extends Migration */ Schema::table( 'transaction_journals', static function (Blueprint $table) { - $table->dateTime('date')->change(); - } + $table->dateTime('date')->change(); + } ); } } diff --git a/database/migrations/2019_03_11_223700_fix_ldap_configuration.php b/database/migrations/2019_03_11_223700_fix_ldap_configuration.php index 4c3aa712a2..9f16faaa27 100644 --- a/database/migrations/2019_03_11_223700_fix_ldap_configuration.php +++ b/database/migrations/2019_03_11_223700_fix_ldap_configuration.php @@ -27,6 +27,7 @@ use Illuminate\Support\Facades\Schema; /** * Class FixLdapConfiguration. + * * @codeCoverageIgnore */ class FixLdapConfiguration extends Migration @@ -40,8 +41,8 @@ class FixLdapConfiguration extends Migration { Schema::table( 'users', static function (Blueprint $table) { - $table->dropColumn(['objectguid']); - } + $table->dropColumn(['objectguid']); + } ); } @@ -59,8 +60,8 @@ class FixLdapConfiguration extends Migration */ Schema::table( 'users', static function (Blueprint $table) { - $table->uuid('objectguid')->nullable()->after('id'); - } + $table->uuid('objectguid')->nullable()->after('id'); + } ); } } diff --git a/database/migrations/2019_03_22_183214_changes_for_v480.php b/database/migrations/2019_03_22_183214_changes_for_v480.php index 71f0b98752..d68a2fbffe 100644 --- a/database/migrations/2019_03_22_183214_changes_for_v480.php +++ b/database/migrations/2019_03_22_183214_changes_for_v480.php @@ -26,6 +26,7 @@ use Illuminate\Database\Schema\Blueprint; /** * Class ChangesForV480. + * * @codeCoverageIgnore */ class ChangesForV480 extends Migration @@ -50,14 +51,14 @@ class ChangesForV480 extends Migration ); Schema::table( 'rule_groups', static function (Blueprint $table) { - $table->dropColumn('stop_processing'); - } + $table->dropColumn('stop_processing'); + } ); Schema::table( 'users', static function (Blueprint $table) { - $table->dropColumn('mfa_secret'); - } + $table->dropColumn('mfa_secret'); + } ); } @@ -84,13 +85,13 @@ class ChangesForV480 extends Migration ); Schema::table( 'rule_groups', static function (Blueprint $table) { - $table->boolean('stop_processing')->default(false); - } + $table->boolean('stop_processing')->default(false); + } ); Schema::table( 'users', static function (Blueprint $table) { - $table->string('mfa_secret', 50)->nullable(); - } + $table->string('mfa_secret', 50)->nullable(); + } ); } } diff --git a/database/migrations/2019_12_28_191351_make_locations_table.php b/database/migrations/2019_12_28_191351_make_locations_table.php index 30eefa6864..0f92217535 100644 --- a/database/migrations/2019_12_28_191351_make_locations_table.php +++ b/database/migrations/2019_12_28_191351_make_locations_table.php @@ -28,6 +28,7 @@ use Illuminate\Support\Facades\Schema; /** * Class MakeLocationsTable. + * * @codeCoverageIgnore */ class MakeLocationsTable extends Migration @@ -51,17 +52,17 @@ class MakeLocationsTable extends Migration { Schema::create( 'locations', static function (Blueprint $table) { - $table->bigIncrements('id'); - $table->timestamps(); - $table->softDeletes(); + $table->bigIncrements('id'); + $table->timestamps(); + $table->softDeletes(); - $table->integer('locatable_id', false, true); - $table->string('locatable_type', 255); + $table->integer('locatable_id', false, true); + $table->string('locatable_type', 255); - $table->decimal('latitude', 24, 12)->nullable(); - $table->decimal('longitude', 24, 12)->nullable(); - $table->smallInteger('zoom_level', false, true)->nullable(); - } + $table->decimal('latitude', 24, 12)->nullable(); + $table->decimal('longitude', 24, 12)->nullable(); + $table->smallInteger('zoom_level', false, true)->nullable(); + } ); } } diff --git a/database/migrations/2020_03_13_201950_changes_for_v520.php b/database/migrations/2020_03_13_201950_changes_for_v520.php index 7cfc93eeb2..72339f579b 100644 --- a/database/migrations/2020_03_13_201950_changes_for_v520.php +++ b/database/migrations/2020_03_13_201950_changes_for_v520.php @@ -28,6 +28,7 @@ use Illuminate\Database\Schema\Blueprint; /** * Class ChangesForV520. + * * @codeCoverageIgnore */ class ChangesForV520 extends Migration diff --git a/database/migrations/2020_06_07_063612_changes_for_v530.php b/database/migrations/2020_06_07_063612_changes_for_v530.php index afc64f15c0..9e20abed8b 100644 --- a/database/migrations/2020_06_07_063612_changes_for_v530.php +++ b/database/migrations/2020_06_07_063612_changes_for_v530.php @@ -27,6 +27,7 @@ use Illuminate\Database\Schema\Blueprint; /** * Class ChangesForV530 + * * @codeCoverageIgnore */ class ChangesForV530 extends Migration diff --git a/database/migrations/2020_06_30_202620_changes_for_v530a.php b/database/migrations/2020_06_30_202620_changes_for_v530a.php index c5f85a156d..b5412de5d4 100644 --- a/database/migrations/2020_06_30_202620_changes_for_v530a.php +++ b/database/migrations/2020_06_30_202620_changes_for_v530a.php @@ -28,6 +28,7 @@ use Illuminate\Support\Facades\Schema; /** * Class ChangesForV530a + * * @codeCoverageIgnore */ class ChangesForV530a extends Migration diff --git a/database/migrations/2020_07_24_162820_changes_for_v540.php b/database/migrations/2020_07_24_162820_changes_for_v540.php index 8940f8eb0c..990ea11eac 100644 --- a/database/migrations/2020_07_24_162820_changes_for_v540.php +++ b/database/migrations/2020_07_24_162820_changes_for_v540.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); use Illuminate\Database\Migrations\Migration; @@ -7,6 +28,7 @@ use Illuminate\Support\Facades\Schema; /** * Class ChangesForV540 + * * @codeCoverageIgnore */ class ChangesForV540 extends Migration @@ -64,8 +86,10 @@ class ChangesForV540 extends Migration // make column nullable: - Schema::table('oauth_clients', function (Blueprint $table) { + Schema::table( + 'oauth_clients', function (Blueprint $table) { $table->string('secret', 100)->nullable()->change(); - }); + } + ); } } diff --git a/database/migrations/2020_11_12_070604_changes_for_v550.php b/database/migrations/2020_11_12_070604_changes_for_v550.php index 6d3431f3a9..dc0c3cedde 100644 --- a/database/migrations/2020_11_12_070604_changes_for_v550.php +++ b/database/migrations/2020_11_12_070604_changes_for_v550.php @@ -1,4 +1,25 @@ . + */ + declare(strict_types=1); use Illuminate\Database\Migrations\Migration; @@ -80,6 +101,8 @@ class ChangesForV550 extends Migration $table->unsignedInteger('created_at'); } ); + // drop failed jobs table. + Schema::dropIfExists('failed_jobs'); // create new failed_jobs table. Schema::create( diff --git a/database/migrations/2021_03_12_061213_changes_for_v550b2.php b/database/migrations/2021_03_12_061213_changes_for_v550b2.php new file mode 100644 index 0000000000..707b29ebf9 --- /dev/null +++ b/database/migrations/2021_03_12_061213_changes_for_v550b2.php @@ -0,0 +1,44 @@ +dropForeign('type_foreign'); + $table->dropColumn('transaction_type_id'); + + } + ); + } + + /** + * Run the migrations. + * + * @return void + */ + public function up(): void + { + // expand recurrence transaction table + Schema::table( + 'recurrences_transactions', function (Blueprint $table) { + $table->integer('transaction_type_id', false, true)->nullable()->after('transaction_currency_id'); + $table->foreign('transaction_type_id', 'type_foreign')->references('id')->on('transaction_types')->onDelete('set null'); + } + ); + } +} diff --git a/database/seeders/TransactionCurrencySeeder.php b/database/seeders/TransactionCurrencySeeder.php index 039b2a3897..2e83a1f129 100644 --- a/database/seeders/TransactionCurrencySeeder.php +++ b/database/seeders/TransactionCurrencySeeder.php @@ -62,7 +62,7 @@ class TransactionCurrencySeeder extends Seeder $currencies[] = ['code' => 'ZAR', 'name' => 'South African rand', 'symbol' => 'R', 'decimal_places' => 2]; // asian currencies - $currencies[] = ['code' => 'JPY', 'name' => 'Japanese yen', 'symbol' => '¥', 'decimal_places' => 2]; + $currencies[] = ['code' => 'JPY', 'name' => 'Japanese yen', 'symbol' => '¥', 'decimal_places' => 0]; $currencies[] = ['code' => 'RMB', 'name' => 'Chinese yuan', 'symbol' => '¥', 'decimal_places' => 2]; $currencies[] = ['code' => 'RUB', 'name' => 'Russian ruble', 'symbol' => '₽', 'decimal_places' => 2]; $currencies[] = ['code' => 'INR', 'name' => 'Indian rupee', 'symbol' => '₹', 'decimal_places' => 2]; diff --git a/database/seeders/TransactionTypeSeeder.php b/database/seeders/TransactionTypeSeeder.php index 412246a673..97eb6c19d0 100644 --- a/database/seeders/TransactionTypeSeeder.php +++ b/database/seeders/TransactionTypeSeeder.php @@ -39,6 +39,7 @@ class TransactionTypeSeeder extends Seeder TransactionType::TRANSFER, TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION, + TransactionType::INVALID ]; foreach ($types as $type) { diff --git a/frontend/build.sh b/frontend/build.sh index 575379ec26..b5c117868f 100755 --- a/frontend/build.sh +++ b/frontend/build.sh @@ -1,5 +1,25 @@ #!/usr/bin/env bash +# +# build.sh +# Copyright (c) 2021 james@firefly-iii.org +# +# This file is part of Firefly III (https://github.com/firefly-iii). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + [ -d "~/Sites" ] && exit 1; # build translations. @@ -8,8 +28,11 @@ php /sites/FF3/dev/tools/cli.php ff3:json-translations v2 # remove old stuff rm -rf public/ rm -rf ../public/fonts +rm -rf ../public/images rm -rf ../public/v2/js rm -rf ../public/v2/css +mkdir -p public/js +mkdir -p public/css # build new stuff yarn install @@ -27,4 +50,4 @@ yarn prod cp -r fonts ../public # remove built stuff -rm -rf public \ No newline at end of file +rm -rf public diff --git a/frontend/mix-manifest.json b/frontend/mix-manifest.json index 4cefb1e099..cd0708d869 100644 --- a/frontend/mix-manifest.json +++ b/frontend/mix-manifest.json @@ -18,5 +18,7 @@ "/public/js/register.js": "/public/js/register.js", "/public/js/register.js.map": "/public/js/register.js.map", "/public/js/transactions/create.js": "/public/js/transactions/create.js", - "/public/js/transactions/create.js.map": "/public/js/transactions/create.js.map" + "/public/js/transactions/create.js.map": "/public/js/transactions/create.js.map", + "/public/js/transactions/edit.js": "/public/js/transactions/edit.js", + "/public/js/transactions/edit.js.map": "/public/js/transactions/edit.js.map" } diff --git a/frontend/package.json b/frontend/package.json index d4c7913448..a2b131ed1d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,16 +10,17 @@ "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" }, "devDependencies": { + "admin-lte": "^3.0", "axios": "^0.21", "cross-env": "^7.0", "laravel-mix": "^5.0.9", "laravel-mix-bundle-analyzer": "^1.0.5", - "lodash": "^4.17.20", + "lodash": "^4.17.21", "lodash.clonedeep": "^4.5.0", "node-forge": ">=0.10.0", "resolve-url-loader": "^3.1.2", - "sass": "^1.32.2", - "sass-loader": "^10.1.0", + "sass": "^1.32.8", + "sass-loader": "^10.1.1", "vue": "^2.6.12", "vue-i18n": "^8.22.2", "vue-template-compiler": "^2.6.12" @@ -27,20 +28,22 @@ "dependencies": { "@fortawesome/fontawesome-free": "^5.15.1", "@johmun/vue-tags-input": "^2.1.0", - "@popperjs/core": "^2.6.0", + "@popperjs/core": "^2.8.6", "bootstrap": "^4.5.3", "chart.js": "^2.9.4", "icheck-bootstrap": "^3.0.1", "jquery": "^3.5.1", "jquery-ui": "^1.12.1", + "leaflet": "^1.7.1", "overlayscrollbars": "^1.13.1", "popper.js": "^1.16.1", "tempusdominus": "^5.16.0", - "v-calendar": "^2.2.1", + "v-calendar": "^2.2.4", "vue-chartjs": "^3.5.1", "vue-router": "^3.4.9", "vue-simple-suggest": "^1.10.3", "vue-typeahead-bootstrap": "^2.5.5", + "vue2-leaflet": "^2.6.0", "vuex": "^3.6.0" } } diff --git a/frontend/render.sh b/frontend/render.sh index 704f5ae52b..da6d9b0363 100755 --- a/frontend/render.sh +++ b/frontend/render.sh @@ -1,5 +1,25 @@ #!/usr/bin/env bash +# +# render.sh +# Copyright (c) 2021 james@firefly-iii.org +# +# This file is part of Firefly III (https://github.com/firefly-iii). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + [ -d "~/Sites" ] && exit 1; # build translations. diff --git a/frontend/src/app.scss b/frontend/src/app.scss index eb12c42e7d..0ab440fcfe 100644 --- a/frontend/src/app.scss +++ b/frontend/src/app.scss @@ -25,10 +25,59 @@ // iCheck @import '~icheck-bootstrap/icheck-bootstrap.css'; // AdminLTE -@import 'dist/css/adminlte.css'; +//@import 'dist/css/adminlte.css'; + +//@import 'adminlte/scss/adminlte.css' + +//@import '~admin-lte/build/scss/AdminLTE'; + +// ADMIN LTE +@import '~bootstrap/scss/functions'; +@import '~admin-lte/build/scss/bootstrap-variables'; +@import '~bootstrap/scss/bootstrap'; + +// Variables and Mixins +// --------------------------------------------------- +@import '~admin-lte/build/scss/variables'; + +// Firefly III colors (?) +@import 'scss/variables'; + +@import '~admin-lte/build/scss/mixins'; +@import '~admin-lte/build/scss/parts/core'; + +// admin LTE components +@import '~admin-lte/build/scss/forms'; +@import '~admin-lte/build/scss/progress-bars'; +@import '~admin-lte/build/scss/cards'; +@import '~admin-lte/build/scss/modals'; +//@import '../toasts'; +@import '~admin-lte/build/scss/buttons'; +//@import '../callout'; +@import '~admin-lte/build/scss/alerts'; +@import '~admin-lte/build/scss/table'; +//@import '../carousel'; + +// admin LTE extra components +//@import '../small-box'; +@import '~admin-lte/build/scss/info-box'; +//@import '../timeline'; +//@import '../products'; +//@import '../direct-chat'; +//@import '../users-list'; +//@import '../social-widgets'; + +// admin LTE pages (unused) +// @import 'parts/pages'; + +// admin LTE plugins (unused) +// @import 'parts/plugins'; + +// admin LTE misc +@import '~admin-lte/build/scss/miscellaneous'; +@import '~admin-lte/build/scss/print'; +@import '~admin-lte/build/scss/text'; +@import '~admin-lte/build/scss/elevation'; +@import '~admin-lte/build/scss/colors'; - -// Bootstrap -// Already imported by AdminLTE -//@import '~bootstrap/scss/bootstrap'; diff --git a/frontend/src/classic/adminlte.js b/frontend/src/classic/adminlte.js index 9652e1d87a..120f6d9e83 100644 --- a/frontend/src/classic/adminlte.js +++ b/frontend/src/classic/adminlte.js @@ -1,3 +1,23 @@ +/* + * adminlte.js + * Copyright (c) 2021 james@firefly-iii.org + * + * This file is part of Firefly III (https://github.com/firefly-iii). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + /*! * AdminLTE v3.0.5 (https://adminlte.io) * Copyright 2014-2020 Colorlib diff --git a/frontend/src/components/accounts/Edit.vue b/frontend/src/components/accounts/Edit.vue index cd1f65fa36..e16f014afa 100644 --- a/frontend/src/components/accounts/Edit.vue +++ b/frontend/src/components/accounts/Edit.vue @@ -23,9 +23,9 @@ diff --git a/frontend/src/components/dashboard/MainAccountList.vue b/frontend/src/components/dashboard/MainAccountList.vue index 945c9c172e..8e791c48d2 100644 --- a/frontend/src/components/dashboard/MainAccountList.vue +++ b/frontend/src/components/dashboard/MainAccountList.vue @@ -19,17 +19,54 @@ --> diff --git a/frontend/src/components/dashboard/MainBillsList.vue b/frontend/src/components/dashboard/MainBillsList.vue index 6808d7dcb4..66d0ec27c7 100644 --- a/frontend/src/components/dashboard/MainBillsList.vue +++ b/frontend/src/components/dashboard/MainBillsList.vue @@ -23,7 +23,20 @@

{{ $t('firefly.bills') }}

-
+ +
+
+ +
+
+ +
+
+ +
+
+ +
@@ -34,47 +47,101 @@ -
{{ $t('firefly.bills') }}
{{ bill.attributes.name }} - ~{{ - Intl.NumberFormat(locale, {style: 'currency', currency: bill.attributes.currency_code}).format((parseFloat(bill.attributes.amount_min) + - parseFloat(bill.attributes.amount_max)) / 2) - }} -
+
{{ bill.attributes.name }} + (~ {{ + Intl.NumberFormat(locale, {style: 'currency', currency: bill.attributes.currency_code}).format((parseFloat(bill.attributes.amount_min) + + parseFloat(bill.attributes.amount_max)) / -2) + }}) + +
+ {{ bill.attributes.object_group_title }} +
- - {{ new Intl.DateTimeFormat(locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(new Date(payDate)) }} -
-
+ +
+
+ + {{ new Intl.DateTimeFormat(locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(new Date(payDate)) }}
+
diff --git a/frontend/src/components/dashboard/MainBudget.vue b/frontend/src/components/dashboard/MainBudget.vue deleted file mode 100644 index 0ac18cf8d5..0000000000 --- a/frontend/src/components/dashboard/MainBudget.vue +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - diff --git a/frontend/src/components/dashboard/MainBudgetList.vue b/frontend/src/components/dashboard/MainBudgetList.vue index 4d07858271..151ce5892d 100644 --- a/frontend/src/components/dashboard/MainBudgetList.vue +++ b/frontend/src/components/dashboard/MainBudgetList.vue @@ -21,42 +21,55 @@ - - diff --git a/frontend/src/components/dashboard/MainCategoryList.vue b/frontend/src/components/dashboard/MainCategoryList.vue index dceeaa7d7a..9c7515796c 100644 --- a/frontend/src/components/dashboard/MainCategoryList.vue +++ b/frontend/src/components/dashboard/MainCategoryList.vue @@ -23,22 +23,32 @@

{{ $t('firefly.categories') }}

-
+ +
+
+ +
+
+ +
+
+ +
+
+ +
or other required elements.\n\tthead: [ 1, \"
{{ category.name }} - -
-
+
+
{{ Intl.NumberFormat(locale, {style: 'currency', currency: category.currency_code}).format(category.spent) }} @@ -49,12 +59,13 @@
-
+
{{ Intl.NumberFormat(locale, {style: 'currency', currency: category.currency_code}).format(category.earned) }}   -
+
{{ Intl.NumberFormat(locale, {style: 'currency', currency: category.currency_code}).format(category.earned) }} @@ -70,12 +81,16 @@ diff --git a/frontend/src/components/dashboard/MainCreditList.vue b/frontend/src/components/dashboard/MainCreditList.vue index 6d559f3062..39fcb01882 100644 --- a/frontend/src/components/dashboard/MainCreditList.vue +++ b/frontend/src/components/dashboard/MainCreditList.vue @@ -23,16 +23,29 @@

{{ $t('firefly.revenue_accounts') }}

-
+ +
+
+ +
+
+ +
+
+ +
+
+ +
{{ entry.name }} -
-
+
+
{{ Intl.NumberFormat(locale, {style: 'currency', currency: entry.currency_code}).format(entry.difference_float) }} @@ -47,34 +60,76 @@
- - diff --git a/frontend/src/components/dashboard/MainDebitList.vue b/frontend/src/components/dashboard/MainDebitList.vue index 1c1ee0ef4d..cd7beffa79 100644 --- a/frontend/src/components/dashboard/MainDebitList.vue +++ b/frontend/src/components/dashboard/MainDebitList.vue @@ -23,16 +23,29 @@

{{ $t('firefly.expense_accounts') }}

-
+ +
+
+ +
+
+ +
+
+ +
+
+ +
{{ entry.name }} -
-
+
+
{{ Intl.NumberFormat(locale, {style: 'currency', currency: entry.currency_code}).format(entry.difference_float) }} @@ -47,32 +60,74 @@
diff --git a/frontend/src/components/dashboard/TopBoxes.vue b/frontend/src/components/dashboard/TopBoxes.vue index 1f72c8243f..3178b71133 100644 --- a/frontend/src/components/dashboard/TopBoxes.vue +++ b/frontend/src/components/dashboard/TopBoxes.vue @@ -25,9 +25,11 @@
- {{ $t("firefly.balance") }} + {{ $t("firefly.balance") }} + + - {{ balance.value_parsed }} + {{ balance.value_parsed }}
@@ -48,10 +50,11 @@
- {{ $t('firefly.bills_to_pay') }} - + {{ $t('firefly.bills_to_pay') }} + + - {{ balance.value_parsed }} + {{ balance.value_parsed }}
@@ -72,10 +75,11 @@
- {{ $t('firefly.left_to_spend') }} - + {{ $t('firefly.left_to_spend') }} + + - {{ left.value_parsed }} + {{ left.value_parsed }}
@@ -97,8 +101,10 @@
- {{ $t('firefly.net_worth') }} - {{ nw.value_parsed }} + {{ $t('firefly.net_worth') }} + + + {{ nw.value_parsed }}
@@ -117,6 +123,9 @@ \ No newline at end of file diff --git a/frontend/src/components/shared/transactions.js b/frontend/src/components/shared/transactions.js new file mode 100644 index 0000000000..d96cd66093 --- /dev/null +++ b/frontend/src/components/shared/transactions.js @@ -0,0 +1,166 @@ +/* + * transactions.js + * Copyright (c) 2021 james@firefly-iii.org + * + * This file is part of Firefly III (https://github.com/firefly-iii). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +export function getDefaultErrors() { + return { + description: [], + amount: [], + source: [], + destination: [], + currency: [], + foreign_currency: [], + foreign_amount: [], + date: [], + custom_dates: [], + budget: [], + category: [], + bill: [], + tags: [], + piggy_bank: [], + internal_reference: [], + external_url: [], + notes: [], + location: [] + }; +} + +export function getDefaultTransaction() { + return { + // basic + description: '', + transaction_journal_id: 0, + // accounts: + source_account_id: null, + source_account_name: null, + source_account_type: null, + + source_account_currency_id: null, + source_account_currency_code: null, + source_account_currency_symbol: null, + + destination_account_id: null, + destination_account_name: null, + destination_account_type: null, + + destination_account_currency_id: null, + destination_account_currency_code: null, + destination_account_currency_symbol: null, + + source_account: { + id: 0, + name: "", + name_with_balance: "", + type: "", + currency_id: 0, + currency_name: '', + currency_code: '', + currency_decimal_places: 2 + }, + destination_account: { + id: 0, + name: "", + type: "", + currency_id: 0, + currency_name: '', + currency_code: '', + currency_decimal_places: 2 + }, + + // amount: + amount: '', + currency_id: 0, + foreign_amount: '', + foreign_currency_id: 0, + + // meta data + category: null, + budget_id: 0, + bill_id: 0, + piggy_bank_id: 0, + tags: [], + + // optional date fields (6x): + interest_date: null, + book_date: null, + process_date: null, + due_date: null, + payment_date: null, + invoice_date: null, + + // optional other fields: + internal_reference: null, + external_url: null, + external_id: null, + notes: null, + + // transaction links: + links: [], + attachments: [], + // location: + zoom_level: null, + longitude: null, + latitude: null, + + // error handling + errors: {}, + } +} + +export function toW3CString(date) { + // https://gist.github.com/tristanlins/6585391 + let year = date.getFullYear(); + let month = date.getMonth(); + month++; + if (month < 10) { + month = '0' + month; + } + let day = date.getDate(); + if (day < 10) { + day = '0' + day; + } + let hours = date.getHours(); + if (hours < 10) { + hours = '0' + hours; + } + let minutes = date.getMinutes(); + if (minutes < 10) { + minutes = '0' + minutes; + } + let seconds = date.getSeconds(); + if (seconds < 10) { + seconds = '0' + seconds; + } + let offset = -date.getTimezoneOffset(); + let offsetHours = Math.abs(Math.floor(offset / 60)); + let offsetMinutes = Math.abs(offset) - offsetHours * 60; + if (offsetHours < 10) { + offsetHours = '0' + offsetHours; + } + if (offsetMinutes < 10) { + offsetMinutes = '0' + offsetMinutes; + } + let offsetSign = '+'; + if (offset < 0) { + offsetSign = '-'; + } + return year + '-' + month + '-' + day + + 'T' + hours + ':' + minutes + ':' + seconds + + offsetSign + offsetHours + ':' + offsetMinutes; +} \ No newline at end of file diff --git a/frontend/src/components/store/index.js b/frontend/src/components/store/index.js index 20c796968e..1c8f0a26d4 100644 --- a/frontend/src/components/store/index.js +++ b/frontend/src/components/store/index.js @@ -21,6 +21,8 @@ import Vue from 'vue' import Vuex, {createLogger} from 'vuex' import transactions_create from './modules/transactions/create'; +import transactions_edit from './modules/transactions/edit'; +import dashboard_index from './modules/dashboard/index'; Vue.use(Vuex) const debug = process.env.NODE_ENV !== 'production' @@ -31,7 +33,14 @@ export default new Vuex.Store( transactions: { namespaced: true, modules: { - create: transactions_create + create: transactions_create, + edit: transactions_edit + } + }, + dashboard: { + namespaced: true, + modules: { + index: dashboard_index } } }, @@ -43,7 +52,7 @@ export default new Vuex.Store( }, mutations: { setCurrencyPreference(state, payload) { - console.log('setCurrencyPreference', payload); + //console.log('setCurrencyPreference', payload); state.currencyPreference = payload.payload; }, initialiseStore(state) { @@ -77,9 +86,9 @@ export default new Vuex.Store( actions: { updateCurrencyPreference(context) { if (localStorage.currencyPreference) { - console.log('set from local storage.'); - console.log(localStorage.currencyPreference); - console.log({payload: JSON.parse(localStorage.currencyPreference)}); + //console.log('set from local storage.'); + //console.log(localStorage.currencyPreference); + //console.log({payload: JSON.parse(localStorage.currencyPreference)}); context.commit('setCurrencyPreference', {payload: JSON.parse(localStorage.currencyPreference)}); return; } @@ -93,8 +102,8 @@ export default new Vuex.Store( decimal_places: parseInt(response.data.data.attributes.decimal_places), }; localStorage.currencyPreference = JSON.stringify(currencyResponse); - console.log('getCurrencyPreference from server') - console.log(JSON.stringify(currencyResponse)); + //console.log('getCurrencyPreference from server') + //console.log(JSON.stringify(currencyResponse)); context.commit('setCurrencyPreference', {payload: currencyResponse}); }).catch(err => { // console.log('Got error response.'); diff --git a/frontend/src/components/store/modules/dashboard/index.js b/frontend/src/components/store/modules/dashboard/index.js new file mode 100644 index 0000000000..f214aadc46 --- /dev/null +++ b/frontend/src/components/store/modules/dashboard/index.js @@ -0,0 +1,218 @@ +/* + * index.js + * Copyright (c) 2020 james@firefly-iii.org + * + * This file is part of Firefly III (https://github.com/firefly-iii). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +// initial state +const state = () => ( + { + viewRange: 'default', + start: null, + end: null, + // default range: + defaultStart: null, + defaultEnd: null, + } +) + + +// getters +const getters = { + start: state => { + return state.start; + }, + end: state => { + return state.end; + }, + defaultStart: state => { + return state.defaultStart; + }, + defaultEnd: state => { + return state.defaultEnd; + }, + viewRange: state => { + return state.viewRange; + } +} + +// actions +const actions = { + initialiseStore(context) { + if ('default' === context.state.viewRange) { + axios.get('./api/v1/preferences/viewRange') + .then(response => { + let viewRange = response.data.data.attributes.data; + context.commit('setViewRange', viewRange); + // call another action: + context.dispatch('setDatesFromViewRange'); + } + ).catch(error => { + // console.log(error); + context.commit('setViewRange', '1M'); + // call another action: + context.dispatch('setDatesFromViewRange'); + }); + } + }, + setDatesFromViewRange(context) { + // console.log('Must set dates from viewRange "' + context.state.viewRange + '"'); + // check local storage first? + if (localStorage.viewRangeStart) { + // console.log('view range start set from local storage.'); + context.commit('setStart', new Date(localStorage.viewRangeStart)); + } + if (localStorage.viewRangeEnd) { + // console.log('view range end set from local storage.'); + context.commit('setEnd', new Date(localStorage.viewRangeEnd)); + } + // also set default: + if (localStorage.viewRangeDefaultStart) { + // console.log('view range default start set from local storage.'); + // console.log(localStorage.viewRangeDefaultStart); + context.commit('setDefaultStart', new Date(localStorage.viewRangeDefaultStart)); + } + if (localStorage.viewRangeDefaultEnd) { + // console.log('view range default end set from local storage.'); + // console.log(localStorage.viewRangeDefaultEnd); + context.commit('setDefaultEnd', new Date(localStorage.viewRangeDefaultEnd)); + } + + if (null !== context.getters.end && null !== context.getters.start) { + return; + } + let start; + let end; + let viewRange = context.getters.viewRange; + // console.log('Will recreate view range on ' + viewRange); + switch (viewRange) { + case '1D': + // one day: + start = new Date; + end = new Date(start.getTime()); + start.setHours(0, 0, 0, 0); + end.setHours(23, 59, 59, 999); + break; + case '1W': + // this week: + start = new Date; + end = new Date(start.getTime()); + // start of week + let diff = start.getDate() - start.getDay() + (start.getDay() === 0 ? -6 : 1); + start.setDate(diff); + start.setHours(0, 0, 0, 0); + + // end of week + let lastday = end.getDate() - (end.getDay() - 1) + 6; + end.setDate(lastday); + end.setHours(23, 59, 59, 999); + break; + case '1M': + // this month: + start = new Date; + start = new Date(start.getFullYear(), start.getMonth(), 1); + start.setHours(0, 0, 0, 0); + end = new Date(start.getFullYear(), start.getMonth() + 1, 0); + end.setHours(23, 59, 59, 999); + break; + case '3M': + // this quarter + start = new Date; + end = new Date; + let quarter = Math.floor((start.getMonth() + 3) / 3) - 1; + // start and end months? I'm sure this could be better: + let startMonths = [0, 3, 6, 9]; + let endMonths = [2, 5, 8, 11]; + // set start to the correct month, day one: + start = new Date(start.getFullYear(), startMonths[quarter], 1); + start.setHours(0, 0, 0, 0); + + // set end to the correct month, day one + end = new Date(end.getFullYear(), endMonths[quarter], 1); + // then to the last day of the month: + end = new Date(end.getFullYear(), end.getMonth() + 1, 0); + end.setHours(23, 59, 59, 999); + break; + case '6M': + // this half-year + start = new Date; + end = new Date; + let half = start.getMonth() <= 5 ? 0 : 1; + + let startHalf = [0, 6]; + let endHalf = [5, 11]; + // set start to the correct month, day one: + start = new Date(start.getFullYear(), startHalf[half], 1); + start.setHours(0, 0, 0, 0); + + // set end to the correct month, day one + end = new Date(end.getFullYear(), endHalf[half], 1); + // then to the last day of the month: + end = new Date(end.getFullYear(), end.getMonth() + 1, 0); + end.setHours(23, 59, 59, 999); + break; + case '1Y': + // this year + start = new Date; + end = new Date; + start = new Date(start.getFullYear(), 0, 1); + + end = new Date(end.getFullYear(), 11, 31); + start.setHours(0, 0, 0, 0); + end.setHours(23, 59, 59, 999); + break; + } + // console.log('Range is ' + viewRange); + // console.log('Start is ' + start); + // console.log('End is ' + end); + context.commit('setStart', start); + context.commit('setEnd', end); + context.commit('setDefaultStart', start); + context.commit('setDefaultEnd', end); + } +} + +// mutations +const mutations = { + setStart(state, value) { + state.start = value; + window.localStorage.setItem('viewRangeStart', value); + }, + setEnd(state, value) { + state.end = value; + window.localStorage.setItem('viewRangeEnd', value); + }, + setDefaultStart(state, value) { + state.defaultStart = value; + window.localStorage.setItem('viewRangeDefaultStart', value); + }, + setDefaultEnd(state, value) { + state.defaultEnd = value; + window.localStorage.setItem('viewRangeDefaultEnd', value); + }, + setViewRange(state, range) { + state.viewRange = range; + } +} + +export default { + namespaced: true, + state, + getters, + actions, + mutations +} diff --git a/frontend/src/components/store/modules/transactions/create.js b/frontend/src/components/store/modules/transactions/create.js index 1d7d9c55ff..4d691b044b 100644 --- a/frontend/src/components/store/modules/transactions/create.js +++ b/frontend/src/components/store/modules/transactions/create.js @@ -20,15 +20,13 @@ const lodashClonedeep = require('lodash.clonedeep'); +import {getDefaultTransaction, getDefaultErrors} from '../../../shared/transactions'; + // initial state const state = () => ({ transactionType: 'any', - date: new Date, + groupTitle: '', transactions: [], - allowedOpposingTypes: {}, - accountToTransaction: {}, - sourceAllowedTypes: ['Asset account', 'Loan', 'Debt', 'Mortgage', 'Revenue account'], - destinationAllowedTypes: ['Asset account', 'Loan', 'Debt', 'Mortgage', 'Expense account'], customDateFields: { interest_date: false, book_date: false, @@ -37,60 +35,8 @@ const state = () => ({ payment_date: false, invoice_date: false, }, - defaultTransaction: { - // basic - description: '', - - // accounts: - source_account: { - id: 0, - name: "", - name_with_balance: "", - type: "", - currency_id: 0, - currency_name: '', - currency_code: '', - currency_decimal_places: 2 - }, - destination_account: { - id: 0, - name: "", - type: "", - currency_id: 0, - currency_name: '', - currency_code: '', - currency_decimal_places: 2 - }, - - // amount: - amount: '', - currency_id: 0, - foreign_amount: '', - foreign_currency_id: 0, - - // meta data - budget_id: 0, - bill_id: 0, - piggy_bank_id: 0, - tags: [], - - // optional date fields (6x): - interest_date: null, - book_date: null, - process_date: null, - due_date: null, - payment_date: null, - invoice_date: null, - - // optional other fields: - internal_reference: null, - external_url: null, - notes: null, - - // transaction links: - links: [], - attachments: [] - }, + defaultTransaction: getDefaultTransaction(), + defaultErrors: getDefaultErrors() } ) @@ -100,12 +46,17 @@ const getters = { transactions: state => { return state.transactions; }, - date: state => { - return state.date; + groupTitle: state => { + return state.groupTitle; }, transactionType: state => { return state.transactionType; }, + accountToTransaction: state => { + // TODO better architecture here, does not need the store. + // possible API point!! + return state.accountToTransaction; + }, defaultTransaction: state => { return state.defaultTransaction; }, @@ -132,62 +83,35 @@ const getters = { } // actions -const actions = { - calcTransactionType(context) { - let source = context.state.transactions[0].source_account; - let dest = context.state.transactions[0].destination_account; - if (null === source || null === dest) { - // console.log('transactionType any'); - context.commit('setTransactionType', 'any'); - return; - } - if ('' === source.type || '' === dest.type) { - // console.log('transactionType any'); - context.commit('setTransactionType', 'any'); - return; - } - - // ok so type is set on both: - let expectedDestinationTypes = context.state.accountToTransaction[source.type]; - if ('undefined' !== typeof expectedDestinationTypes) { - let transactionType = expectedDestinationTypes[dest.type]; - if ('undefined' !== typeof expectedDestinationTypes[dest.type]) { - // console.log('Found a type: ' + transactionType); - context.commit('setTransactionType', transactionType); - return; - } - } - // console.log('Found no type for ' + source.type + ' --> ' + dest.type); - if ('Asset account' !== source.type) { - console.log('Drop ID from source. TODO'); - - // source.id =null - // context.commit('updateField', {field: 'source_account',index: }) - // context.state.transactions[0].source_account.id = null; - } - if ('Asset account' !== dest.type) { - console.log('Drop ID from destination. TODO'); - //context.state.transactions[0].destination_account.id = null; - } - - context.commit('setTransactionType', 'any'); - } -} +const actions = {} // mutations const mutations = { addTransaction(state) { let newTransaction = lodashClonedeep(state.defaultTransaction); + newTransaction.errors = lodashClonedeep(state.defaultErrors); state.transactions.push(newTransaction); }, - setDate(state, payload) { - state.date = payload.date; + resetErrors(state, payload) { + //console.log('resetErrors for index ' + payload.index); + state.transactions[payload.index].errors = lodashClonedeep(state.defaultErrors); + }, + resetTransactions(state) { + state.transactions = []; + }, + setGroupTitle(state, payload) { + state.groupTitle = payload.groupTitle; }, setCustomDateFields(state, payload) { state.customDateFields = payload; }, deleteTransaction(state, payload) { state.transactions.splice(payload.index, 1); + // console.log('Deleted transaction ' + payload.index); + // console.log(state.transactions); + if (0 === state.transactions.length) { + // console.log('array is empty!'); + } }, setTransactionType(state, transactionType) { state.transactionType = transactionType; @@ -201,6 +125,11 @@ const mutations = { updateField(state, payload) { state.transactions[payload.index][payload.field] = payload.value; }, + setTransactionError(state, payload) { + //console.log('Will set transactions[' + payload.index + '][errors][' + payload.field + '] to '); + //console.log(payload.errors); + state.transactions[payload.index].errors[payload.field] = payload.errors; + }, setDestinationAllowedTypes(state, payload) { // console.log('Destination allowed types was changed!'); state.destinationAllowedTypes = payload; diff --git a/frontend/src/components/store/modules/transactions/edit.js b/frontend/src/components/store/modules/transactions/edit.js new file mode 100644 index 0000000000..c813b5c26d --- /dev/null +++ b/frontend/src/components/store/modules/transactions/edit.js @@ -0,0 +1,40 @@ +/* + * edit.js + * Copyright (c) 2021 james@firefly-iii.org + * + * This file is part of Firefly III (https://github.com/firefly-iii). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +// initial state +const state = () => ({}); + + +// getters +const getters = {}; + +// actions +const actions = {}; + +// mutations +const mutations = {}; + +export default { + namespaced: true, + state, + getters, + actions, + mutations +} diff --git a/frontend/src/components/transactions/Create.vue b/frontend/src/components/transactions/Create.vue index edd3f0faab..061fe2920b 100644 --- a/frontend/src/components/transactions/Create.vue +++ b/frontend/src/components/transactions/Create.vue @@ -20,354 +20,717 @@ diff --git a/frontend/src/components/transactions/Edit.vue b/frontend/src/components/transactions/Edit.vue new file mode 100644 index 0000000000..2c525bf1a3 --- /dev/null +++ b/frontend/src/components/transactions/Edit.vue @@ -0,0 +1,687 @@ + + + + + + + \ No newline at end of file diff --git a/frontend/src/components/transactions/SplitForm.vue b/frontend/src/components/transactions/SplitForm.vue new file mode 100644 index 0000000000..cd988b64ad --- /dev/null +++ b/frontend/src/components/transactions/SplitForm.vue @@ -0,0 +1,459 @@ + + + + + diff --git a/frontend/src/components/transactions/SplitPills.vue b/frontend/src/components/transactions/SplitPills.vue new file mode 100644 index 0000000000..793d759934 --- /dev/null +++ b/frontend/src/components/transactions/SplitPills.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/frontend/src/components/transactions/SwitchAccount.vue b/frontend/src/components/transactions/SwitchAccount.vue index 3018d12f24..f90a4253fb 100644 --- a/frontend/src/components/transactions/SwitchAccount.vue +++ b/frontend/src/components/transactions/SwitchAccount.vue @@ -21,10 +21,10 @@ diff --git a/frontend/src/components/transactions/TransactionAccount.vue b/frontend/src/components/transactions/TransactionAccount.vue index 53222c2cf2..a1396396d4 100644 --- a/frontend/src/components/transactions/TransactionAccount.vue +++ b/frontend/src/components/transactions/TransactionAccount.vue @@ -20,28 +20,44 @@ diff --git a/frontend/src/components/transactions/TransactionAttachments.vue b/frontend/src/components/transactions/TransactionAttachments.vue index 3638714b64..566242ce43 100644 --- a/frontend/src/components/transactions/TransactionAttachments.vue +++ b/frontend/src/components/transactions/TransactionAttachments.vue @@ -19,24 +19,102 @@ --> diff --git a/frontend/src/components/transactions/TransactionBill.vue b/frontend/src/components/transactions/TransactionBill.vue index 970f37bea2..01553ed689 100644 --- a/frontend/src/components/transactions/TransactionBill.vue +++ b/frontend/src/components/transactions/TransactionBill.vue @@ -26,43 +26,38 @@
+ + {{ error }}
+
diff --git a/frontend/src/components/transactions/TransactionBudget.vue b/frontend/src/components/transactions/TransactionBudget.vue index 495b655c53..c3d7c8c827 100644 --- a/frontend/src/components/transactions/TransactionBudget.vue +++ b/frontend/src/components/transactions/TransactionBudget.vue @@ -26,42 +26,37 @@
+ + {{ error }}
+
diff --git a/frontend/src/components/transactions/TransactionCategory.vue b/frontend/src/components/transactions/TransactionCategory.vue index 22d636c887..165b85bc55 100644 --- a/frontend/src/components/transactions/TransactionCategory.vue +++ b/frontend/src/components/transactions/TransactionCategory.vue @@ -25,42 +25,44 @@
- + + {{ error }}
+
- - \ No newline at end of file diff --git a/frontend/src/components/transactions/TransactionDate.vue b/frontend/src/components/transactions/TransactionDate.vue index c77c52eea8..fbcb2f93af 100644 --- a/frontend/src/components/transactions/TransactionDate.vue +++ b/frontend/src/components/transactions/TransactionDate.vue @@ -25,87 +25,96 @@
+ + {{ error }}
+
+ {{ timeZone }}
- - \ No newline at end of file diff --git a/frontend/src/components/transactions/TransactionDescription.vue b/frontend/src/components/transactions/TransactionDescription.vue index caf1d5b200..6143f1279f 100644 --- a/frontend/src/components/transactions/TransactionDescription.vue +++ b/frontend/src/components/transactions/TransactionDescription.vue @@ -20,50 +20,46 @@ diff --git a/frontend/src/components/transactions/TransactionExternalUrl.vue b/frontend/src/components/transactions/TransactionExternalUrl.vue index 79e13f624e..df722e3fc7 100644 --- a/frontend/src/components/transactions/TransactionExternalUrl.vue +++ b/frontend/src/components/transactions/TransactionExternalUrl.vue @@ -19,42 +19,53 @@ --> diff --git a/frontend/src/components/transactions/TransactionForeignCurrency.vue b/frontend/src/components/transactions/TransactionForeignCurrency.vue index 6845d38023..5f68b02bef 100644 --- a/frontend/src/components/transactions/TransactionForeignCurrency.vue +++ b/frontend/src/components/transactions/TransactionForeignCurrency.vue @@ -20,10 +20,10 @@ diff --git a/frontend/src/components/transactions/TransactionGroupTitle.vue b/frontend/src/components/transactions/TransactionGroupTitle.vue new file mode 100644 index 0000000000..9bffdbad48 --- /dev/null +++ b/frontend/src/components/transactions/TransactionGroupTitle.vue @@ -0,0 +1,104 @@ + + + + + + + \ No newline at end of file diff --git a/frontend/src/components/transactions/TransactionInternalReference.vue b/frontend/src/components/transactions/TransactionInternalReference.vue index f99a902540..a06a28ffa1 100644 --- a/frontend/src/components/transactions/TransactionInternalReference.vue +++ b/frontend/src/components/transactions/TransactionInternalReference.vue @@ -19,47 +19,56 @@ --> - - \ No newline at end of file diff --git a/frontend/src/components/transactions/TransactionLinks.vue b/frontend/src/components/transactions/TransactionLinks.vue index 12d870c445..a6b8faae07 100644 --- a/frontend/src/components/transactions/TransactionLinks.vue +++ b/frontend/src/components/transactions/TransactionLinks.vue @@ -19,18 +19,18 @@ --> \ No newline at end of file diff --git a/frontend/src/components/transactions/TransactionNotes.vue b/frontend/src/components/transactions/TransactionNotes.vue index 2786e7a8ae..d934115fc9 100644 --- a/frontend/src/components/transactions/TransactionNotes.vue +++ b/frontend/src/components/transactions/TransactionNotes.vue @@ -20,35 +20,53 @@ diff --git a/frontend/src/components/transactions/TransactionTags.vue b/frontend/src/components/transactions/TransactionTags.vue index 9d5e0cce9e..a11500c58f 100644 --- a/frontend/src/components/transactions/TransactionTags.vue +++ b/frontend/src/components/transactions/TransactionTags.vue @@ -34,61 +34,72 @@ @tags-changed="newTags => this.tags = newTags" />
+ + {{ error }}
+
\n\n\n","import { render, staticRenderFns } from \"./Index.vue?vue&type=template&id=d668ce46&scoped=true&\"\nimport script from \"./Index.vue?vue&type=script&lang=js&\"\nexport * from \"./Index.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"d668ce46\",\n null\n \n)\n\nexport default component.exports","/*\n * index.js\n * Copyright (c) 2020 james@firefly-iii.org\n *\n * This file is part of Firefly III (https://github.com/firefly-iii).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see .\n */\n\nrequire('../../bootstrap');\n\nimport Index from \"../../components/accounts/Index\";\n\n// i18n\nlet i18n = require('../../i18n');\n\nlet props = {};\nnew Vue({\n i18n,\n render(createElement) {\n return createElement(Index, {props: props});\n }\n }).$mount('#accounts');\n"],"sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack:///./src/components/accounts/Index.vue?0fa6","webpack:///./src/components/accounts/Index.vue?6395","webpack:///src/components/accounts/Index.vue","webpack:///./src/components/accounts/Index.vue","webpack:///./src/pages/accounts/index.js"],"names":["name","props","accountTypes","String","data","accounts","created","axios","get","this","$props","methods","loadAccounts","hasOwnProperty","key","test","acct","attributes","type","account_role","$t","iban","account_number","push","_vm","_h","$createElement","_c","_self","staticClass","_m","_v","staticStyle","_s","attrs","_e","_l","account","id","Intl","NumberFormat","style","currency","currency_code","format","current_balance","require","i18n","Vue","render","createElement","Index","$mount"],"mappings":"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","file":"/public/js/accounts/index.js","sourcesContent":["var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col-lg-12 col-md-12 col-sm-12 col-xs-12\"},[_c('div',{staticClass:\"card\"},[_vm._m(0),_vm._v(\" \"),_c('div',{staticClass:\"card-body p-0\"},[_c('table',{staticClass:\"table table-sm table-striped\"},[_c('caption',{staticStyle:{\"display\":\"none\"}},[_vm._v(_vm._s(_vm.$t('list.name')))]),_vm._v(\" \"),_c('thead',[_c('tr',[_c('th',{attrs:{\"scope\":\"col\"}},[_vm._v(\" \")]),_vm._v(\" \"),_c('th',{attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('list.name')))]),_vm._v(\" \"),('asset' === _vm.$props.accountTypes)?_c('th',{attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('list.role')))]):_vm._e(),_vm._v(\" \"),_c('th',{attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('list.iban')))]),_vm._v(\" \"),_c('th',{staticStyle:{\"text-align\":\"right\"},attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('list.currentBalance')))]),_vm._v(\" \"),_c('th',{attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('list.balanceDiff')))])])]),_vm._v(\" \"),_c('tbody',_vm._l((_vm.accounts),function(account){return _c('tr',[_c('td',[_c('div',{staticClass:\"btn-group btn-group-xs\"},[_c('a',{staticClass:\"btn btn-xs btn-default\",attrs:{\"href\":'./accounts/edit/' + account.id}},[_c('i',{staticClass:\"fa fas fa-pencil-alt\"})]),_vm._v(\" \"),_c('a',{staticClass:\"btn btn-xs btn-danger\",attrs:{\"href\":'./accounts/delete/' + account.id}},[_c('i',{staticClass:\"fa far fa-trash\"})])])]),_vm._v(\" \"),_c('td',[_vm._v(_vm._s(account.attributes.name)+\"\\n \")]),_vm._v(\" \"),('asset' === _vm.$props.accountTypes)?_c('td',[_vm._v(\"\\n \"+_vm._s(account.attributes.account_role)+\"\\n \")]):_vm._e(),_vm._v(\" \"),_c('td',[_vm._v(\"\\n \"+_vm._s(account.attributes.iban)+\"\\n \")]),_vm._v(\" \"),_c('td',{staticStyle:{\"text-align\":\"right\"}},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat('en-US', {\n style: 'currency', currency:\n account.attributes.currency_code\n }).format(account.attributes.current_balance))+\"\\n \")]),_vm._v(\" \"),_c('td',[_vm._v(\"diff\")])])}),0)])]),_vm._v(\" \"),_c('div',{staticClass:\"card-footer\"},[_vm._v(\"\\n Footer stuff.\\n \")])])])])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"card-header\"},[_c('h3',{staticClass:\"card-title\"},[_vm._v(\"Title thing\")]),_vm._v(\" \"),_c('div',{staticClass:\"card-tools\"},[_c('div',{staticClass:\"input-group input-group-sm\",staticStyle:{\"width\":\"150px\"}},[_c('input',{staticClass:\"form-control float-right\",attrs:{\"name\":\"table_search\",\"placeholder\":\"Search\",\"type\":\"text\"}}),_vm._v(\" \"),_c('div',{staticClass:\"input-group-append\"},[_c('button',{staticClass:\"btn btn-default\",attrs:{\"type\":\"submit\"}},[_c('i',{staticClass:\"fas fa-search\"})])])])])])}]\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Index.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Index.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n\n","import { render, staticRenderFns } from \"./Index.vue?vue&type=template&id=3bbb2516&scoped=true&\"\nimport script from \"./Index.vue?vue&type=script&lang=js&\"\nexport * from \"./Index.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"3bbb2516\",\n null\n \n)\n\nexport default component.exports","/*\n * index.js\n * Copyright (c) 2020 james@firefly-iii.org\n *\n * This file is part of Firefly III (https://github.com/firefly-iii).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see .\n */\n\nrequire('../../bootstrap');\n\nimport Index from \"../../components/accounts/Index\";\n\n// i18n\nlet i18n = require('../../i18n');\n\nlet props = {};\nnew Vue({\n i18n,\n render(createElement) {\n return createElement(Index, {props: props});\n }\n }).$mount('#accounts');\n"],"sourceRoot":""} \ No newline at end of file diff --git a/public/v2/js/accounts/show.js b/public/v2/js/accounts/show.js index 2e96c29be3..0992059ca0 100755 --- a/public/v2/js/accounts/show.js +++ b/public/v2/js/accounts/show.js @@ -1,2 +1,2 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[3],{293:function(n,e,t){n.exports=t(411)},411:function(n,e,t){"use strict";t.r(e);var o={name:"Show"},r=t(1),s=Object(r.a)(o,(function(){var n=this.$createElement;return(this._self._c||n)("div",[this._v("\n I am a show\n")])}),[],!1,null,"dcd61a50",null).exports;t(15);var c=t(19),u={};new Vue({i18n:c,render:function(n){return n(s,{props:u})}}).$mount("#accounts_show")}},[[293,0,1]]]); +(window.webpackJsonp=window.webpackJsonp||[]).push([[3],{303:function(n,e,t){n.exports=t(429)},429:function(n,e,t){"use strict";t.r(e);var o={name:"Show"},r=t(1),s=Object(r.a)(o,(function(){var n=this.$createElement;return(this._self._c||n)("div",[this._v("\n I am a show\n")])}),[],!1,null,"321a1058",null).exports;t(16);var u=t(19),a={};new Vue({i18n:u,render:function(n){return n(s,{props:a})}}).$mount("#accounts_show")}},[[303,0,1]]]); //# sourceMappingURL=show.js.map \ No newline at end of file diff --git a/public/v2/js/accounts/show.js.map b/public/v2/js/accounts/show.js.map index 9bb2133a98..9ad80a1576 100755 --- a/public/v2/js/accounts/show.js.map +++ b/public/v2/js/accounts/show.js.map @@ -1 +1 @@ -{"version":3,"sources":["webpack:///./src/components/accounts/Show.vue?b4b4","webpack:///./src/components/accounts/Show.vue?4c02","webpack:///src/components/accounts/Show.vue","webpack:///./src/components/accounts/Show.vue","webpack:///./src/pages/accounts/show.js"],"names":["_h","this","$createElement","_self","_c","_v","require","i18n","props","Vue","render","createElement","Show","$mount"],"mappings":"uIAAA,ICAoM,EC2BpM,CACE,KAAF,Q,OCVe,EAXC,YACd,GHRW,WAAa,IAAiBA,EAATC,KAAgBC,eAAuC,OAAvDD,KAA0CE,MAAMC,IAAIJ,GAAa,MAAM,CAAvEC,KAA4EI,GAAG,2BAC3F,IGUpB,EACA,KACA,WACA,M,QCOFC,EAAQ,IAKR,IAAIC,EAAOD,EAAQ,IAKfE,EAAQ,GAGZ,IAAIC,IAAI,CACIF,OACAG,OAFJ,SAEWC,GACH,OAAOA,EAAcC,EAAM,CAACJ,MAAOA,OAExCK,OAAO,oB","file":"/public/js/accounts/show.js","sourcesContent":["var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_vm._v(\"\\n I am a show\\n\")])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Show.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Show.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n\n","import { render, staticRenderFns } from \"./Show.vue?vue&type=template&id=dcd61a50&scoped=true&\"\nimport script from \"./Show.vue?vue&type=script&lang=js&\"\nexport * from \"./Show.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"dcd61a50\",\n null\n \n)\n\nexport default component.exports","/*\n * index.js\n * Copyright (c) 2020 james@firefly-iii.org\n *\n * This file is part of Firefly III (https://github.com/firefly-iii).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see .\n */\n\n\nrequire('../../bootstrap');\n\nimport Show from \"../../components/accounts/Show\";\n\n// i18n\nlet i18n = require('../../i18n');\n\n// get page name?\n\n\nlet props = {\n\n};\nnew Vue({\n i18n,\n render(createElement) {\n return createElement(Show, {props: props});\n }\n }).$mount('#accounts_show');\n"],"sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack:///./src/components/accounts/Show.vue?4a89","webpack:///./src/components/accounts/Show.vue?4c02","webpack:///src/components/accounts/Show.vue","webpack:///./src/components/accounts/Show.vue","webpack:///./src/pages/accounts/show.js"],"names":["name","_h","this","$createElement","_self","_c","_v","require","i18n","props","Vue","render","createElement","Show","$mount"],"mappings":"uIAAA,ICAoM,EC2BpM,CACEA,KAAM,Q,OCVO,EAXC,YACd,GHRW,WAAa,IAAiBC,EAATC,KAAgBC,eAAuC,OAAvDD,KAA0CE,MAAMC,IAAIJ,GAAa,MAAM,CAAvEC,KAA4EI,GAAG,yBAC3F,IGUpB,EACA,KACA,WACA,M,QCOFC,EAAQ,IAKR,IAAIC,EAAOD,EAAQ,IAKfE,EAAQ,GAGZ,IAAIC,IAAI,CACIF,OACAG,OAFJ,SAEWC,GACH,OAAOA,EAAcC,EAAM,CAACJ,MAAOA,OAExCK,OAAO,oB","file":"/public/js/accounts/show.js","sourcesContent":["var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_vm._v(\"\\n I am a show\\n\")])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Show.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Show.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n\n","import { render, staticRenderFns } from \"./Show.vue?vue&type=template&id=321a1058&scoped=true&\"\nimport script from \"./Show.vue?vue&type=script&lang=js&\"\nexport * from \"./Show.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"321a1058\",\n null\n \n)\n\nexport default component.exports","/*\n * index.js\n * Copyright (c) 2020 james@firefly-iii.org\n *\n * This file is part of Firefly III (https://github.com/firefly-iii).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see .\n */\n\n\nrequire('../../bootstrap');\n\nimport Show from \"../../components/accounts/Show\";\n\n// i18n\nlet i18n = require('../../i18n');\n\n// get page name?\n\n\nlet props = {\n\n};\nnew Vue({\n i18n,\n render(createElement) {\n return createElement(Show, {props: props});\n }\n }).$mount('#accounts_show');\n"],"sourceRoot":""} \ No newline at end of file diff --git a/public/v2/js/dashboard.js b/public/v2/js/dashboard.js index d856383ab3..003910f07e 100755 --- a/public/v2/js/dashboard.js +++ b/public/v2/js/dashboard.js @@ -1,2 +1,2 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[4],{235:function(t,e,s){s(408),t.exports=s(412)},238:function(t,e,s){"use strict";s(52)},239:function(t,e,s){(t.exports=s(24)(!1)).push([t.i,".dropdown-item[data-v-03e62f58],.dropdown-item[data-v-03e62f58]:hover{color:#212529}",""])},267:function(t,e,s){var a={"./af":61,"./af.js":61,"./ar":62,"./ar-dz":63,"./ar-dz.js":63,"./ar-kw":64,"./ar-kw.js":64,"./ar-ly":65,"./ar-ly.js":65,"./ar-ma":66,"./ar-ma.js":66,"./ar-sa":67,"./ar-sa.js":67,"./ar-tn":68,"./ar-tn.js":68,"./ar.js":62,"./az":69,"./az.js":69,"./be":70,"./be.js":70,"./bg":71,"./bg.js":71,"./bm":72,"./bm.js":72,"./bn":73,"./bn-bd":74,"./bn-bd.js":74,"./bn.js":73,"./bo":75,"./bo.js":75,"./br":76,"./br.js":76,"./bs":77,"./bs.js":77,"./ca":78,"./ca.js":78,"./cs":79,"./cs.js":79,"./cv":80,"./cv.js":80,"./cy":81,"./cy.js":81,"./da":82,"./da.js":82,"./de":83,"./de-at":84,"./de-at.js":84,"./de-ch":85,"./de-ch.js":85,"./de.js":83,"./dv":86,"./dv.js":86,"./el":87,"./el.js":87,"./en-au":88,"./en-au.js":88,"./en-ca":89,"./en-ca.js":89,"./en-gb":90,"./en-gb.js":90,"./en-ie":91,"./en-ie.js":91,"./en-il":92,"./en-il.js":92,"./en-in":93,"./en-in.js":93,"./en-nz":94,"./en-nz.js":94,"./en-sg":95,"./en-sg.js":95,"./eo":96,"./eo.js":96,"./es":97,"./es-do":98,"./es-do.js":98,"./es-mx":99,"./es-mx.js":99,"./es-us":100,"./es-us.js":100,"./es.js":97,"./et":101,"./et.js":101,"./eu":102,"./eu.js":102,"./fa":103,"./fa.js":103,"./fi":104,"./fi.js":104,"./fil":105,"./fil.js":105,"./fo":106,"./fo.js":106,"./fr":107,"./fr-ca":108,"./fr-ca.js":108,"./fr-ch":109,"./fr-ch.js":109,"./fr.js":107,"./fy":110,"./fy.js":110,"./ga":111,"./ga.js":111,"./gd":112,"./gd.js":112,"./gl":113,"./gl.js":113,"./gom-deva":114,"./gom-deva.js":114,"./gom-latn":115,"./gom-latn.js":115,"./gu":116,"./gu.js":116,"./he":117,"./he.js":117,"./hi":118,"./hi.js":118,"./hr":119,"./hr.js":119,"./hu":120,"./hu.js":120,"./hy-am":121,"./hy-am.js":121,"./id":122,"./id.js":122,"./is":123,"./is.js":123,"./it":124,"./it-ch":125,"./it-ch.js":125,"./it.js":124,"./ja":126,"./ja.js":126,"./jv":127,"./jv.js":127,"./ka":128,"./ka.js":128,"./kk":129,"./kk.js":129,"./km":130,"./km.js":130,"./kn":131,"./kn.js":131,"./ko":132,"./ko.js":132,"./ku":133,"./ku.js":133,"./ky":134,"./ky.js":134,"./lb":135,"./lb.js":135,"./lo":136,"./lo.js":136,"./lt":137,"./lt.js":137,"./lv":138,"./lv.js":138,"./me":139,"./me.js":139,"./mi":140,"./mi.js":140,"./mk":141,"./mk.js":141,"./ml":142,"./ml.js":142,"./mn":143,"./mn.js":143,"./mr":144,"./mr.js":144,"./ms":145,"./ms-my":146,"./ms-my.js":146,"./ms.js":145,"./mt":147,"./mt.js":147,"./my":148,"./my.js":148,"./nb":149,"./nb.js":149,"./ne":150,"./ne.js":150,"./nl":151,"./nl-be":152,"./nl-be.js":152,"./nl.js":151,"./nn":153,"./nn.js":153,"./oc-lnc":154,"./oc-lnc.js":154,"./pa-in":155,"./pa-in.js":155,"./pl":156,"./pl.js":156,"./pt":157,"./pt-br":158,"./pt-br.js":158,"./pt.js":157,"./ro":159,"./ro.js":159,"./ru":160,"./ru.js":160,"./sd":161,"./sd.js":161,"./se":162,"./se.js":162,"./si":163,"./si.js":163,"./sk":164,"./sk.js":164,"./sl":165,"./sl.js":165,"./sq":166,"./sq.js":166,"./sr":167,"./sr-cyrl":168,"./sr-cyrl.js":168,"./sr.js":167,"./ss":169,"./ss.js":169,"./sv":170,"./sv.js":170,"./sw":171,"./sw.js":171,"./ta":172,"./ta.js":172,"./te":173,"./te.js":173,"./tet":174,"./tet.js":174,"./tg":175,"./tg.js":175,"./th":176,"./th.js":176,"./tk":177,"./tk.js":177,"./tl-ph":178,"./tl-ph.js":178,"./tlh":179,"./tlh.js":179,"./tr":180,"./tr.js":180,"./tzl":181,"./tzl.js":181,"./tzm":182,"./tzm-latn":183,"./tzm-latn.js":183,"./tzm.js":182,"./ug-cn":184,"./ug-cn.js":184,"./uk":185,"./uk.js":185,"./ur":186,"./ur.js":186,"./uz":187,"./uz-latn":188,"./uz-latn.js":188,"./uz.js":187,"./vi":189,"./vi.js":189,"./x-pseudo":190,"./x-pseudo.js":190,"./yo":191,"./yo.js":191,"./zh-cn":192,"./zh-cn.js":192,"./zh-hk":193,"./zh-hk.js":193,"./zh-mo":194,"./zh-mo.js":194,"./zh-tw":195,"./zh-tw.js":195};function r(t){var e=n(t);return s(e)}function n(t){if(!s.o(a,t)){var e=new Error("Cannot find module '"+t+"'");throw e.code="MODULE_NOT_FOUND",e}return a[t]}r.keys=function(){return Object.keys(a)},r.resolve=n,t.exports=r,r.id=267},408:function(t,e,s){"use strict";s.r(e);var a={name:"Dashboard",created:function(){},computed:{},methods:{}},r=s(1),n=Object(r.a)(a,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",[s("top-boxes"),t._v(" "),s("div",{staticClass:"row"},[s("div",{staticClass:"col"},[s("main-account")],1)]),t._v(" "),s("main-account-list"),t._v(" "),s("div",{staticClass:"row"},[s("div",{staticClass:"col"},[s("main-budget-list")],1)]),t._v(" "),s("div",{staticClass:"row"},[s("div",{staticClass:"col"},[s("main-category-list")],1)]),t._v(" "),s("div",{staticClass:"row"},[s("div",{staticClass:"col-lg-6 col-md-12 col-sm-12 col-xs-12"},[s("main-debit-list")],1),t._v(" "),s("div",{staticClass:"col-lg-6 col-md-12 col-sm-12 col-xs-12"},[s("main-credit-list")],1)]),t._v(" "),s("div",{staticClass:"row"},[s("div",{staticClass:"col-lg-6 col-md-12 col-sm-12 col-xs-12"},[s("main-piggy-list")],1),t._v(" "),s("div",{staticClass:"col-lg-6 col-md-12 col-sm-12 col-xs-12"},[s("main-bills-list")],1)])],1)}),[],!1,null,null,null).exports,i={name:"TopBoxes",props:{},data:function(){return{summary:[],balances:[],billsPaid:[],billsUnpaid:[],leftToSpend:[],netWorth:[]}},computed:{prefCurrencyBalances:function(){return this.filterOnCurrency(this.balances)},notPrefCurrencyBalances:function(){return this.filterOnNotCurrency(this.balances)},prefBillsUnpaid:function(){return this.filterOnCurrency(this.billsUnpaid)},notPrefBillsUnpaid:function(){return this.filterOnNotCurrency(this.billsUnpaid)},prefLeftToSpend:function(){return this.filterOnCurrency(this.leftToSpend)},notPrefLeftToSpend:function(){return this.filterOnNotCurrency(this.leftToSpend)},prefNetWorth:function(){return this.filterOnCurrency(this.netWorth)},notPrefNetWorth:function(){return this.filterOnNotCurrency(this.netWorth)},currencyCode:function(){return this.$store.getters.currencyCode},currencyId:function(){return this.$store.getters.currencyId}},created:function(){this.prepareComponent()},methods:{filterOnCurrency:function(t){var e=[];for(var s in t)t.hasOwnProperty(s)&&t[s].currency_id===this.currencyId&&e.push(t[s]);return 0===e.length&&t.hasOwnProperty(0)&&e.push(t[0]),e},filterOnNotCurrency:function(t){var e=[];for(var s in t)t.hasOwnProperty(s)&&t[s].currency_id!==this.currencyId&&e.push(t[s]);return e},prepareComponent:function(){var t=this;axios.get("./api/v1/summary/basic?start="+window.sessionStart+"&end="+window.sessionEnd).then((function(e){t.summary=e.data,t.buildComponent()}))},buildComponent:function(){this.getBalanceEntries(),this.getBillsEntries(),this.getLeftToSpend(),this.getNetWorth()},hasCurrency:function(t){for(var e in t)if(t.hasOwnProperty(e)&&t[e].currency_id===this.currencyId)return!0;return!1},getBalanceEntries:function(){this.balances=this.getKeyedEntries("balance-in-")},getNetWorth:function(){this.netWorth=this.getKeyedEntries("net-worth-in-")},getLeftToSpend:function(){this.leftToSpend=this.getKeyedEntries("left-to-spend-in-")},getBillsEntries:function(){this.billsPaid=this.getKeyedEntries("bills-paid-in-"),this.billsUnpaid=this.getKeyedEntries("bills-unpaid-in-")},getKeyedEntries:function(t){var e=[];for(var s in this.summary)this.summary.hasOwnProperty(s)&&t===s.substr(0,t.length)&&e.push(this.summary[s]);return e}}},c=Object(r.a)(i,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"row"},[s("div",{staticClass:"col"},[s("div",{staticClass:"info-box"},[t._m(0),t._v(" "),s("div",{staticClass:"info-box-content"},[s("span",{staticClass:"info-box-text"},[t._v(t._s(t.$t("firefly.balance")))]),t._v(" "),t._l(t.prefCurrencyBalances,(function(e){return s("span",{staticClass:"info-box-number",attrs:{title:e.sub_title}},[t._v(t._s(e.value_parsed))])})),t._v(" "),t._m(1),t._v(" "),s("span",{staticClass:"progress-description"},[t._l(t.notPrefCurrencyBalances,(function(e,a){return s("span",{attrs:{title:e.sub_title}},[t._v("\n "+t._s(e.value_parsed)),a+1!==t.notPrefCurrencyBalances.length?s("span",[t._v(", ")]):t._e()])})),t._v(" "),0===t.notPrefCurrencyBalances.length?s("span",[t._v(" ")]):t._e()],2)],2)])]),t._v(" "),s("div",{staticClass:"col"},[s("div",{staticClass:"info-box"},[t._m(2),t._v(" "),s("div",{staticClass:"info-box-content"},[s("span",{staticClass:"info-box-text"},[t._v(t._s(t.$t("firefly.bills_to_pay")))]),t._v(" "),t._l(t.prefBillsUnpaid,(function(e){return s("span",{staticClass:"info-box-number"},[t._v(t._s(e.value_parsed))])})),t._v(" "),t._m(3),t._v(" "),s("span",{staticClass:"progress-description"},[t._l(t.notPrefBillsUnpaid,(function(e,a){return s("span",[t._v("\n "+t._s(e.value_parsed)),a+1!==t.notPrefBillsUnpaid.length?s("span",[t._v(", ")]):t._e()])})),t._v(" "),0===t.notPrefBillsUnpaid.length?s("span",[t._v(" ")]):t._e()],2)],2)])]),t._v(" "),s("div",{staticClass:"col"},[s("div",{staticClass:"info-box"},[t._m(4),t._v(" "),s("div",{staticClass:"info-box-content"},[s("span",{staticClass:"info-box-text"},[t._v(t._s(t.$t("firefly.left_to_spend")))]),t._v(" "),t._l(t.prefLeftToSpend,(function(e){return s("span",{staticClass:"info-box-number",attrs:{title:e.sub_title}},[t._v(t._s(e.value_parsed))])})),t._v(" "),t._m(5),t._v(" "),s("span",{staticClass:"progress-description"},[t._l(t.notPrefLeftToSpend,(function(e,a){return s("span",[t._v("\n "+t._s(e.value_parsed)),a+1!==t.notPrefLeftToSpend.length?s("span",[t._v(", ")]):t._e()])})),t._v(" "),0===t.notPrefLeftToSpend.length?s("span",[t._v(" ")]):t._e()],2)],2)])]),t._v(" "),s("div",{staticClass:"col"},[s("div",{staticClass:"info-box"},[t._m(6),t._v(" "),s("div",{staticClass:"info-box-content"},[s("span",{staticClass:"info-box-text"},[s("span",[t._v(t._s(t.$t("firefly.net_worth")))])]),t._v(" "),t._l(t.prefNetWorth,(function(e){return s("span",{staticClass:"info-box-number",attrs:{title:e.sub_title}},[t._v(t._s(e.value_parsed))])})),t._v(" "),t._m(7),t._v(" "),s("span",{staticClass:"progress-description"},[t._l(t.notPrefNetWorth,(function(e,a){return s("span",[t._v("\n "+t._s(e.value_parsed)),a+1!==t.notPrefNetWorth.length?s("span",[t._v(", ")]):t._e()])})),t._v(" "),0===t.notPrefNetWorth.length?s("span",[t._v(" ")]):t._e()],2)],2)])])])}),[function(){var t=this.$createElement,e=this._self._c||t;return e("span",{staticClass:"info-box-icon"},[e("i",{staticClass:"far fa-bookmark text-info"})])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"progress bg-info"},[e("div",{staticClass:"progress-bar",staticStyle:{width:"0"}})])},function(){var t=this.$createElement,e=this._self._c||t;return e("span",{staticClass:"info-box-icon"},[e("i",{staticClass:"far fa-calendar-alt text-teal"})])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"progress bg-teal"},[e("div",{staticClass:"progress-bar",staticStyle:{width:"0"}})])},function(){var t=this.$createElement,e=this._self._c||t;return e("span",{staticClass:"info-box-icon"},[e("i",{staticClass:"fas fa-money-bill text-success"})])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"progress bg-success"},[e("div",{staticClass:"progress-bar",staticStyle:{width:"0"}})])},function(){var t=this.$createElement,e=this._self._c||t;return e("span",{staticClass:"info-box-icon"},[e("i",{staticClass:"fas fa-money-bill text-success"})])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"progress bg-success"},[e("div",{staticClass:"progress-bar",staticStyle:{width:"0"}})])}],!1,null,null,null).exports,o={name:"DataConverter",data:function(){return{dataSet:null,newDataSet:null,locale:localStorage.local}},methods:{convertChart:function(t){return this.dataSet=t,this.newDataSet={count:0,labels:[],datasets:[]},this.getLabels(),this.getDataSets(),this.newDataSet.count=this.newDataSet.datasets.length,this.newDataSet},colorizeBarData:function(t){this.dataSet=t,this.newDataSet={count:0,labels:[],datasets:[]};for(var e=[[53,124,165],[0,141,76],[219,139,11],[202,25,90],[85,82,153],[66,133,244],[219,68,55],[244,180,0],[15,157,88],[171,71,188],[0,172,193],[255,112,67],[158,157,36],[92,107,192],[240,98,146],[0,121,107],[194,24,91]],s=[],a=0;a0){var i=r+" "+t;if(!(i.length>e))return n===a.length-1?void s.push(i):void(r=i);s.push(r),r=""}n!==a.length-1&&t.length2}},[s("div",{staticClass:"card"},[s("div",{staticClass:"card-header"},[s("h3",{staticClass:"card-title"},[s("a",{attrs:{href:e.uri}},[t._v(t._s(e.title))])])]),t._v(" "),s("div",{staticClass:"card-body table-responsive p-0"},[1===t.accounts.length?s("transaction-list-large",{attrs:{transactions:e.transactions,account_id:e.id}}):t._e(),t._v(" "),2===t.accounts.length?s("transaction-list-medium",{attrs:{transactions:e.transactions,account_id:e.id}}):t._e(),t._v(" "),t.accounts.length>2?s("transaction-list-small",{attrs:{transactions:e.transactions,account_id:e.id}}):t._e()],1)])])})),0)}),[],!1,null,"2f3a38b8",null).exports,b={name:"MainBillsList",computed:{locale:function(){return this.$store.getters.locale}},created:function(){var t=this;axios.get("./api/v1/bills?start="+window.sessionStart+"&end="+window.sessionEnd).then((function(e){t.loadBills(e.data.data)}))},components:{},methods:{loadBills:function(t){for(var e in t)if(t.hasOwnProperty(e)&&/^0$|^[1-9]\d*$/.test(e)&&e<=4294967294){var s=t[e],a=s.attributes.active;s.attributes.pay_dates.length>0&&a&&this.bills.push(s)}}},data:function(){return{bills:[]}}},v=Object(r.a)(b,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"card"},[s("div",{staticClass:"card-header"},[s("h3",{staticClass:"card-title"},[t._v(t._s(t.$t("firefly.bills")))])]),t._v(" "),s("div",{staticClass:"card-body table-responsive p-0"},[s("table",{staticClass:"table table-striped"},[s("caption",{staticStyle:{display:"none"}},[t._v(t._s(t.$t("firefly.bills")))]),t._v(" "),s("thead",[s("tr",[s("th",{staticStyle:{width:"35%"},attrs:{scope:"col"}},[t._v(t._s(t.$t("list.name")))]),t._v(" "),s("th",{staticStyle:{width:"25%"},attrs:{scope:"col"}},[t._v(t._s(t.$t("list.next_expected_match")))])])]),t._v(" "),s("tbody",t._l(this.bills,(function(e){return s("tr",[s("td",[s("a",{attrs:{href:"./bills/show"+e.id,title:e.attributes.name}},[t._v(t._s(e.attributes.name))]),t._v("\n ~"+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.attributes.currency_code}).format((parseFloat(e.attributes.amount_min)+parseFloat(e.attributes.amount_max))/2))+"\n "),s("br")]),t._v(" "),s("td",t._l(e.attributes.pay_dates,(function(e){return s("span",[t._v("\n "+t._s(new Intl.DateTimeFormat(t.locale,{year:"numeric",month:"long",day:"numeric"}).format(new Date(e)))+"\n "),s("br")])})),0)])})),0)])]),t._v(" "),s("div",{staticClass:"card-footer"},[s("a",{staticClass:"btn btn-default button-sm",attrs:{href:"./bills"}},[s("i",{staticClass:"far fa-money-bill-alt"}),t._v(" "+t._s(t.$t("firefly.go_to_bills")))])])])}),[],!1,null,null,null).exports,h={name:"BudgetLimitRow",mounted:function(){var t;this.locale=null!==(t=localStorage.locale)&&void 0!==t?t:"en-US"},data:function(){return{locale:"en-US"}},props:{budgetLimit:{type:Object,default:function(){return{}}},budget:{type:Object,default:function(){return{}}}}},g=Object(r.a)(h,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("tr",[s("td",{staticStyle:{width:"25%"}},[s("a",{attrs:{href:"./budgets/show/"+t.budgetLimit.budget_id}},[t._v(t._s(t.budgetLimit.budget_name))])]),t._v(" "),s("td",{staticStyle:{"vertical-align":"middle"}},[s("div",{staticClass:"progress progress active"},[s("div",{staticClass:"progress-bar bg-success progress-bar-striped",style:"width: "+t.budgetLimit.pctGreen+"%;",attrs:{role:"progressbar","aria-valuenow":t.budgetLimit.pctGreen,"aria-valuemin":"0","aria-valuemax":"100"}},[t.budgetLimit.pctGreen>35?s("span",[t._v("\n Spent\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budgetLimit.currency_code}).format(t.budgetLimit.spent))+"\n of\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budgetLimit.currency_code}).format(t.budgetLimit.amount))+"\n ")]):t._e()]),t._v(" "),s("div",{staticClass:"progress-bar bg-warning progress-bar-striped",style:"width: "+t.budgetLimit.pctOrange+"%;",attrs:{role:"progressbar","aria-valuenow":t.budgetLimit.pctOrange,"aria-valuemin":"0","aria-valuemax":"100"}},[t.budgetLimit.pctRed<=50&&t.budgetLimit.pctOrange>35?s("span",[t._v("\n Spent\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budgetLimit.currency_code}).format(t.budgetLimit.spent))+"\n of\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budgetLimit.currency_code}).format(t.budgetLimit.amount))+"\n ")]):t._e()]),t._v(" "),s("div",{staticClass:"progress-bar bg-danger progress-bar-striped",style:"width: "+t.budgetLimit.pctRed+"%;",attrs:{role:"progressbar","aria-valuenow":t.budgetLimit.pctRed,"aria-valuemin":"0","aria-valuemax":"100"}},[t.budgetLimit.pctOrange<=50&&t.budgetLimit.pctRed>35?s("span",[t._v("\n Spent\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budgetLimit.currency_code}).format(t.budgetLimit.spent))+"\n of\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budgetLimit.currency_code}).format(t.budgetLimit.amount))+"\n ")]):t._e()])]),t._v(" "),s("small",{staticClass:"d-none d-lg-block"},[t._v("\n "+t._s(new Intl.DateTimeFormat(t.locale,{year:"numeric",month:"long",day:"numeric"}).format(t.budgetLimit.start))+"\n →\n "+t._s(new Intl.DateTimeFormat(t.locale,{year:"numeric",month:"long",day:"numeric"}).format(t.budgetLimit.end))+"\n ")])]),t._v(" "),s("td",{staticClass:"align-middle d-none d-lg-table-cell",staticStyle:{width:"10%"}},[parseFloat(t.budgetLimit.amount)+parseFloat(t.budgetLimit.spent)>0?s("span",{staticClass:"text-success"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budgetLimit.currency_code}).format(parseFloat(t.budgetLimit.amount)+parseFloat(t.budgetLimit.spent)))+"\n ")]):t._e(),t._v(" "),0===parseFloat(t.budgetLimit.amount)+parseFloat(t.budgetLimit.spent)?s("span",{staticClass:"text-muted"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budgetLimit.currency_code}).format(0))+"\n ")]):t._e(),t._v(" "),parseFloat(t.budgetLimit.amount)+parseFloat(t.budgetLimit.spent)<0?s("span",{staticClass:"text-danger"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budgetLimit.currency_code}).format(parseFloat(t.budgetLimit.amount)+parseFloat(t.budgetLimit.spent)))+"\n ")]):t._e()])])}),[],!1,null,"20d55ede",null).exports,y={name:"BudgetRow",mounted:function(){var t;this.locale=null!==(t=localStorage.locale)&&void 0!==t?t:"en-US"},data:function(){return{locale:"en-US"}},props:{budget:{type:Object,default:{}}}},C={name:"BudgetListGroup",components:{BudgetLimitRow:g,BudgetRow:Object(r.a)(y,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("tr",[s("td",{staticStyle:{width:"25%"}},[s("a",{attrs:{href:"./budgets/show/"+t.budget.id}},[t._v(t._s(t.budget.name))])]),t._v(" "),s("td",{staticClass:"align-middle text-right"},[s("span",{staticClass:"text-danger"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budget.currency_code}).format(parseFloat(t.budget.spent)))+"\n ")])])])}),[],!1,null,"3e0b277e",null).exports},props:{title:String,budgetLimits:Array,budgets:Array}},w={name:"MainBudgetList",components:{BudgetListGroup:Object(r.a)(C,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"card"},[s("div",{staticClass:"card-header"},[s("h3",{staticClass:"card-title"},[t._v(t._s(t.title))])]),t._v(" "),s("div",{staticClass:"card-body table-responsive p-0"},[s("table",{staticClass:"table table-sm"},[s("tbody",[t._l(t.budgetLimits,(function(t,e){return s("BudgetLimitRow",{key:e,attrs:{budgetLimit:t}})})),t._v(" "),t._l(t.budgets,(function(t,e){return s("BudgetRow",{key:e,attrs:{budget:t}})}))],2)])]),t._v(" "),s("div",{staticClass:"card-footer"},[s("a",{staticClass:"btn btn-default button-sm",attrs:{href:"./budgets"}},[s("i",{staticClass:"far fa-money-bill-alt"}),t._v(" "+t._s(t.$t("firefly.go_to_budgets")))])])])}),[],!1,null,"1480b15a",null).exports},data:function(){return{budgetList:["daily","weekly","monthly","quarterly","half_year","yearly","other"],budgetLimits:{daily:[],weekly:[],monthly:[],quarterly:[],half_year:[],yearly:[],other:[]},budgets:{},rawBudgets:[],locale:"en-US"}},created:function(){var t;this.locale=null!==(t=localStorage.locale)&&void 0!==t?t:"en-US",this.collectData()},methods:{collectData:function(){this.getBudgets()},getBudgets:function(){var t=this;axios.get("./api/v1/budgets?start="+window.sessionStart+"&end="+window.sessionEnd).then((function(e){t.parseBudgets(e.data)}))},parseBudgets:function(t){for(var e in t.data)if(t.data.hasOwnProperty(e)&&/^0$|^[1-9]\d*$/.test(e)&&e<=4294967294){var s=t.data[e];for(var a in s.attributes.spent)if(s.attributes.spent.hasOwnProperty(a)&&/^0$|^[1-9]\d*$/.test(a)&&a<=4294967294){var r=s.attributes.spent[a];this.rawBudgets.push({id:parseInt(s.id),name:s.attributes.name,currency_id:parseInt(r.currency_id),currency_code:r.currency_code,spent:r.sum})}}this.getBudgetLimits()},getBudgetLimits:function(){var t=this;axios.get("./api/v1/budgets/limits?start="+window.sessionStart+"&end="+window.sessionEnd).then((function(e){t.parseBudgetLimits(e.data)}))},parseBudgetLimits:function(t){for(var e in t.included)t.included.hasOwnProperty(e)&&/^0$|^[1-9]\d*$/.test(e)&&e<=4294967294&&(this.budgets[t.included[e].id]={id:t.included[e].id,name:t.included[e].attributes.name});for(var s in t.data)if(t.data.hasOwnProperty(s)&&/^0$|^[1-9]\d*$/.test(s)&&s<=4294967294){var a,r=0,n=0,i=0;this.filterBudgets(t.data[s].attributes.budget_id,t.data[s].attributes.currency_id),0!==parseFloat(t.data[s].attributes.spent)&&-1*parseFloat(t.data[s].attributes.spent)parseFloat(t.data[s].attributes.amount)&&(i=100-(n=parseFloat(t.data[s].attributes.amount)/parseFloat(t.data[s].attributes.spent)*-1*100));var c={id:t.data[s].id,amount:t.data[s].attributes.amount,budget_id:t.data[s].attributes.budget_id,budget_name:this.budgets[t.data[s].attributes.budget_id].name,currency_id:t.data[s].attributes.currency_id,currency_code:t.data[s].attributes.currency_code,period:t.data[s].attributes.period,start:new Date(t.data[s].attributes.start),end:new Date(t.data[s].attributes.end),spent:t.data[s].attributes.spent,pctGreen:r,pctOrange:n,pctRed:i},o=null!==(a=t.data[s].attributes.period)&&void 0!==a?a:"other";this.budgetLimits[o].push(c)}},filterBudgets:function(t,e){for(var s in this.rawBudgets)this.rawBudgets.hasOwnProperty(s)&&/^0$|^[1-9]\d*$/.test(s)&&s<=4294967294&&this.rawBudgets[s].currency_id===e&&this.rawBudgets[s].id===t&&this.rawBudgets.splice(s,1)}}},x=Object(r.a)(w,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",[s("div",{staticClass:"row"},[t.budgetLimits.daily.length>0?s("div",{staticClass:"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12"},[s("BudgetListGroup",{attrs:{title:t.$t("firefly.daily_budgets"),budgetLimits:t.budgetLimits.daily}})],1):t._e(),t._v(" "),t.budgetLimits.weekly.length>0?s("div",{staticClass:"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12"},[s("BudgetListGroup",{attrs:{title:t.$t("firefly.weekly_budgets"),budgetLimits:t.budgetLimits.weekly}})],1):t._e(),t._v(" "),t.budgetLimits.monthly.length>0?s("div",{staticClass:"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12"},[s("BudgetListGroup",{attrs:{title:t.$t("firefly.monthly_budgets"),budgetLimits:t.budgetLimits.monthly}})],1):t._e(),t._v(" "),t.budgetLimits.quarterly.length>0?s("div",{staticClass:"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12"},[s("BudgetListGroup",{attrs:{title:t.$t("firefly.quarterly_budgets"),budgetLimits:t.budgetLimits.quarterly}})],1):t._e(),t._v(" "),t.budgetLimits.half_year.length>0?s("div",{staticClass:"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12"},[s("BudgetListGroup",{attrs:{title:t.$t("firefly.half_year_budgets"),budgetLimits:t.budgetLimits.half_year}})],1):t._e(),t._v(" "),t.budgetLimits.yearly.length>0?s("div",{staticClass:"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12"},[s("BudgetListGroup",{attrs:{title:t.$t("firefly.yearly_budgets"),budgetLimits:t.budgetLimits.yearly}})],1):t._e(),t._v(" "),t.budgetLimits.other.length>0||t.rawBudgets.length>0?s("div",{staticClass:"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12"},[s("BudgetListGroup",{attrs:{title:t.$t("firefly.other_budgets"),budgetLimits:t.budgetLimits.other,budgets:t.rawBudgets}})],1):t._e()])])}),[],!1,null,"f0d512f0",null).exports,j={name:"MainCreditList",data:function(){return{locale:"en-US",income:[],max:0}},created:function(){var t;this.locale=null!==(t=localStorage.locale)&&void 0!==t?t:"en-US",this.getExpenses()},methods:{getExpenses:function(){var t=this;axios.get("./api/v1/insight/income/date/basic?start="+window.sessionStart+"&end="+window.sessionEnd).then((function(e){t.parseExpenses(e.data)}))},parseExpenses:function(t){for(var e in t)if(t.hasOwnProperty(e)&&/^0$|^[1-9]\d*$/.test(e)&&e<=4294967294){var s=t[e];0===parseInt(e)&&(this.max=t[e].difference_float,s.pct=100),0!==parseInt(e)&&(s.pct=t[e].difference_float/this.max*100),this.income.push(s)}}}},S=Object(r.a)(j,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"card"},[s("div",{staticClass:"card-header"},[s("h3",{staticClass:"card-title"},[t._v(t._s(t.$t("firefly.revenue_accounts")))])]),t._v(" "),s("div",{staticClass:"card-body table-responsive p-0"},[s("table",{staticClass:"table table-sm"},[s("tbody",t._l(t.income,(function(e){return s("tr",[s("td",{staticStyle:{width:"20%"}},[s("a",{attrs:{href:"./accounts/show/"+e.id}},[t._v(t._s(e.name))])]),t._v(" "),s("td",{staticClass:"align-middle"},[e.pct>0?s("div",{staticClass:"progress"},[s("div",{staticClass:"progress-bar progress-bar-striped bg-success",style:{width:e.pct+"%"},attrs:{role:"progressbar","aria-valuenow":e.pct,"aria-valuemin":"0","aria-valuemax":"100"}},[e.pct>20?s("span",[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.difference_float))+"\n ")]):t._e()]),t._v(" "),e.pct<=20?s("span",[t._v(" \n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.difference_float))+"\n ")]):t._e()]):t._e()])])})),0)])]),t._v(" "),s("div",{staticClass:"card-footer"},[s("a",{staticClass:"btn btn-default button-sm",attrs:{href:"./transactions/deposit"}},[s("i",{staticClass:"far fa-money-bill-alt"}),t._v(" "+t._s(t.$t("firefly.go_to_deposits")))])])])}),[],!1,null,null,null).exports,L={name:"MainDebitList",data:function(){return{locale:"en-US",expenses:[],max:0}},created:function(){var t;this.locale=null!==(t=localStorage.locale)&&void 0!==t?t:"en-US",this.getExpenses()},methods:{getExpenses:function(){var t=this;axios.get("./api/v1/insight/expense/date/basic?start="+window.sessionStart+"&end="+window.sessionEnd).then((function(e){t.parseExpenses(e.data)}))},parseExpenses:function(t){for(var e in t)if(t.hasOwnProperty(e)&&/^0$|^[1-9]\d*$/.test(e)&&e<=4294967294){var s=t[e];0===parseInt(e)&&(this.max=t[e].difference_float,s.pct=100),0!==parseInt(e)&&(s.pct=t[e].difference_float/this.max*100),this.expenses.push(s)}}}},F=Object(r.a)(L,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"card"},[s("div",{staticClass:"card-header"},[s("h3",{staticClass:"card-title"},[t._v(t._s(t.$t("firefly.expense_accounts")))])]),t._v(" "),s("div",{staticClass:"card-body table-responsive p-0"},[s("table",{staticClass:"table table-sm"},[s("tbody",t._l(t.expenses,(function(e){return s("tr",[s("td",{staticStyle:{width:"20%"}},[s("a",{attrs:{href:"./accounts/show/"+e.id}},[t._v(t._s(e.name))])]),t._v(" "),s("td",{staticClass:"align-middle"},[e.pct>0?s("div",{staticClass:"progress"},[s("div",{staticClass:"progress-bar progress-bar-striped bg-danger",style:{width:e.pct+"%"},attrs:{role:"progressbar","aria-valuenow":e.pct,"aria-valuemin":"0","aria-valuemax":"100"}},[e.pct>20?s("span",[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.difference_float))+"\n ")]):t._e()]),t._v(" "),e.pct<=20?s("span",[t._v(" \n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.difference_float))+"\n ")]):t._e()]):t._e()])])})),0)])]),t._v(" "),s("div",{staticClass:"card-footer"},[s("a",{staticClass:"btn btn-default button-sm",attrs:{href:"./transactions/withdrawal"}},[s("i",{staticClass:"far fa-money-bill-alt"}),t._v(" "+t._s(t.$t("firefly.go_to_withdrawals")))])])])}),[],!1,null,null,null).exports,O={name:"MainPiggyList",created:function(){var t=this;axios.get("./api/v1/piggy_banks").then((function(e){t.loadPiggyBanks(e.data.data)}))},computed:{locale:function(){return this.$store.getters.locale}},methods:{loadPiggyBanks:function(t){for(var e in t)if(t.hasOwnProperty(e)&&/^0$|^[1-9]\d*$/.test(e)&&e<=4294967294){var s=t[e];0!==parseFloat(s.attributes.left_to_save)&&(s.attributes.pct=parseFloat(s.attributes.current_amount)/parseFloat(s.attributes.target_amount)*100,this.piggy_banks.push(s))}this.piggy_banks.sort((function(t,e){return e.attributes.pct-t.attributes.pct}))}},data:function(){return{piggy_banks:[]}}},P=Object(r.a)(O,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"card"},[s("div",{staticClass:"card-header"},[s("h3",{staticClass:"card-title"},[t._v(t._s(t.$t("firefly.piggy_banks")))])]),t._v(" "),s("div",{staticClass:"card-body table-responsive p-0"},[s("table",{staticClass:"table table-striped"},[s("caption",{staticStyle:{display:"none"}},[t._v(t._s(t.$t("firefly.piggy_banks")))]),t._v(" "),s("thead",[s("tr",[s("th",{staticStyle:{width:"35%"},attrs:{scope:"col"}},[t._v(t._s(t.$t("list.piggy_bank")))]),t._v(" "),s("th",{staticStyle:{width:"40%"},attrs:{scope:"col"}},[t._v(t._s(t.$t("list.percentage"))+" "),s("small",[t._v("/ "+t._s(t.$t("list.amount")))])])])]),t._v(" "),s("tbody",t._l(this.piggy_banks,(function(e){return s("tr",[s("td",[t._v(t._s(e.attributes.name)+"\n "),e.attributes.object_group_title?s("small",{staticClass:"text-muted"},[s("br"),t._v("\n "+t._s(e.attributes.object_group_title)+"\n ")]):t._e()]),t._v(" "),s("td",[s("div",{staticClass:"progress-group"},[s("div",{staticClass:"progress progress-sm"},[e.attributes.pct<100?s("div",{staticClass:"progress-bar progress-bar-striped primary",style:{width:e.attributes.pct+"%"}}):t._e(),t._v(" "),100===e.attributes.pct?s("div",{staticClass:"progress-bar progress-bar-striped bg-success",style:{width:e.attributes.pct+"%"}}):t._e()])]),t._v(" "),s("span",{staticClass:"text-success"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.attributes.currency_code}).format(e.attributes.current_amount))+"\n ")]),t._v("\n of\n "),s("span",{staticClass:"text-success"},[t._v(t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.attributes.currency_code}).format(e.attributes.target_amount)))])])])})),0)])]),t._v(" "),s("div",{staticClass:"card-footer"},[s("a",{staticClass:"btn btn-default button-sm",attrs:{href:"./piggy-banks"}},[s("i",{staticClass:"far fa-money-bill-alt"}),t._v(" "+t._s(t.$t("firefly.go_to_piggies")))])])])}),[],!1,null,"14a8a9a0",null).exports,k={name:"TransactionListLarge",props:{transactions:{type:Array,default:function(){return[]}},account_id:{type:Number,default:function(){return 0}}}},E=Object(r.a)(k,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("table",{staticClass:"table table-striped table-sm"},[s("caption",{staticStyle:{display:"none"}},[t._v(t._s(t.$t("firefly.transaction_table_description")))]),t._v(" "),s("thead",[s("tr",[s("th",{staticClass:"text-left",attrs:{scope:"col"}},[t._v(t._s(t.$t("firefly.description")))]),t._v(" "),s("th",{attrs:{scope:"col"}},[t._v(t._s(t.$t("firefly.opposing_account")))]),t._v(" "),s("th",{staticClass:"text-right",attrs:{scope:"col"}},[t._v(t._s(t.$t("firefly.amount")))]),t._v(" "),s("th",{attrs:{scope:"col"}},[t._v(t._s(t.$t("firefly.category")))]),t._v(" "),s("th",{attrs:{scope:"col"}},[t._v(t._s(t.$t("firefly.budget")))])])]),t._v(" "),s("tbody",t._l(this.transactions,(function(e){return s("tr",[s("td",[s("a",{attrs:{href:"transactions/show/"+e.id,title:e.date}},[e.attributes.transactions.length>1?s("span",[t._v(t._s(e.attributes.group_title))]):t._e(),t._v(" "),1===e.attributes.transactions.length?s("span",[t._v(t._s(e.attributes.transactions[0].description))]):t._e()])]),t._v(" "),s("td",t._l(e.attributes.transactions,(function(e){return s("span",["withdrawal"===e.type?s("a",{attrs:{href:"accounts/show/"+e.destination_id}},[t._v(t._s(e.destination_name))]):t._e(),t._v(" "),"deposit"===e.type?s("a",{attrs:{href:"accounts/show/"+e.source_id}},[t._v(t._s(e.source_name))]):t._e(),t._v(" "),"transfer"===e.type&&e.source_id===t.account_id?s("a",{attrs:{href:"accounts/show/"+e.destination_id}},[t._v(t._s(e.destination_name))]):t._e(),t._v(" "),"transfer"===e.type&&e.destination_id===t.account_id?s("a",{attrs:{href:"accounts/show/"+e.source_id}},[t._v(t._s(e.source_name))]):t._e(),t._v(" "),s("br")])})),0),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},t._l(e.attributes.transactions,(function(e){return s("span",["withdrawal"===e.type?s("span",{staticClass:"text-danger"},[t._v("\n "+t._s(Intl.NumberFormat("en-US",{style:"currency",currency:e.currency_code}).format(-1*e.amount))),s("br")]):t._e(),t._v(" "),"deposit"===e.type?s("span",{staticClass:"text-success"},[t._v("\n "+t._s(Intl.NumberFormat("en-US",{style:"currency",currency:e.currency_code}).format(e.amount))),s("br")]):t._e(),t._v(" "),"transfer"===e.type&&e.source_id===t.account_id?s("span",{staticClass:"text-info"},[t._v("\n "+t._s(Intl.NumberFormat("en-US",{style:"currency",currency:e.currency_code}).format(-1*e.amount))),s("br")]):t._e(),t._v(" "),"transfer"===e.type&&e.destination_id===t.account_id?s("span",{staticClass:"text-info"},[t._v("\n "+t._s(Intl.NumberFormat("en-US",{style:"currency",currency:e.currency_code}).format(e.amount))),s("br")]):t._e()])})),0),t._v(" "),s("td",t._l(e.attributes.transactions,(function(e){return s("span",[0!==e.category_id?s("a",{attrs:{href:"categories/show/"+e.category_id}},[t._v(t._s(e.category_name))]):t._e(),s("br")])})),0),t._v(" "),s("td",t._l(e.attributes.transactions,(function(e){return s("span",[0!==e.budget_id?s("a",{attrs:{href:"budgets/show/"+e.budget_id}},[t._v(t._s(e.budget_name))]):t._e(),s("br")])})),0)])})),0)])}),[],!1,null,"3b50021b",null).exports,I={name:"TransactionListMedium",props:{transactions:{type:Array,default:function(){return[]}},account_id:{type:Number,default:function(){return 0}}}},B=Object(r.a)(I,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("table",{staticClass:"table table-striped table-sm"},[s("caption",{staticStyle:{display:"none"}},[t._v(t._s(t.$t("firefly.transaction_table_description")))]),t._v(" "),s("thead",[s("tr",[s("th",{staticClass:"text-left",attrs:{scope:"col"}},[t._v(t._s(t.$t("firefly.description")))]),t._v(" "),s("th",{attrs:{scope:"col"}},[t._v(t._s(t.$t("firefly.opposing_account")))]),t._v(" "),s("th",{staticClass:"text-right",attrs:{scope:"col"}},[t._v(t._s(t.$t("firefly.amount")))])])]),t._v(" "),s("tbody",t._l(this.transactions,(function(e){return s("tr",[s("td",[s("a",{attrs:{href:"transactions/show/"+e.id,title:e.date}},[e.attributes.transactions.length>1?s("span",[t._v(t._s(e.attributes.group_title))]):t._e(),t._v(" "),1===e.attributes.transactions.length?s("span",[t._v(t._s(e.attributes.transactions[0].description))]):t._e()])]),t._v(" "),s("td",t._l(e.attributes.transactions,(function(e){return s("span",["withdrawal"===e.type?s("a",{attrs:{href:"accounts/show/"+e.destination_id}},[t._v(t._s(e.destination_name))]):t._e(),t._v(" "),"deposit"===e.type?s("a",{attrs:{href:"accounts/show/"+e.source_id}},[t._v(t._s(e.source_name))]):t._e(),t._v(" "),"transfer"===e.type&&e.source_id===t.account_id?s("a",{attrs:{href:"accounts/show/"+e.destination_id}},[t._v(t._s(e.destination_name))]):t._e(),t._v(" "),"transfer"===e.type&&e.destination_id===t.account_id?s("a",{attrs:{href:"accounts/show/"+e.source_id}},[t._v(t._s(e.source_name))]):t._e(),t._v(" "),s("br")])})),0),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},t._l(e.attributes.transactions,(function(e){return s("span",["withdrawal"===e.type?s("span",{staticClass:"text-danger"},[t._v("\n "+t._s(Intl.NumberFormat("en-US",{style:"currency",currency:e.currency_code}).format(-1*e.amount))),s("br")]):t._e(),t._v(" "),"deposit"===e.type?s("span",{staticClass:"text-success"},[t._v("\n "+t._s(Intl.NumberFormat("en-US",{style:"currency",currency:e.currency_code}).format(e.amount))),s("br")]):t._e(),t._v(" "),"transfer"===e.type&&e.source_id===t.account_id?s("span",{staticClass:"text-info"},[t._v("\n "+t._s(Intl.NumberFormat("en-US",{style:"currency",currency:e.currency_code}).format(-1*e.amount))),s("br")]):t._e(),t._v(" "),"transfer"===e.type&&e.destination_id===t.account_id?s("span",{staticClass:"text-info"},[t._v("\n "+t._s(Intl.NumberFormat("en-US",{style:"currency",currency:e.currency_code}).format(e.amount))),s("br")]):t._e()])})),0)])})),0)])}),[],!1,null,"0c7078c9",null).exports,D={name:"TransactionListSmall",data:function(){return{locale:"en-US"}},created:function(){var t;this.locale=null!==(t=localStorage.locale)&&void 0!==t?t:"en-US"},methods:{},props:{transactions:{type:Array,default:function(){return[]}},account_id:{type:Number,default:function(){return 0}}}},N=Object(r.a)(D,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("table",{staticClass:"table table-striped table-sm"},[s("caption",{staticStyle:{display:"none"}},[t._v(t._s(t.$t("firefly.transaction_table_description")))]),t._v(" "),s("thead",[s("tr",[s("th",{staticClass:"text-left",attrs:{scope:"col"}},[t._v(t._s(t.$t("firefly.description")))]),t._v(" "),s("th",{staticClass:"text-right",attrs:{scope:"col"}},[t._v(t._s(t.$t("firefly.amount")))])])]),t._v(" "),s("tbody",t._l(this.transactions,(function(e){return s("tr",[s("td",[s("a",{attrs:{href:"transactions/show/"+e.id,title:new Intl.DateTimeFormat(t.locale,{year:"numeric",month:"long",day:"numeric"}).format(new Date(e.attributes.transactions[0].date))}},[e.attributes.transactions.length>1?s("span",[t._v(t._s(e.attributes.group_title))]):t._e(),t._v(" "),1===e.attributes.transactions.length?s("span",[t._v(t._s(e.attributes.transactions[0].description))]):t._e()])]),t._v(" "),s("td",{staticStyle:{"text-align":"right"}},t._l(e.attributes.transactions,(function(e){return s("span",["withdrawal"===e.type?s("span",{staticClass:"text-danger"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(-1*e.amount))),s("br")]):t._e(),t._v(" "),"deposit"===e.type?s("span",{staticClass:"text-success"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.amount))),s("br")]):t._e(),t._v(" "),"transfer"===e.type&&e.source_id===t.account_id?s("span",{staticClass:"text-info"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(-1*e.amount))),s("br")]):t._e(),t._v(" "),"transfer"===e.type&&e.destination_id===t.account_id?s("span",{staticClass:"text-info"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.amount))),s("br")]):t._e()])})),0)])})),0)])}),[],!1,null,"fae12aa6",null).exports,U=s(227),T=s.n(U),z={name:"Calendar",created:function(){},data:function(){return{locale:"en-US",range:{start:new Date(window.sessionStart),end:new Date(window.sessionEnd)},defaultRange:{start:new Date(window.sessionStart),end:new Date(window.sessionEnd)}}}},A=(s(238),Object(r.a)(z,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",[s("div",{staticClass:"row"},[s("div",{staticClass:"col"},[t._v("Start")]),t._v(" "),s("div",{staticClass:"col-8"},[t._v(t._s(new Intl.DateTimeFormat(t.locale,{year:"numeric",month:"long",day:"numeric"}).format(t.range.start)))])]),t._v(" "),s("div",{staticClass:"row"},[s("div",{staticClass:"col"},[t._v("End")]),t._v(" "),s("div",{staticClass:"col-8"},[t._v(t._s(new Intl.DateTimeFormat(t.locale,{year:"numeric",month:"long",day:"numeric"}).format(t.range.end)))])]),t._v(" "),s("date-picker",{attrs:{mode:"date",rows:"2","is-range":""},scopedSlots:t._u([{key:"default",fn:function(e){var a=e.inputValue,r=e.inputEvents,n=e.isDragging,i=e.togglePopover;return[s("div",{staticClass:"row"},[s("div",{staticClass:"col"},[s("div",{staticClass:"btn-group btn-group-sm d-flex"},[s("button",{staticClass:"btn btn-secondary btn-sm",on:{click:function(t){return i({placement:"auto-start",positionFixed:!0})}}},[s("i",{staticClass:"fas fa-calendar-alt"})]),t._v(" "),s("button",{staticClass:"btn btn-secondary"},[s("i",{staticClass:"fas fa-history"})]),t._v(" "),s("button",{staticClass:"btn btn-secondary dropdown-toggle",attrs:{type:"button",id:"dropdownMenuButton","data-toggle":"dropdown","aria-haspopup":"true","aria-expanded":"false"}},[s("i",{staticClass:"fas fa-list"})]),t._v(" "),s("div",{staticClass:"dropdown-menu",attrs:{"aria-labelledby":"dropdownMenuButton"}},[s("a",{staticClass:"dropdown-item",attrs:{href:"#"}},[t._v("(prev period)")]),t._v(" "),s("a",{staticClass:"dropdown-item",attrs:{href:"#"}},[t._v("(next period)")]),t._v(" "),s("a",{staticClass:"dropdown-item",attrs:{href:"#"}},[t._v("(this week?)")])])]),t._v(" "),s("input",t._g({class:n?"text-gray-600":"text-gray-900",attrs:{type:"hidden"},domProps:{value:a.start}},r.start)),t._v(" "),s("input",t._g({class:n?"text-gray-600":"text-gray-900",attrs:{type:"hidden"},domProps:{value:a.end}},r.end))])])]}}]),model:{value:t.range,callback:function(e){t.range=e},expression:"range"}})],1)}),[],!1,null,"03e62f58",null).exports),R={name:"MainCategoryList",created:function(){var t;this.locale=null!==(t=localStorage.locale)&&void 0!==t?t:"en-US",this.getCategories()},data:function(){return{locale:"en-US",categories:[],sortedList:[],spent:0,earned:0}},methods:{getCategories:function(){var t=this;axios.get("./api/v1/categories?start="+window.sessionStart+"&end="+window.sessionEnd).then((function(e){t.parseCategories(e.data)}))},parseCategories:function(t){for(var e in t.data)if(t.data.hasOwnProperty(e)&&/^0$|^[1-9]\d*$/.test(e)&&e<=4294967294){var s=t.data[e],a=null,r=parseInt(s.id);for(var n in s.attributes.spent)if(s.attributes.spent.hasOwnProperty(n)&&/^0$|^[1-9]\d*$/.test(n)&&n<=4294967294){var i,c=s.attributes.spent[n];a=c.currency_id.toString()+"-"+s.id.toString(),this.categories[a]=null!==(i=this.categories[a])&&void 0!==i?i:{id:r,name:s.attributes.name,currency_code:c.currency_code,currency_symbol:c.currency_symbol,spent:0,earned:0,spentPct:0,earnedPct:0},this.categories[a].spent=parseFloat(c.sum),this.spent=parseFloat(c.sum)this.earned?parseFloat(u.sum):this.earned}}this.sortCategories()},sortCategories:function(){var t=[];for(var e in this.categories)this.categories.hasOwnProperty(e)&&t.push(this.categories[e]);for(var s in t.sort((function(t,e){return t.spent+t.earned-(e.spent+e.earned)})),t)if(t.hasOwnProperty(s)){var a=t[s];a.spentPct=a.spent/this.spent*100,a.earnedPct=a.earned/this.earned*100,this.sortedList.push(a)}}}},G=Object(r.a)(R,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"card"},[s("div",{staticClass:"card-header"},[s("h3",{staticClass:"card-title"},[t._v(t._s(t.$t("firefly.categories")))])]),t._v(" "),s("div",{staticClass:"card-body table-responsive p-0"},[s("table",{staticClass:"table table-sm"},[s("tbody",t._l(t.sortedList,(function(e){return s("tr",[s("td",{staticStyle:{width:"20%"}},[s("a",{attrs:{href:"./categories/show/"+e.id}},[t._v(t._s(e.name))])]),t._v(" "),s("td",{staticClass:"align-middle"},[e.spentPct>0?s("div",{staticClass:"progress"},[s("div",{staticClass:"progress-bar progress-bar-striped bg-danger",style:{width:e.spentPct+"%"},attrs:{role:"progressbar","aria-valuenow":e.spentPct,"aria-valuemin":"0","aria-valuemax":"100"}},[e.spentPct>20?s("span",[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.spent))+"\n ")]):t._e()]),t._v(" "),e.spentPct<=20?s("span",[t._v(" \n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.spent))+"\n ")]):t._e()]):t._e(),t._v(" "),e.earnedPct>0?s("div",{staticClass:"progress justify-content-end",attrs:{title:"hello2"}},[e.earnedPct<=20?s("span",[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.earned))+"\n  ")]):t._e(),t._v(" "),s("div",{staticClass:"progress-bar progress-bar-striped bg-success",style:{width:e.earnedPct+"%"},attrs:{role:"progressbar","aria-valuenow":e.earnedPct,"aria-valuemin":"0","aria-valuemax":"100",title:"hello"}},[e.earnedPct>20?s("span",[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.earned))+"\n ")]):t._e()])]):t._e()])])})),0)])])])}),[],!1,null,"00bc733f",null).exports,M=s(3),W=s.n(M),q=s(2),K=s(23);s(15),s(266),W.a.component("transaction-list-large",E),W.a.component("transaction-list-medium",B),W.a.component("transaction-list-small",N),W.a.component("date-picker",T.a),W.a.component("dashboard",n),W.a.component("top-boxes",c),W.a.component("main-account",p),W.a.component("main-account-list",m),W.a.component("main-bills-list",v),W.a.component("main-budget-list",x),W.a.component("main-category-list",G),W.a.component("main-debit-list",F),W.a.component("main-credit-list",S),W.a.component("main-piggy-list",P),W.a.use(q.c);var J=s(19),V={};new W.a({i18n:J,store:K.a,el:"#dashboard",render:function(t){return t(n,{props:V})},beforeCreate:function(){this.$store.commit("initialiseStore"),this.$store.dispatch("updateCurrencyPreference")}}),new W.a({i18n:J,store:K.a,el:"#calendar",render:function(t){return t(A,{props:V})}})},412:function(t,e){},52:function(t,e,s){var a=s(239);"string"==typeof a&&(a=[[t.i,a,""]]);var r={hmr:!0,transform:void 0,insertInto:void 0};s(25)(a,r);a.locals&&(t.exports=a.locals)}},[[235,0,1]]]); +(window.webpackJsonp=window.webpackJsonp||[]).push([[4],{196:function(t,e,a){var s=a(250);"string"==typeof s&&(s=[[t.i,s,""]]);var r={hmr:!0,transform:void 0,insertInto:void 0};a(24)(s,r);s.locals&&(t.exports=s.locals)},245:function(t,e,a){a(426),t.exports=a(468)},246:function(t,e,a){var s={"./af":59,"./af.js":59,"./ar":60,"./ar-dz":61,"./ar-dz.js":61,"./ar-kw":62,"./ar-kw.js":62,"./ar-ly":63,"./ar-ly.js":63,"./ar-ma":64,"./ar-ma.js":64,"./ar-sa":65,"./ar-sa.js":65,"./ar-tn":66,"./ar-tn.js":66,"./ar.js":60,"./az":67,"./az.js":67,"./be":68,"./be.js":68,"./bg":69,"./bg.js":69,"./bm":70,"./bm.js":70,"./bn":71,"./bn-bd":72,"./bn-bd.js":72,"./bn.js":71,"./bo":73,"./bo.js":73,"./br":74,"./br.js":74,"./bs":75,"./bs.js":75,"./ca":76,"./ca.js":76,"./cs":77,"./cs.js":77,"./cv":78,"./cv.js":78,"./cy":79,"./cy.js":79,"./da":80,"./da.js":80,"./de":81,"./de-at":82,"./de-at.js":82,"./de-ch":83,"./de-ch.js":83,"./de.js":81,"./dv":84,"./dv.js":84,"./el":85,"./el.js":85,"./en-au":86,"./en-au.js":86,"./en-ca":87,"./en-ca.js":87,"./en-gb":88,"./en-gb.js":88,"./en-ie":89,"./en-ie.js":89,"./en-il":90,"./en-il.js":90,"./en-in":91,"./en-in.js":91,"./en-nz":92,"./en-nz.js":92,"./en-sg":93,"./en-sg.js":93,"./eo":94,"./eo.js":94,"./es":95,"./es-do":96,"./es-do.js":96,"./es-mx":97,"./es-mx.js":97,"./es-us":98,"./es-us.js":98,"./es.js":95,"./et":99,"./et.js":99,"./eu":100,"./eu.js":100,"./fa":101,"./fa.js":101,"./fi":102,"./fi.js":102,"./fil":103,"./fil.js":103,"./fo":104,"./fo.js":104,"./fr":105,"./fr-ca":106,"./fr-ca.js":106,"./fr-ch":107,"./fr-ch.js":107,"./fr.js":105,"./fy":108,"./fy.js":108,"./ga":109,"./ga.js":109,"./gd":110,"./gd.js":110,"./gl":111,"./gl.js":111,"./gom-deva":112,"./gom-deva.js":112,"./gom-latn":113,"./gom-latn.js":113,"./gu":114,"./gu.js":114,"./he":115,"./he.js":115,"./hi":116,"./hi.js":116,"./hr":117,"./hr.js":117,"./hu":118,"./hu.js":118,"./hy-am":119,"./hy-am.js":119,"./id":120,"./id.js":120,"./is":121,"./is.js":121,"./it":122,"./it-ch":123,"./it-ch.js":123,"./it.js":122,"./ja":124,"./ja.js":124,"./jv":125,"./jv.js":125,"./ka":126,"./ka.js":126,"./kk":127,"./kk.js":127,"./km":128,"./km.js":128,"./kn":129,"./kn.js":129,"./ko":130,"./ko.js":130,"./ku":131,"./ku.js":131,"./ky":132,"./ky.js":132,"./lb":133,"./lb.js":133,"./lo":134,"./lo.js":134,"./lt":135,"./lt.js":135,"./lv":136,"./lv.js":136,"./me":137,"./me.js":137,"./mi":138,"./mi.js":138,"./mk":139,"./mk.js":139,"./ml":140,"./ml.js":140,"./mn":141,"./mn.js":141,"./mr":142,"./mr.js":142,"./ms":143,"./ms-my":144,"./ms-my.js":144,"./ms.js":143,"./mt":145,"./mt.js":145,"./my":146,"./my.js":146,"./nb":147,"./nb.js":147,"./ne":148,"./ne.js":148,"./nl":149,"./nl-be":150,"./nl-be.js":150,"./nl.js":149,"./nn":151,"./nn.js":151,"./oc-lnc":152,"./oc-lnc.js":152,"./pa-in":153,"./pa-in.js":153,"./pl":154,"./pl.js":154,"./pt":155,"./pt-br":156,"./pt-br.js":156,"./pt.js":155,"./ro":157,"./ro.js":157,"./ru":158,"./ru.js":158,"./sd":159,"./sd.js":159,"./se":160,"./se.js":160,"./si":161,"./si.js":161,"./sk":162,"./sk.js":162,"./sl":163,"./sl.js":163,"./sq":164,"./sq.js":164,"./sr":165,"./sr-cyrl":166,"./sr-cyrl.js":166,"./sr.js":165,"./ss":167,"./ss.js":167,"./sv":168,"./sv.js":168,"./sw":169,"./sw.js":169,"./ta":170,"./ta.js":170,"./te":171,"./te.js":171,"./tet":172,"./tet.js":172,"./tg":173,"./tg.js":173,"./th":174,"./th.js":174,"./tk":175,"./tk.js":175,"./tl-ph":176,"./tl-ph.js":176,"./tlh":177,"./tlh.js":177,"./tr":178,"./tr.js":178,"./tzl":179,"./tzl.js":179,"./tzm":180,"./tzm-latn":181,"./tzm-latn.js":181,"./tzm.js":180,"./ug-cn":182,"./ug-cn.js":182,"./uk":183,"./uk.js":183,"./ur":184,"./ur.js":184,"./uz":185,"./uz-latn":186,"./uz-latn.js":186,"./uz.js":185,"./vi":187,"./vi.js":187,"./x-pseudo":188,"./x-pseudo.js":188,"./yo":189,"./yo.js":189,"./zh-cn":190,"./zh-cn.js":190,"./zh-hk":191,"./zh-hk.js":191,"./zh-mo":192,"./zh-mo.js":192,"./zh-tw":193,"./zh-tw.js":193};function r(t){var e=n(t);return a(e)}function n(t){if(!a.o(s,t)){var e=new Error("Cannot find module '"+t+"'");throw e.code="MODULE_NOT_FOUND",e}return s[t]}r.keys=function(){return Object.keys(s)},r.resolve=n,t.exports=r,r.id=246},249:function(t,e,a){"use strict";a(196)},250:function(t,e,a){(t.exports=a(23)(!1)).push([t.i,".dropdown-item[data-v-51abf689],.dropdown-item[data-v-51abf689]:hover{color:#212529}",""])},426:function(t,e,a){"use strict";a.r(e);var s={name:"Dashboard"},r=a(1),n=Object(r.a)(s,(function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",[a("top-boxes"),t._v(" "),a("div",{staticClass:"row"},[a("div",{staticClass:"col"},[a("main-account")],1)]),t._v(" "),a("main-account-list"),t._v(" "),a("div",{staticClass:"row"},[a("div",{staticClass:"col"},[a("main-budget-list")],1)]),t._v(" "),a("div",{staticClass:"row"},[a("div",{staticClass:"col"},[a("main-category-list")],1)]),t._v(" "),a("div",{staticClass:"row"},[a("div",{staticClass:"col-lg-6 col-md-12 col-sm-12 col-xs-12"},[a("main-debit-list")],1),t._v(" "),a("div",{staticClass:"col-lg-6 col-md-12 col-sm-12 col-xs-12"},[a("main-credit-list")],1)]),t._v(" "),a("div",{staticClass:"row"},[a("div",{staticClass:"col-lg-6 col-md-12 col-sm-12 col-xs-12"},[a("main-piggy-list")],1),t._v(" "),a("div",{staticClass:"col-lg-6 col-md-12 col-sm-12 col-xs-12"},[a("main-bills-list")],1)])],1)}),[],!1,null,null,null).exports,i=a(3);function c(t,e){var a=Object.keys(t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(t);e&&(s=s.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),a.push.apply(a,s)}return a}function o(t){for(var e=1;e0){var i=r+" "+t;if(!(i.length>e))return n===s.length-1?void a.push(i):void(r=i);a.push(r),r=""}n!==s.length-1&&t.length2}},[a("div",{staticClass:"card"},[a("div",{staticClass:"card-header"},[a("h3",{staticClass:"card-title"},[a("a",{attrs:{href:e.url}},[t._v(t._s(e.title))])]),t._v(" "),a("div",{staticClass:"card-tools"},[a("span",{class:parseFloat(e.current_balance)<0?"text-danger":"text-success"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(parseFloat(e.current_balance)))+"\n ")])])]),t._v(" "),a("div",{staticClass:"card-body table-responsive p-0"},[a("div",[1===t.accounts.length?a("transaction-list-large",{attrs:{account_id:e.id,transactions:e.transactions}}):t._e(),t._v(" "),2===t.accounts.length?a("transaction-list-medium",{attrs:{account_id:e.id,transactions:e.transactions}}):t._e(),t._v(" "),t.accounts.length>2?a("transaction-list-small",{attrs:{account_id:e.id,transactions:e.transactions}}):t._e()],1)])])])})),0)])}),[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"col"},[e("div",{staticClass:"card"},[e("div",{staticClass:"card-body"},[e("div",{staticClass:"text-center"},[e("i",{staticClass:"fas fa-spinner fa-spin"})])])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"col"},[e("div",{staticClass:"card"},[e("div",{staticClass:"card-body"},[e("div",{staticClass:"text-center"},[e("i",{staticClass:"fas fa-exclamation-triangle text-danger"})])])])])}],!1,null,"5d6bb842",null).exports;function N(t,e){var a=Object.keys(t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(t);e&&(s=s.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),a.push.apply(a,s)}return a}function T(t){for(var e=1;e'+a+""},loadBills:function(t){for(var e in t)if(t.hasOwnProperty(e)&&/^0$|^[1-9]\d*$/.test(e)&&e<=4294967294){var a=t[e],s=a.attributes.active;a.attributes.pay_dates.length>0&&s&&this.bills.push(a)}this.error=!1,this.loading=!1}}}),M=Object(r.a)(z,(function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"card"},[a("div",{staticClass:"card-header"},[a("h3",{staticClass:"card-title"},[t._v(t._s(t.$t("firefly.bills")))])]),t._v(" "),t.loading&&!t.error?a("div",{staticClass:"card-body"},[t._m(0)]):t._e(),t._v(" "),t.error?a("div",{staticClass:"card-body"},[t._m(1)]):t._e(),t._v(" "),t.loading||t.error?t._e():a("div",{staticClass:"card-body table-responsive p-0"},[a("table",{staticClass:"table table-striped"},[a("caption",{staticStyle:{display:"none"}},[t._v(t._s(t.$t("firefly.bills")))]),t._v(" "),a("thead",[a("tr",[a("th",{staticStyle:{width:"35%"},attrs:{scope:"col"}},[t._v(t._s(t.$t("list.name")))]),t._v(" "),a("th",{staticStyle:{width:"25%"},attrs:{scope:"col"}},[t._v(t._s(t.$t("list.next_expected_match")))])])]),t._v(" "),a("tbody",t._l(this.bills,(function(e){return a("tr",[a("td",[a("a",{attrs:{href:"./bills/show/"+e.id,title:e.attributes.name}},[t._v(t._s(e.attributes.name))]),t._v("\n (~ "),a("span",{staticClass:"text-danger"},[t._v(t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.attributes.currency_code}).format((parseFloat(e.attributes.amount_min)+parseFloat(e.attributes.amount_max))/-2)))]),t._v(")\n "),e.attributes.object_group_title?a("small",{staticClass:"text-muted"},[a("br"),t._v("\n "+t._s(e.attributes.object_group_title)+"\n ")]):t._e()]),t._v(" "),a("td",[t._l(e.attributes.paid_dates,(function(e){return a("span",[a("span",{domProps:{innerHTML:t._s(t.renderPaidDate(e))}}),a("br")])})),t._v(" "),t._l(e.attributes.pay_dates,(function(s){return 0===e.attributes.paid_dates.length?a("span",[t._v("\n "+t._s(new Intl.DateTimeFormat(t.locale,{year:"numeric",month:"long",day:"numeric"}).format(new Date(s)))),a("br")]):t._e()}))],2)])})),0)])]),t._v(" "),a("div",{staticClass:"card-footer"},[a("a",{staticClass:"btn btn-default button-sm",attrs:{href:"./bills"}},[a("i",{staticClass:"far fa-money-bill-alt"}),t._v(" "+t._s(t.$t("firefly.go_to_bills")))])])])}),[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"text-center"},[e("i",{staticClass:"fas fa-spinner fa-spin"})])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"text-center"},[e("i",{staticClass:"fas fa-exclamation-triangle text-danger"})])}],!1,null,null,null).exports,G={name:"BudgetLimitRow",created:function(){var t;this.locale=null!==(t=localStorage.locale)&&void 0!==t?t:"en-US"},data:function(){return{locale:"en-US"}},props:{budgetLimit:{type:Object,default:function(){return{}}},budget:{type:Object,default:function(){return{}}}}},W=Object(r.a)(G,(function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("tr",[a("td",{staticStyle:{width:"25%"}},[a("a",{attrs:{href:"./budgets/show/"+t.budgetLimit.budget_id}},[t._v(t._s(t.budgetLimit.budget_name))])]),t._v(" "),a("td",{staticStyle:{"vertical-align":"middle"}},[a("div",{staticClass:"progress progress active"},[a("div",{staticClass:"progress-bar bg-success progress-bar-striped",style:"width: "+t.budgetLimit.pctGreen+"%;",attrs:{"aria-valuenow":t.budgetLimit.pctGreen,"aria-valuemax":"100","aria-valuemin":"0",role:"progressbar"}},[t.budgetLimit.pctGreen>35?a("span",[t._v("\n Spent\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budgetLimit.currency_code}).format(t.budgetLimit.spent))+"\n of\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budgetLimit.currency_code}).format(t.budgetLimit.amount))+"\n ")]):t._e()]),t._v(" "),a("div",{staticClass:"progress-bar bg-warning progress-bar-striped",style:"width: "+t.budgetLimit.pctOrange+"%;",attrs:{"aria-valuenow":t.budgetLimit.pctOrange,"aria-valuemax":"100","aria-valuemin":"0",role:"progressbar"}},[t.budgetLimit.pctRed<=50&&t.budgetLimit.pctOrange>35?a("span",[t._v("\n Spent\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budgetLimit.currency_code}).format(t.budgetLimit.spent))+"\n of\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budgetLimit.currency_code}).format(t.budgetLimit.amount))+"\n ")]):t._e()]),t._v(" "),a("div",{staticClass:"progress-bar bg-danger progress-bar-striped",style:"width: "+t.budgetLimit.pctRed+"%;",attrs:{"aria-valuenow":t.budgetLimit.pctRed,"aria-valuemax":"100","aria-valuemin":"0",role:"progressbar"}},[t.budgetLimit.pctOrange<=50&&t.budgetLimit.pctRed>35?a("span",{staticClass:"text-muted"},[t._v("\n Spent\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budgetLimit.currency_code}).format(t.budgetLimit.spent))+"\n of\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budgetLimit.currency_code}).format(t.budgetLimit.amount))+"\n ")]):t._e()]),t._v(" "),t.budgetLimit.pctGreen<=35&&0===t.budgetLimit.pctOrange&&0===t.budgetLimit.pctRed&&0!==t.budgetLimit.pctGreen?a("span",[t._v("\n  \n Spent\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budgetLimit.currency_code}).format(t.budgetLimit.spent))+"\n of\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budgetLimit.currency_code}).format(t.budgetLimit.amount))+"\n ")]):t._e()]),t._v(" "),a("small",{staticClass:"d-none d-lg-block"},[t._v("\n "+t._s(new Intl.DateTimeFormat(t.locale,{year:"numeric",month:"long",day:"numeric"}).format(t.budgetLimit.start))+"\n →\n "+t._s(new Intl.DateTimeFormat(t.locale,{year:"numeric",month:"long",day:"numeric"}).format(t.budgetLimit.end))+"\n ")])]),t._v(" "),a("td",{staticClass:"align-middle d-none d-lg-table-cell",staticStyle:{width:"10%"}},[parseFloat(t.budgetLimit.amount)+parseFloat(t.budgetLimit.spent)>0?a("span",{staticClass:"text-success"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budgetLimit.currency_code}).format(parseFloat(t.budgetLimit.amount)+parseFloat(t.budgetLimit.spent)))+"\n ")]):t._e(),t._v(" "),0===parseFloat(t.budgetLimit.amount)+parseFloat(t.budgetLimit.spent)?a("span",{staticClass:"text-muted"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budgetLimit.currency_code}).format(0))+"\n ")]):t._e(),t._v(" "),parseFloat(t.budgetLimit.amount)+parseFloat(t.budgetLimit.spent)<0?a("span",{staticClass:"text-danger"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budgetLimit.currency_code}).format(parseFloat(t.budgetLimit.amount)+parseFloat(t.budgetLimit.spent)))+"\n ")]):t._e()])])}),[],!1,null,"6b6de222",null).exports,q={name:"BudgetRow",created:function(){var t;this.locale=null!==(t=localStorage.locale)&&void 0!==t?t:"en-US"},data:function(){return{locale:"en-US"}},props:{budget:{type:Object,default:{}}}},K={name:"BudgetListGroup",components:{BudgetLimitRow:W,BudgetRow:Object(r.a)(q,(function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("tr",[a("td",{staticStyle:{width:"25%"}},[a("a",{attrs:{href:"./budgets/show/"+t.budget.id}},[t._v(t._s(t.budget.name))])]),t._v(" "),a("td",{staticClass:"align-middle text-right"},[a("span",{staticClass:"text-danger"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:t.budget.currency_code}).format(parseFloat(t.budget.spent)))+"\n ")])])])}),[],!1,null,"2fc8f640",null).exports},props:{title:String,budgetLimits:Array,budgets:Array}},J=Object(r.a)(K,(function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"card"},[a("div",{staticClass:"card-header"},[a("h3",{staticClass:"card-title"},[t._v(t._s(t.title))])]),t._v(" "),a("div",{staticClass:"card-body table-responsive p-0"},[a("table",{staticClass:"table table-sm"},[a("tbody",[t._l(t.budgetLimits,(function(t,e){return a("BudgetLimitRow",{key:e,attrs:{budgetLimit:t}})})),t._v(" "),t._l(t.budgets,(function(t,e){return a("BudgetRow",{key:e,attrs:{budget:t}})}))],2)])]),t._v(" "),a("div",{staticClass:"card-footer"},[a("a",{staticClass:"btn btn-default button-sm",attrs:{href:"./budgets"}},[a("i",{staticClass:"far fa-money-bill-alt"}),t._v(" "+t._s(t.$t("firefly.go_to_budgets")))])])])}),[],!1,null,"658dd996",null).exports;function H(t,e){var a=Object.keys(t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(t);e&&(s=s.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),a.push.apply(a,s)}return a}function V(t){for(var e=1;eparseFloat(t.data[a].attributes.amount)&&(i=100-(n=parseFloat(t.data[a].attributes.amount)/parseFloat(t.data[a].attributes.spent)*-1*100));var c={id:t.data[a].id,amount:t.data[a].attributes.amount,budget_id:t.data[a].attributes.budget_id,budget_name:this.budgets[t.data[a].attributes.budget_id].name,currency_id:t.data[a].attributes.currency_id,currency_code:t.data[a].attributes.currency_code,period:t.data[a].attributes.period,start:new Date(t.data[a].attributes.start),end:new Date(t.data[a].attributes.end),spent:t.data[a].attributes.spent,pctGreen:r,pctOrange:n,pctRed:i},o=null!==(s=t.data[a].attributes.period)&&void 0!==s?s:"other";this.budgetLimits[o].push(c)}},filterBudgets:function(t,e){for(var a in this.rawBudgets)this.rawBudgets.hasOwnProperty(a)&&/^0$|^[1-9]\d*$/.test(a)&&a<=4294967294&&this.rawBudgets[a].currency_id===e&&this.rawBudgets[a].id===t&&this.rawBudgets.splice(a,1)}}}),tt=Object(r.a)(Y,(function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",[t.loading?t._e():a("div",{staticClass:"row"},[t.budgetLimits.daily.length>0?a("div",{staticClass:"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12"},[a("BudgetListGroup",{attrs:{budgetLimits:t.budgetLimits.daily,title:t.$t("firefly.daily_budgets")}})],1):t._e(),t._v(" "),t.budgetLimits.weekly.length>0?a("div",{staticClass:"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12"},[a("BudgetListGroup",{attrs:{budgetLimits:t.budgetLimits.weekly,title:t.$t("firefly.weekly_budgets")}})],1):t._e(),t._v(" "),t.budgetLimits.monthly.length>0?a("div",{staticClass:"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12"},[a("BudgetListGroup",{attrs:{budgetLimits:t.budgetLimits.monthly,title:t.$t("firefly.monthly_budgets")}})],1):t._e(),t._v(" "),t.budgetLimits.quarterly.length>0?a("div",{staticClass:"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12"},[a("BudgetListGroup",{attrs:{budgetLimits:t.budgetLimits.quarterly,title:t.$t("firefly.quarterly_budgets")}})],1):t._e(),t._v(" "),t.budgetLimits.half_year.length>0?a("div",{staticClass:"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12"},[a("BudgetListGroup",{attrs:{budgetLimits:t.budgetLimits.half_year,title:t.$t("firefly.half_year_budgets")}})],1):t._e(),t._v(" "),t.budgetLimits.yearly.length>0?a("div",{staticClass:"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12"},[a("BudgetListGroup",{attrs:{budgetLimits:t.budgetLimits.yearly,title:t.$t("firefly.yearly_budgets")}})],1):t._e(),t._v(" "),t.budgetLimits.other.length>0||t.rawBudgets.length>0?a("div",{staticClass:"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12"},[a("BudgetListGroup",{attrs:{budgetLimits:t.budgetLimits.other,budgets:t.rawBudgets,title:t.$t("firefly.other_budgets")}})],1):t._e()]),t._v(" "),t.loading&&!t.error?a("div",{staticClass:"row"},[t._m(0)]):t._e()])}),[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"col"},[e("div",{staticClass:"card"},[e("div",{staticClass:"card-body"},[e("div",{staticClass:"text-center"},[e("i",{staticClass:"fas fa-spinner fa-spin"})])])])])}],!1,null,"03d11977",null).exports;function et(t,e){var a=Object.keys(t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(t);e&&(s=s.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),a.push.apply(a,s)}return a}function at(t){for(var e=1;e0?a("div",{staticClass:"progress"},[a("div",{staticClass:"progress-bar progress-bar-striped bg-success",style:{width:e.pct+"%"},attrs:{"aria-valuenow":e.pct,"aria-valuemax":"100","aria-valuemin":"0",role:"progressbar"}},[e.pct>20?a("span",[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.difference_float))+"\n ")]):t._e()]),t._v(" "),e.pct<=20?a("span",[t._v(" \n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.difference_float))+"\n ")]):t._e()]):t._e()])])})),0)])]),t._v(" "),a("div",{staticClass:"card-footer"},[a("a",{staticClass:"btn btn-default button-sm",attrs:{href:"./transactions/deposit"}},[a("i",{staticClass:"far fa-money-bill-alt"}),t._v(" "+t._s(t.$t("firefly.go_to_deposits")))])])])}),[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"text-center"},[e("i",{staticClass:"fas fa-spinner fa-spin"})])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"text-center"},[e("i",{staticClass:"fas fa-exclamation-triangle text-danger"})])}],!1,null,null,null).exports;function ot(t,e){var a=Object.keys(t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(t);e&&(s=s.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),a.push.apply(a,s)}return a}function lt(t){for(var e=1;e0?a("div",{staticClass:"progress"},[a("div",{staticClass:"progress-bar progress-bar-striped bg-danger",style:{width:e.pct+"%"},attrs:{"aria-valuenow":e.pct,"aria-valuemax":"100","aria-valuemin":"0",role:"progressbar"}},[e.pct>20?a("span",[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.difference_float))+"\n ")]):t._e()]),t._v(" "),e.pct<=20?a("span",[t._v(" \n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.difference_float))+"\n ")]):t._e()]):t._e()])])})),0)])]),t._v(" "),a("div",{staticClass:"card-footer"},[a("a",{staticClass:"btn btn-default button-sm",attrs:{href:"./transactions/withdrawal"}},[a("i",{staticClass:"far fa-money-bill-alt"}),t._v(" "+t._s(t.$t("firefly.go_to_withdrawals")))])])])}),[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"text-center"},[e("i",{staticClass:"fas fa-spinner fa-spin"})])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"text-center"},[e("i",{staticClass:"fas fa-exclamation-triangle text-danger"})])}],!1,null,null,null).exports,bt={name:"MainPiggyList",data:function(){return{piggy_banks:[],loading:!0,error:!1,locale:"en-US"}},created:function(){var t,e=this;this.locale=null!==(t=localStorage.locale)&&void 0!==t?t:"en-US",axios.get("./api/v1/piggy_banks").then((function(t){e.loadPiggyBanks(t.data.data),e.loading=!1})).catch((function(t){e.error=!0}))},methods:{loadPiggyBanks:function(t){for(var e in t)if(t.hasOwnProperty(e)&&/^0$|^[1-9]\d*$/.test(e)&&e<=4294967294){var a=t[e];0!==parseFloat(a.attributes.left_to_save)&&(a.attributes.pct=parseFloat(a.attributes.current_amount)/parseFloat(a.attributes.target_amount)*100,this.piggy_banks.push(a))}this.piggy_banks.sort((function(t,e){return e.attributes.pct-t.attributes.pct}))}}},ht=Object(r.a)(bt,(function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"card"},[a("div",{staticClass:"card-header"},[a("h3",{staticClass:"card-title"},[t._v(t._s(t.$t("firefly.piggy_banks")))])]),t._v(" "),t.loading&&!t.error?a("div",{staticClass:"card-body"},[t._m(0)]):t._e(),t._v(" "),t.error?a("div",{staticClass:"card-body"},[t._m(1)]):t._e(),t._v(" "),t.loading||t.error?t._e():a("div",{staticClass:"card-body table-responsive p-0"},[a("table",{staticClass:"table table-striped"},[a("caption",{staticStyle:{display:"none"}},[t._v(t._s(t.$t("firefly.piggy_banks")))]),t._v(" "),a("thead",[a("tr",[a("th",{staticStyle:{width:"35%"},attrs:{scope:"col"}},[t._v(t._s(t.$t("list.piggy_bank")))]),t._v(" "),a("th",{staticStyle:{width:"40%"},attrs:{scope:"col"}},[t._v(t._s(t.$t("list.percentage"))+" "),a("small",[t._v("/ "+t._s(t.$t("list.amount")))])])])]),t._v(" "),a("tbody",t._l(this.piggy_banks,(function(e){return a("tr",[a("td",[a("a",{attrs:{href:"./piggy-banks/show/"+e.id,title:e.attributes.name}},[t._v(t._s(e.attributes.name))]),t._v(" "),e.attributes.object_group_title?a("small",{staticClass:"text-muted"},[a("br"),t._v("\n "+t._s(e.attributes.object_group_title)+"\n ")]):t._e()]),t._v(" "),a("td",[a("div",{staticClass:"progress-group"},[a("div",{staticClass:"progress progress-sm"},[e.attributes.pct<100?a("div",{staticClass:"progress-bar progress-bar-striped primary",style:{width:e.attributes.pct+"%"}}):t._e(),t._v(" "),100===e.attributes.pct?a("div",{staticClass:"progress-bar progress-bar-striped bg-success",style:{width:e.attributes.pct+"%"}}):t._e()])]),t._v(" "),a("span",{staticClass:"text-success"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.attributes.currency_code}).format(e.attributes.current_amount))+"\n ")]),t._v("\n of\n "),a("span",{staticClass:"text-success"},[t._v(t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.attributes.currency_code}).format(e.attributes.target_amount)))])])])})),0)])]),t._v(" "),a("div",{staticClass:"card-footer"},[a("a",{staticClass:"btn btn-default button-sm",attrs:{href:"./piggy-banks"}},[a("i",{staticClass:"far fa-money-bill-alt"}),t._v(" "+t._s(t.$t("firefly.go_to_piggies")))])])])}),[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"text-center"},[e("i",{staticClass:"fas fa-spinner fa-spin"})])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"text-center"},[e("i",{staticClass:"fas fa-exclamation-triangle text-danger"})])}],!1,null,"c17c9a5a",null).exports,mt={name:"TransactionListLarge",data:function(){return{locale:"en-US"}},created:function(){var t;this.locale=null!==(t=localStorage.locale)&&void 0!==t?t:"en-US"},props:{transactions:{type:Array,default:function(){return[]}},account_id:{type:Number,default:function(){return 0}}}},vt=Object(r.a)(mt,(function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("table",{staticClass:"table table-striped table-sm"},[a("caption",{staticStyle:{display:"none"}},[t._v(t._s(t.$t("firefly.transaction_table_description")))]),t._v(" "),a("thead",[a("tr",[a("th",{staticClass:"text-left",attrs:{scope:"col"}},[t._v(t._s(t.$t("firefly.description")))]),t._v(" "),a("th",{attrs:{scope:"col"}},[t._v(t._s(t.$t("firefly.opposing_account")))]),t._v(" "),a("th",{staticClass:"text-right",attrs:{scope:"col"}},[t._v(t._s(t.$t("firefly.amount")))]),t._v(" "),a("th",{attrs:{scope:"col"}},[t._v(t._s(t.$t("firefly.category")))]),t._v(" "),a("th",{attrs:{scope:"col"}},[t._v(t._s(t.$t("firefly.budget")))])])]),t._v(" "),a("tbody",t._l(this.transactions,(function(e){return a("tr",[a("td",[a("a",{attrs:{href:"transactions/show/"+e.id,title:e.date}},[e.attributes.transactions.length>1?a("span",[t._v(t._s(e.attributes.group_title))]):t._e(),t._v(" "),1===e.attributes.transactions.length?a("span",[t._v(t._s(e.attributes.transactions[0].description))]):t._e()])]),t._v(" "),a("td",t._l(e.attributes.transactions,(function(e){return a("span",["withdrawal"===e.type?a("a",{attrs:{href:"accounts/show/"+e.destination_id}},[t._v(t._s(e.destination_name))]):t._e(),t._v(" "),"deposit"===e.type?a("a",{attrs:{href:"accounts/show/"+e.source_id}},[t._v(t._s(e.source_name))]):t._e(),t._v(" "),"transfer"===e.type&&e.source_id===t.account_id?a("a",{attrs:{href:"accounts/show/"+e.destination_id}},[t._v(t._s(e.destination_name))]):t._e(),t._v(" "),"transfer"===e.type&&e.destination_id===t.account_id?a("a",{attrs:{href:"accounts/show/"+e.source_id}},[t._v(t._s(e.source_name))]):t._e(),t._v(" "),a("br")])})),0),t._v(" "),a("td",{staticStyle:{"text-align":"right"}},t._l(e.attributes.transactions,(function(e){return a("span",["withdrawal"===e.type?a("span",{staticClass:"text-danger"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(-1*e.amount))),a("br")]):t._e(),t._v(" "),"deposit"===e.type?a("span",{staticClass:"text-success"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.amount))),a("br")]):t._e(),t._v(" "),"transfer"===e.type&&e.source_id===t.account_id?a("span",{staticClass:"text-info"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(-1*e.amount))),a("br")]):t._e(),t._v(" "),"transfer"===e.type&&e.destination_id===t.account_id?a("span",{staticClass:"text-info"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.amount))),a("br")]):t._e()])})),0),t._v(" "),a("td",t._l(e.attributes.transactions,(function(e){return a("span",[0!==e.category_id?a("a",{attrs:{href:"categories/show/"+e.category_id}},[t._v(t._s(e.category_name))]):t._e(),a("br")])})),0),t._v(" "),a("td",t._l(e.attributes.transactions,(function(e){return a("span",[0!==e.budget_id?a("a",{attrs:{href:"budgets/show/"+e.budget_id}},[t._v(t._s(e.budget_name))]):t._e(),a("br")])})),0)])})),0)])}),[],!1,null,"3aeaec71",null).exports,gt={name:"TransactionListMedium",data:function(){return{locale:"en-US"}},created:function(){var t;this.locale=null!==(t=localStorage.locale)&&void 0!==t?t:"en-US"},props:{transactions:{type:Array,default:function(){return[]}},account_id:{type:Number,default:function(){return 0}}}},yt=Object(r.a)(gt,(function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("table",{staticClass:"table table-striped table-sm"},[a("caption",{staticStyle:{display:"none"}},[t._v(t._s(t.$t("firefly.transaction_table_description")))]),t._v(" "),a("thead",[a("tr",[a("th",{staticClass:"text-left",attrs:{scope:"col"}},[t._v(t._s(t.$t("firefly.description")))]),t._v(" "),a("th",{attrs:{scope:"col"}},[t._v(t._s(t.$t("firefly.opposing_account")))]),t._v(" "),a("th",{staticClass:"text-right",attrs:{scope:"col"}},[t._v(t._s(t.$t("firefly.amount")))])])]),t._v(" "),a("tbody",t._l(this.transactions,(function(e){return a("tr",[a("td",[a("a",{attrs:{href:"transactions/show/"+e.id,title:e.date}},[e.attributes.transactions.length>1?a("span",[t._v(t._s(e.attributes.group_title))]):t._e(),t._v(" "),1===e.attributes.transactions.length?a("span",[t._v(t._s(e.attributes.transactions[0].description))]):t._e()])]),t._v(" "),a("td",t._l(e.attributes.transactions,(function(e){return a("span",["withdrawal"===e.type?a("a",{attrs:{href:"accounts/show/"+e.destination_id}},[t._v(t._s(e.destination_name))]):t._e(),t._v(" "),"deposit"===e.type?a("a",{attrs:{href:"accounts/show/"+e.source_id}},[t._v(t._s(e.source_name))]):t._e(),t._v(" "),"transfer"===e.type&&e.source_id===t.account_id?a("a",{attrs:{href:"accounts/show/"+e.destination_id}},[t._v(t._s(e.destination_name))]):t._e(),t._v(" "),"transfer"===e.type&&e.destination_id===t.account_id?a("a",{attrs:{href:"accounts/show/"+e.source_id}},[t._v(t._s(e.source_name))]):t._e(),t._v(" "),a("br")])})),0),t._v(" "),a("td",{staticStyle:{"text-align":"right"}},t._l(e.attributes.transactions,(function(e){return a("span",["withdrawal"===e.type?a("span",{staticClass:"text-danger"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(-1*e.amount))),a("br")]):t._e(),t._v(" "),"deposit"===e.type?a("span",{staticClass:"text-success"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.amount))),a("br")]):t._e(),t._v(" "),"transfer"===e.type&&e.source_id===t.account_id?a("span",{staticClass:"text-info"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(-1*e.amount))),a("br")]):t._e(),t._v(" "),"transfer"===e.type&&e.destination_id===t.account_id?a("span",{staticClass:"text-info"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.amount))),a("br")]):t._e()])})),0)])})),0)])}),[],!1,null,"c9de6006",null).exports,Ct={name:"TransactionListSmall",data:function(){return{locale:"en-US"}},created:function(){var t;this.locale=null!==(t=localStorage.locale)&&void 0!==t?t:"en-US"},methods:{},props:{transactions:{type:Array,default:function(){return[]}},account_id:{type:Number,default:function(){return 0}}}},jt=Object(r.a)(Ct,(function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("table",{staticClass:"table table-striped table-sm"},[a("caption",{staticStyle:{display:"none"}},[t._v(t._s(t.$t("firefly.transaction_table_description")))]),t._v(" "),a("thead",[a("tr",[a("th",{staticClass:"text-left",attrs:{scope:"col"}},[t._v(t._s(t.$t("firefly.description")))]),t._v(" "),a("th",{staticClass:"text-right",attrs:{scope:"col"}},[t._v(t._s(t.$t("firefly.amount")))])])]),t._v(" "),a("tbody",t._l(this.transactions,(function(e){return a("tr",[a("td",[a("a",{attrs:{href:"transactions/show/"+e.id,title:new Intl.DateTimeFormat(t.locale,{year:"numeric",month:"long",day:"numeric"}).format(new Date(e.attributes.transactions[0].date))}},[e.attributes.transactions.length>1?a("span",[t._v(t._s(e.attributes.group_title))]):t._e(),t._v(" "),1===e.attributes.transactions.length?a("span",[t._v(t._s(e.attributes.transactions[0].description))]):t._e()])]),t._v(" "),a("td",{staticStyle:{"text-align":"right"}},t._l(e.attributes.transactions,(function(e){return a("span",["withdrawal"===e.type?a("span",{staticClass:"text-danger"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(-1*e.amount))),a("br")]):t._e(),t._v(" "),"deposit"===e.type?a("span",{staticClass:"text-success"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.amount))),a("br")]):t._e(),t._v(" "),"transfer"===e.type&&e.source_id===t.account_id?a("span",{staticClass:"text-info"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(-1*e.amount))),a("br")]):t._e(),t._v(" "),"transfer"===e.type&&e.destination_id===t.account_id?a("span",{staticClass:"text-info"},[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.amount))),a("br")]):t._e()])})),0)])})),0)])}),[],!1,null,"aa431dd2",null).exports,wt=a(237),Ot=a.n(wt);function xt(t,e){var a=Object.keys(t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(t);e&&(s=s.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),a.push.apply(a,s)}return a}function St(t){for(var e=1;ethis.earned?parseFloat(u.sum):this.earned}}this.sortCategories()},sortCategories:function(){var t=[];for(var e in this.categories)this.categories.hasOwnProperty(e)&&t.push(this.categories[e]);for(var a in t.sort((function(t,e){return t.spent+t.earned-(e.spent+e.earned)})),t)if(t.hasOwnProperty(a)){var s=t[a];s.spentPct=s.spent/this.spent*100,s.earnedPct=s.earned/this.earned*100,this.sortedList.push(s)}}}}),Ut=Object(r.a)(Tt,(function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"card"},[a("div",{staticClass:"card-header"},[a("h3",{staticClass:"card-title"},[t._v(t._s(t.$t("firefly.categories")))])]),t._v(" "),t.loading&&!t.error?a("div",{staticClass:"card-body"},[t._m(0)]):t._e(),t._v(" "),t.error?a("div",{staticClass:"card-body"},[t._m(1)]):t._e(),t._v(" "),t.loading||t.error?t._e():a("div",{staticClass:"card-body table-responsive p-0"},[a("table",{staticClass:"table table-sm"},[a("tbody",t._l(t.sortedList,(function(e){return a("tr",[a("td",{staticStyle:{width:"20%"}},[a("a",{attrs:{href:"./categories/show/"+e.id}},[t._v(t._s(e.name))])]),t._v(" "),a("td",{staticClass:"align-middle"},[e.spentPct>0?a("div",{staticClass:"progress"},[a("div",{staticClass:"progress-bar progress-bar-striped bg-danger",style:{width:e.spentPct+"%"},attrs:{"aria-valuenow":e.spentPct,"aria-valuemax":"100","aria-valuemin":"0",role:"progressbar"}},[e.spentPct>20?a("span",[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.spent))+"\n ")]):t._e()]),t._v(" "),e.spentPct<=20?a("span",[t._v(" \n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.spent))+"\n ")]):t._e()]):t._e(),t._v(" "),e.earnedPct>0?a("div",{staticClass:"progress justify-content-end",attrs:{title:"hello2"}},[e.earnedPct<=20?a("span",[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.earned))+"\n  ")]):t._e(),t._v(" "),a("div",{staticClass:"progress-bar progress-bar-striped bg-success",style:{width:e.earnedPct+"%"},attrs:{"aria-valuenow":e.earnedPct,"aria-valuemax":"100","aria-valuemin":"0",role:"progressbar",title:"hello"}},[e.earnedPct>20?a("span",[t._v("\n "+t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(e.earned))+"\n ")]):t._e()])]):t._e()])])})),0)])])])}),[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"text-center"},[e("i",{staticClass:"fas fa-spinner fa-spin"})])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"text-center"},[e("i",{staticClass:"fas fa-exclamation-triangle text-danger"})])}],!1,null,"2a51418c",null).exports,Rt=a(2),At=a.n(Rt),zt=a(17);a(16),a(55),At.a.component("transaction-list-large",vt),At.a.component("transaction-list-medium",yt),At.a.component("transaction-list-small",jt),At.a.component("date-picker",Ot.a),At.a.component("dashboard",n),At.a.component("top-boxes",f),At.a.component("main-account",L),At.a.component("main-account-list",B),At.a.component("main-bills-list",M),At.a.component("main-budget-list",tt),At.a.component("main-category-list",Ut),At.a.component("main-debit-list",_t),At.a.component("main-credit-list",ct),At.a.component("main-piggy-list",ht),At.a.use(i.c);var Mt=a(19),Gt={};new At.a({i18n:Mt,store:zt.a,el:"#dashboard",render:function(t){return t(n,{props:Gt})},beforeCreate:function(){this.$store.commit("initialiseStore"),this.$store.dispatch("updateCurrencyPreference"),this.$store.dispatch("dashboard/index/initialiseStore")}}),new At.a({i18n:Mt,store:zt.a,el:"#calendar",render:function(t){return t(It,{props:Gt})}})},468:function(t,e){}},[[245,0,1]]]); //# sourceMappingURL=dashboard.js.map \ No newline at end of file diff --git a/public/v2/js/dashboard.js.map b/public/v2/js/dashboard.js.map index dcd3a7a396..5ae0a84eaf 100755 --- a/public/v2/js/dashboard.js.map +++ b/public/v2/js/dashboard.js.map @@ -1 +1 @@ -{"version":3,"sources":["webpack:///./src/components/dashboard/Calendar.vue?0ca4","webpack:///./src/components/dashboard/Calendar.vue?7459","webpack:///./node_modules/moment/locale sync ^\\.\\/.*$","webpack:///./src/components/dashboard/Dashboard.vue?2e08","webpack:///./src/components/dashboard/Dashboard.vue?e9df","webpack:///src/components/dashboard/Dashboard.vue","webpack:///./src/components/dashboard/Dashboard.vue","webpack:///./src/components/dashboard/TopBoxes.vue?c977","webpack:///src/components/dashboard/TopBoxes.vue","webpack:///./src/components/dashboard/TopBoxes.vue","webpack:///./src/components/dashboard/TopBoxes.vue?9c54","webpack:///./src/components/charts/DataConverter.vue?d682","webpack:///src/components/charts/DataConverter.vue","webpack:///./src/components/charts/DataConverter.vue","webpack:///./src/components/charts/DefaultLineOptions.vue?36d0","webpack:///src/components/charts/DefaultLineOptions.vue","webpack:///./src/components/charts/DefaultLineOptions.vue","webpack:///./src/components/charts/DefaultLineOptions.vue?e894","webpack:///./src/components/dashboard/MainAccount.vue?1e69","webpack:///src/components/dashboard/MainAccount.vue","webpack:///./src/components/dashboard/MainAccount.vue","webpack:///./src/components/dashboard/MainAccount.vue?7d6d","webpack:///./src/components/dashboard/MainAccountList.vue?889b","webpack:///src/components/dashboard/MainAccountList.vue","webpack:///./src/components/dashboard/MainAccountList.vue","webpack:///./src/components/dashboard/MainAccountList.vue?ac92","webpack:///./src/components/dashboard/MainBillsList.vue?536e","webpack:///src/components/dashboard/MainBillsList.vue","webpack:///./src/components/dashboard/MainBillsList.vue","webpack:///./src/components/dashboard/MainBillsList.vue?8acb","webpack:///./src/components/dashboard/BudgetLimitRow.vue?c9a7","webpack:///src/components/dashboard/BudgetLimitRow.vue","webpack:///./src/components/dashboard/BudgetLimitRow.vue","webpack:///./src/components/dashboard/BudgetLimitRow.vue?d193","webpack:///./src/components/dashboard/BudgetRow.vue?09f9","webpack:///src/components/dashboard/BudgetRow.vue","webpack:///./src/components/dashboard/BudgetListGroup.vue?7954","webpack:///src/components/dashboard/BudgetListGroup.vue","webpack:///./src/components/dashboard/BudgetRow.vue","webpack:///./src/components/dashboard/BudgetRow.vue?1b73","webpack:///./src/components/dashboard/MainBudgetList.vue?9843","webpack:///src/components/dashboard/MainBudgetList.vue","webpack:///./src/components/dashboard/BudgetListGroup.vue","webpack:///./src/components/dashboard/BudgetListGroup.vue?9cbb","webpack:///./src/components/dashboard/MainBudgetList.vue","webpack:///./src/components/dashboard/MainBudgetList.vue?4433","webpack:///./src/components/dashboard/MainCreditList.vue?53af","webpack:///src/components/dashboard/MainCreditList.vue","webpack:///./src/components/dashboard/MainCreditList.vue","webpack:///./src/components/dashboard/MainCreditList.vue?52ba","webpack:///./src/components/dashboard/MainDebitList.vue?587b","webpack:///src/components/dashboard/MainDebitList.vue","webpack:///./src/components/dashboard/MainDebitList.vue","webpack:///./src/components/dashboard/MainDebitList.vue?a917","webpack:///./src/components/dashboard/MainPiggyList.vue?7bae","webpack:///src/components/dashboard/MainPiggyList.vue","webpack:///./src/components/dashboard/MainPiggyList.vue","webpack:///./src/components/dashboard/MainPiggyList.vue?b9e0","webpack:///./src/components/transactions/TransactionListLarge.vue?3500","webpack:///src/components/transactions/TransactionListLarge.vue","webpack:///./src/components/transactions/TransactionListLarge.vue","webpack:///./src/components/transactions/TransactionListLarge.vue?350e","webpack:///./src/components/transactions/TransactionListMedium.vue?d4b8","webpack:///src/components/transactions/TransactionListMedium.vue","webpack:///./src/components/transactions/TransactionListMedium.vue","webpack:///./src/components/transactions/TransactionListMedium.vue?0bb3","webpack:///./src/components/transactions/TransactionListSmall.vue?5a3e","webpack:///src/components/transactions/TransactionListSmall.vue","webpack:///./src/components/transactions/TransactionListSmall.vue","webpack:///./src/components/transactions/TransactionListSmall.vue?6a70","webpack:///./src/components/dashboard/Calendar.vue?b9d0","webpack:///src/components/dashboard/Calendar.vue","webpack:///./src/components/dashboard/Calendar.vue","webpack:///./src/components/dashboard/Calendar.vue?7cba","webpack:///./src/components/dashboard/MainCategoryList.vue?9f14","webpack:///src/components/dashboard/MainCategoryList.vue","webpack:///./src/components/dashboard/MainCategoryList.vue","webpack:///./src/components/dashboard/MainCategoryList.vue?dc06","webpack:///./src/pages/dashboard.js","webpack:///./src/components/dashboard/Calendar.vue?3f8b"],"names":["module","exports","push","i","map","webpackContext","req","id","webpackContextResolve","__webpack_require__","o","e","Error","code","keys","Object","resolve","name","created","computed","methods","_vm","this","_h","$createElement","_c","_self","_v","staticClass","props","data","summary","balances","billsPaid","billsUnpaid","leftToSpend","netWorth","prefCurrencyBalances","filterOnCurrency","notPrefCurrencyBalances","filterOnNotCurrency","prefBillsUnpaid","notPrefBillsUnpaid","prefLeftToSpend","notPrefLeftToSpend","prefNetWorth","notPrefNetWorth","currencyCode","$store","getters","currencyId","prepareComponent","array","hasOwnProperty","key","currency_id","ret","length","axios","get","window","sessionStart","sessionEnd","buildComponent","getBalanceEntries","getBillsEntries","getLeftToSpend","getNetWorth","hasCurrency","getKeyedEntries","expected","substr","result","_m","_s","$t","_l","balance","attrs","sub_title","value_parsed","index","_e","bill","left","nw","staticStyle","dataSet","newDataSet","locale","localStorage","local","convertChart","count","labels","datasets","getLabels","getDataSets","colorizeBarData","fillColors","colourSet","setKey","dataset","fill","backgroundColor","borderColor","colorizeLineData","convertLabelsToDate","labelKey","Intl","DateTimeFormat","format","unixTimeZero","firstSet","entries","entryLabel","oldSet","newSet","label","type","currency_symbol","currency_code","yAxisID","formatLabel","sections","words","str","String","split","temp","forEach","item","concat","maxwidth","getDefaultOptions","self","legend","display","animation","duration","responsive","maintainAspectRatio","elements","line","cubicInterpolationMode","scales","xAxes","yAxes","ticks","callback","NumberFormat","beginAtZero","tooltips","mode","callbacks","tooltipItem","datasetIndex","nrString","accounts","account","class","uri","title","transactions","components","loadBills","test","attributes","pay_dates","active","bills","style","currency","parseFloat","amount_min","amount_max","payDate","year","month","day","Date","mounted","budgetLimit","default","budget","budget_id","budget_name","pctGreen","spent","amount","pctOrange","pctRed","start","end","budgetLimits","Array","budgets","budgetList","daily","weekly","monthly","quarterly","half_year","yearly","other","rawBudgets","collectData","income","max","getExpenses","parseExpenses","mainKey","parseInt","difference_float","current","pct","entry","width","expenses","loadPiggyBanks","piggy","left_to_save","piggy_banks","sort","a","b","object_group_title","current_amount","target_amount","transaction","date","group_title","description","tr","destination_id","destination_name","source_id","source_name","account_id","category_id","category_name","Number","range","defaultRange","scopedSlots","_u","fn","ref","inputValue","inputEvents","isDragging","togglePopover","on","$event","placement","positionFixed","_g","domProps","model","value","$$v","expression","getCategories","categories","sortedList","earned","category","spentPct","earnedPct","require","Vue","component","TransactionListLarge","TransactionListMedium","TransactionListSmall","DatePicker","Dashboard","TopBoxes","MainAccount","MainAccountList","MainBillsList","MainBudgetList","MainCategoryList","MainDebitList","MainCreditList","MainPiggyList","use","Vuex","i18n","store","el","render","createElement","beforeCreate","commit","dispatch","Calendar","content","options","transform","undefined","locals"],"mappings":"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","file":"/public/js/dashboard.js","sourcesContent":["export * from \"-!../../../node_modules/style-loader/index.js!../../../node_modules/css-loader/index.js??ref--6-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src/index.js??ref--6-2!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Calendar.vue?vue&type=style&index=0&id=03e62f58&scoped=true&lang=css&\"","exports = module.exports = require(\"../../../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \".dropdown-item[data-v-03e62f58],.dropdown-item[data-v-03e62f58]:hover{color:#212529}\", \"\"]);\n\n// exports\n","var map = {\n\t\"./af\": 61,\n\t\"./af.js\": 61,\n\t\"./ar\": 62,\n\t\"./ar-dz\": 63,\n\t\"./ar-dz.js\": 63,\n\t\"./ar-kw\": 64,\n\t\"./ar-kw.js\": 64,\n\t\"./ar-ly\": 65,\n\t\"./ar-ly.js\": 65,\n\t\"./ar-ma\": 66,\n\t\"./ar-ma.js\": 66,\n\t\"./ar-sa\": 67,\n\t\"./ar-sa.js\": 67,\n\t\"./ar-tn\": 68,\n\t\"./ar-tn.js\": 68,\n\t\"./ar.js\": 62,\n\t\"./az\": 69,\n\t\"./az.js\": 69,\n\t\"./be\": 70,\n\t\"./be.js\": 70,\n\t\"./bg\": 71,\n\t\"./bg.js\": 71,\n\t\"./bm\": 72,\n\t\"./bm.js\": 72,\n\t\"./bn\": 73,\n\t\"./bn-bd\": 74,\n\t\"./bn-bd.js\": 74,\n\t\"./bn.js\": 73,\n\t\"./bo\": 75,\n\t\"./bo.js\": 75,\n\t\"./br\": 76,\n\t\"./br.js\": 76,\n\t\"./bs\": 77,\n\t\"./bs.js\": 77,\n\t\"./ca\": 78,\n\t\"./ca.js\": 78,\n\t\"./cs\": 79,\n\t\"./cs.js\": 79,\n\t\"./cv\": 80,\n\t\"./cv.js\": 80,\n\t\"./cy\": 81,\n\t\"./cy.js\": 81,\n\t\"./da\": 82,\n\t\"./da.js\": 82,\n\t\"./de\": 83,\n\t\"./de-at\": 84,\n\t\"./de-at.js\": 84,\n\t\"./de-ch\": 85,\n\t\"./de-ch.js\": 85,\n\t\"./de.js\": 83,\n\t\"./dv\": 86,\n\t\"./dv.js\": 86,\n\t\"./el\": 87,\n\t\"./el.js\": 87,\n\t\"./en-au\": 88,\n\t\"./en-au.js\": 88,\n\t\"./en-ca\": 89,\n\t\"./en-ca.js\": 89,\n\t\"./en-gb\": 90,\n\t\"./en-gb.js\": 90,\n\t\"./en-ie\": 91,\n\t\"./en-ie.js\": 91,\n\t\"./en-il\": 92,\n\t\"./en-il.js\": 92,\n\t\"./en-in\": 93,\n\t\"./en-in.js\": 93,\n\t\"./en-nz\": 94,\n\t\"./en-nz.js\": 94,\n\t\"./en-sg\": 95,\n\t\"./en-sg.js\": 95,\n\t\"./eo\": 96,\n\t\"./eo.js\": 96,\n\t\"./es\": 97,\n\t\"./es-do\": 98,\n\t\"./es-do.js\": 98,\n\t\"./es-mx\": 99,\n\t\"./es-mx.js\": 99,\n\t\"./es-us\": 100,\n\t\"./es-us.js\": 100,\n\t\"./es.js\": 97,\n\t\"./et\": 101,\n\t\"./et.js\": 101,\n\t\"./eu\": 102,\n\t\"./eu.js\": 102,\n\t\"./fa\": 103,\n\t\"./fa.js\": 103,\n\t\"./fi\": 104,\n\t\"./fi.js\": 104,\n\t\"./fil\": 105,\n\t\"./fil.js\": 105,\n\t\"./fo\": 106,\n\t\"./fo.js\": 106,\n\t\"./fr\": 107,\n\t\"./fr-ca\": 108,\n\t\"./fr-ca.js\": 108,\n\t\"./fr-ch\": 109,\n\t\"./fr-ch.js\": 109,\n\t\"./fr.js\": 107,\n\t\"./fy\": 110,\n\t\"./fy.js\": 110,\n\t\"./ga\": 111,\n\t\"./ga.js\": 111,\n\t\"./gd\": 112,\n\t\"./gd.js\": 112,\n\t\"./gl\": 113,\n\t\"./gl.js\": 113,\n\t\"./gom-deva\": 114,\n\t\"./gom-deva.js\": 114,\n\t\"./gom-latn\": 115,\n\t\"./gom-latn.js\": 115,\n\t\"./gu\": 116,\n\t\"./gu.js\": 116,\n\t\"./he\": 117,\n\t\"./he.js\": 117,\n\t\"./hi\": 118,\n\t\"./hi.js\": 118,\n\t\"./hr\": 119,\n\t\"./hr.js\": 119,\n\t\"./hu\": 120,\n\t\"./hu.js\": 120,\n\t\"./hy-am\": 121,\n\t\"./hy-am.js\": 121,\n\t\"./id\": 122,\n\t\"./id.js\": 122,\n\t\"./is\": 123,\n\t\"./is.js\": 123,\n\t\"./it\": 124,\n\t\"./it-ch\": 125,\n\t\"./it-ch.js\": 125,\n\t\"./it.js\": 124,\n\t\"./ja\": 126,\n\t\"./ja.js\": 126,\n\t\"./jv\": 127,\n\t\"./jv.js\": 127,\n\t\"./ka\": 128,\n\t\"./ka.js\": 128,\n\t\"./kk\": 129,\n\t\"./kk.js\": 129,\n\t\"./km\": 130,\n\t\"./km.js\": 130,\n\t\"./kn\": 131,\n\t\"./kn.js\": 131,\n\t\"./ko\": 132,\n\t\"./ko.js\": 132,\n\t\"./ku\": 133,\n\t\"./ku.js\": 133,\n\t\"./ky\": 134,\n\t\"./ky.js\": 134,\n\t\"./lb\": 135,\n\t\"./lb.js\": 135,\n\t\"./lo\": 136,\n\t\"./lo.js\": 136,\n\t\"./lt\": 137,\n\t\"./lt.js\": 137,\n\t\"./lv\": 138,\n\t\"./lv.js\": 138,\n\t\"./me\": 139,\n\t\"./me.js\": 139,\n\t\"./mi\": 140,\n\t\"./mi.js\": 140,\n\t\"./mk\": 141,\n\t\"./mk.js\": 141,\n\t\"./ml\": 142,\n\t\"./ml.js\": 142,\n\t\"./mn\": 143,\n\t\"./mn.js\": 143,\n\t\"./mr\": 144,\n\t\"./mr.js\": 144,\n\t\"./ms\": 145,\n\t\"./ms-my\": 146,\n\t\"./ms-my.js\": 146,\n\t\"./ms.js\": 145,\n\t\"./mt\": 147,\n\t\"./mt.js\": 147,\n\t\"./my\": 148,\n\t\"./my.js\": 148,\n\t\"./nb\": 149,\n\t\"./nb.js\": 149,\n\t\"./ne\": 150,\n\t\"./ne.js\": 150,\n\t\"./nl\": 151,\n\t\"./nl-be\": 152,\n\t\"./nl-be.js\": 152,\n\t\"./nl.js\": 151,\n\t\"./nn\": 153,\n\t\"./nn.js\": 153,\n\t\"./oc-lnc\": 154,\n\t\"./oc-lnc.js\": 154,\n\t\"./pa-in\": 155,\n\t\"./pa-in.js\": 155,\n\t\"./pl\": 156,\n\t\"./pl.js\": 156,\n\t\"./pt\": 157,\n\t\"./pt-br\": 158,\n\t\"./pt-br.js\": 158,\n\t\"./pt.js\": 157,\n\t\"./ro\": 159,\n\t\"./ro.js\": 159,\n\t\"./ru\": 160,\n\t\"./ru.js\": 160,\n\t\"./sd\": 161,\n\t\"./sd.js\": 161,\n\t\"./se\": 162,\n\t\"./se.js\": 162,\n\t\"./si\": 163,\n\t\"./si.js\": 163,\n\t\"./sk\": 164,\n\t\"./sk.js\": 164,\n\t\"./sl\": 165,\n\t\"./sl.js\": 165,\n\t\"./sq\": 166,\n\t\"./sq.js\": 166,\n\t\"./sr\": 167,\n\t\"./sr-cyrl\": 168,\n\t\"./sr-cyrl.js\": 168,\n\t\"./sr.js\": 167,\n\t\"./ss\": 169,\n\t\"./ss.js\": 169,\n\t\"./sv\": 170,\n\t\"./sv.js\": 170,\n\t\"./sw\": 171,\n\t\"./sw.js\": 171,\n\t\"./ta\": 172,\n\t\"./ta.js\": 172,\n\t\"./te\": 173,\n\t\"./te.js\": 173,\n\t\"./tet\": 174,\n\t\"./tet.js\": 174,\n\t\"./tg\": 175,\n\t\"./tg.js\": 175,\n\t\"./th\": 176,\n\t\"./th.js\": 176,\n\t\"./tk\": 177,\n\t\"./tk.js\": 177,\n\t\"./tl-ph\": 178,\n\t\"./tl-ph.js\": 178,\n\t\"./tlh\": 179,\n\t\"./tlh.js\": 179,\n\t\"./tr\": 180,\n\t\"./tr.js\": 180,\n\t\"./tzl\": 181,\n\t\"./tzl.js\": 181,\n\t\"./tzm\": 182,\n\t\"./tzm-latn\": 183,\n\t\"./tzm-latn.js\": 183,\n\t\"./tzm.js\": 182,\n\t\"./ug-cn\": 184,\n\t\"./ug-cn.js\": 184,\n\t\"./uk\": 185,\n\t\"./uk.js\": 185,\n\t\"./ur\": 186,\n\t\"./ur.js\": 186,\n\t\"./uz\": 187,\n\t\"./uz-latn\": 188,\n\t\"./uz-latn.js\": 188,\n\t\"./uz.js\": 187,\n\t\"./vi\": 189,\n\t\"./vi.js\": 189,\n\t\"./x-pseudo\": 190,\n\t\"./x-pseudo.js\": 190,\n\t\"./yo\": 191,\n\t\"./yo.js\": 191,\n\t\"./zh-cn\": 192,\n\t\"./zh-cn.js\": 192,\n\t\"./zh-hk\": 193,\n\t\"./zh-hk.js\": 193,\n\t\"./zh-mo\": 194,\n\t\"./zh-mo.js\": 194,\n\t\"./zh-tw\": 195,\n\t\"./zh-tw.js\": 195\n};\n\n\nfunction webpackContext(req) {\n\tvar id = webpackContextResolve(req);\n\treturn __webpack_require__(id);\n}\nfunction webpackContextResolve(req) {\n\tif(!__webpack_require__.o(map, req)) {\n\t\tvar e = new Error(\"Cannot find module '\" + req + \"'\");\n\t\te.code = 'MODULE_NOT_FOUND';\n\t\tthrow e;\n\t}\n\treturn map[req];\n}\nwebpackContext.keys = function webpackContextKeys() {\n\treturn Object.keys(map);\n};\nwebpackContext.resolve = webpackContextResolve;\nmodule.exports = webpackContext;\nwebpackContext.id = 267;","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('top-boxes'),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('main-account')],1)]),_vm._v(\" \"),_c('main-account-list'),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('main-budget-list')],1)]),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('main-category-list')],1)]),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col-lg-6 col-md-12 col-sm-12 col-xs-12\"},[_c('main-debit-list')],1),_vm._v(\" \"),_c('div',{staticClass:\"col-lg-6 col-md-12 col-sm-12 col-xs-12\"},[_c('main-credit-list')],1)]),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col-lg-6 col-md-12 col-sm-12 col-xs-12\"},[_c('main-piggy-list')],1),_vm._v(\" \"),_c('div',{staticClass:\"col-lg-6 col-md-12 col-sm-12 col-xs-12\"},[_c('main-bills-list')],1)])],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Dashboard.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Dashboard.vue?vue&type=script&lang=js&\"","\n\n\n\n\n","import { render, staticRenderFns } from \"./Dashboard.vue?vue&type=template&id=6fea41ee&\"\nimport script from \"./Dashboard.vue?vue&type=script&lang=js&\"\nexport * from \"./Dashboard.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TopBoxes.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TopBoxes.vue?vue&type=script&lang=js&\"","\n\n\n\n\n","import { render, staticRenderFns } from \"./TopBoxes.vue?vue&type=template&id=b9980d70&\"\nimport script from \"./TopBoxes.vue?vue&type=script&lang=js&\"\nexport * from \"./TopBoxes.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('div',{staticClass:\"info-box\"},[_vm._m(0),_vm._v(\" \"),_c('div',{staticClass:\"info-box-content\"},[_c('span',{staticClass:\"info-box-text\"},[_vm._v(_vm._s(_vm.$t(\"firefly.balance\")))]),_vm._v(\" \"),_vm._l((_vm.prefCurrencyBalances),function(balance){return _c('span',{staticClass:\"info-box-number\",attrs:{\"title\":balance.sub_title}},[_vm._v(_vm._s(balance.value_parsed))])}),_vm._v(\" \"),_vm._m(1),_vm._v(\" \"),_c('span',{staticClass:\"progress-description\"},[_vm._l((_vm.notPrefCurrencyBalances),function(balance,index){return _c('span',{attrs:{\"title\":balance.sub_title}},[_vm._v(\"\\n \"+_vm._s(balance.value_parsed)),(index+1 !== _vm.notPrefCurrencyBalances.length)?_c('span',[_vm._v(\", \")]):_vm._e()])}),_vm._v(\" \"),(0===_vm.notPrefCurrencyBalances.length)?_c('span',[_vm._v(\" \")]):_vm._e()],2)],2)])]),_vm._v(\" \"),_c('div',{staticClass:\"col\"},[_c('div',{staticClass:\"info-box\"},[_vm._m(2),_vm._v(\" \"),_c('div',{staticClass:\"info-box-content\"},[_c('span',{staticClass:\"info-box-text\"},[_vm._v(_vm._s(_vm.$t('firefly.bills_to_pay')))]),_vm._v(\" \"),_vm._l((_vm.prefBillsUnpaid),function(balance){return _c('span',{staticClass:\"info-box-number\"},[_vm._v(_vm._s(balance.value_parsed))])}),_vm._v(\" \"),_vm._m(3),_vm._v(\" \"),_c('span',{staticClass:\"progress-description\"},[_vm._l((_vm.notPrefBillsUnpaid),function(bill,index){return _c('span',[_vm._v(\"\\n \"+_vm._s(bill.value_parsed)),(index+1 !== _vm.notPrefBillsUnpaid.length)?_c('span',[_vm._v(\", \")]):_vm._e()])}),_vm._v(\" \"),(0===_vm.notPrefBillsUnpaid.length)?_c('span',[_vm._v(\" \")]):_vm._e()],2)],2)])]),_vm._v(\" \"),_c('div',{staticClass:\"col\"},[_c('div',{staticClass:\"info-box\"},[_vm._m(4),_vm._v(\" \"),_c('div',{staticClass:\"info-box-content\"},[_c('span',{staticClass:\"info-box-text\"},[_vm._v(_vm._s(_vm.$t('firefly.left_to_spend')))]),_vm._v(\" \"),_vm._l((_vm.prefLeftToSpend),function(left){return _c('span',{staticClass:\"info-box-number\",attrs:{\"title\":left.sub_title}},[_vm._v(_vm._s(left.value_parsed))])}),_vm._v(\" \"),_vm._m(5),_vm._v(\" \"),_c('span',{staticClass:\"progress-description\"},[_vm._l((_vm.notPrefLeftToSpend),function(left,index){return _c('span',[_vm._v(\"\\n \"+_vm._s(left.value_parsed)),(index+1 !== _vm.notPrefLeftToSpend.length)?_c('span',[_vm._v(\", \")]):_vm._e()])}),_vm._v(\" \"),(0===_vm.notPrefLeftToSpend.length)?_c('span',[_vm._v(\" \")]):_vm._e()],2)],2)])]),_vm._v(\" \"),_c('div',{staticClass:\"col\"},[_c('div',{staticClass:\"info-box\"},[_vm._m(6),_vm._v(\" \"),_c('div',{staticClass:\"info-box-content\"},[_c('span',{staticClass:\"info-box-text\"},[_c('span',[_vm._v(_vm._s(_vm.$t('firefly.net_worth')))])]),_vm._v(\" \"),_vm._l((_vm.prefNetWorth),function(nw){return _c('span',{staticClass:\"info-box-number\",attrs:{\"title\":nw.sub_title}},[_vm._v(_vm._s(nw.value_parsed))])}),_vm._v(\" \"),_vm._m(7),_vm._v(\" \"),_c('span',{staticClass:\"progress-description\"},[_vm._l((_vm.notPrefNetWorth),function(nw,index){return _c('span',[_vm._v(\"\\n \"+_vm._s(nw.value_parsed)),(index+1 !== _vm.notPrefNetWorth.length)?_c('span',[_vm._v(\", \")]):_vm._e()])}),_vm._v(\" \"),(0===_vm.notPrefNetWorth.length)?_c('span',[_vm._v(\" \")]):_vm._e()],2)],2)])])])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('span',{staticClass:\"info-box-icon\"},[_c('i',{staticClass:\"far fa-bookmark text-info\"})])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"progress bg-info\"},[_c('div',{staticClass:\"progress-bar\",staticStyle:{\"width\":\"0\"}})])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('span',{staticClass:\"info-box-icon\"},[_c('i',{staticClass:\"far fa-calendar-alt text-teal\"})])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"progress bg-teal\"},[_c('div',{staticClass:\"progress-bar\",staticStyle:{\"width\":\"0\"}})])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('span',{staticClass:\"info-box-icon\"},[_c('i',{staticClass:\"fas fa-money-bill text-success\"})])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"progress bg-success\"},[_c('div',{staticClass:\"progress-bar\",staticStyle:{\"width\":\"0\"}})])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('span',{staticClass:\"info-box-icon\"},[_c('i',{staticClass:\"fas fa-money-bill text-success\"})])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"progress bg-success\"},[_c('div',{staticClass:\"progress-bar\",staticStyle:{\"width\":\"0\"}})])}]\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./DataConverter.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./DataConverter.vue?vue&type=script&lang=js&\"","\n\n\n","var render, staticRenderFns\nimport script from \"./DataConverter.vue?vue&type=script&lang=js&\"\nexport * from \"./DataConverter.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./DefaultLineOptions.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./DefaultLineOptions.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n\n\n","import { render, staticRenderFns } from \"./DefaultLineOptions.vue?vue&type=template&id=55cd0635&scoped=true&\"\nimport script from \"./DefaultLineOptions.vue?vue&type=script&lang=js&\"\nexport * from \"./DefaultLineOptions.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"55cd0635\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c(\"div\")}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainAccount.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainAccount.vue?vue&type=script&lang=js&\"","\n\n\n\n\n","import { render, staticRenderFns } from \"./MainAccount.vue?vue&type=template&id=4debd515&\"\nimport script from \"./MainAccount.vue?vue&type=script&lang=js&\"\nexport * from \"./MainAccount.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-header\"},[_c('h3',{staticClass:\"card-title\"},[_vm._v(_vm._s(_vm.$t('firefly.yourAccounts')))])]),_vm._v(\" \"),_vm._m(0),_vm._v(\" \"),_c('div',{staticClass:\"card-footer\"},[_c('a',{staticClass:\"btn btn-default button-sm\",attrs:{\"href\":\"./accounts/asset\"}},[_c('i',{staticClass:\"far fa-money-bill-alt\"}),_vm._v(\" \"+_vm._s(_vm.$t('firefly.go_to_asset_accounts')))])])])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"card-body\"},[_c('div',[_c('canvas',{staticStyle:{\"min-height\":\"400px\",\"height\":\"400px\",\"max-height\":\"400px\",\"max-width\":\"100%\"},attrs:{\"id\":\"mainAccountsChart\"}})])])}]\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainAccountList.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainAccountList.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n\n","import { render, staticRenderFns } from \"./MainAccountList.vue?vue&type=template&id=2f3a38b8&scoped=true&\"\nimport script from \"./MainAccountList.vue?vue&type=script&lang=js&\"\nexport * from \"./MainAccountList.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"2f3a38b8\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"row\"},_vm._l((_vm.accounts),function(account){return _c('div',{class:{ 'col-lg-12': 1 === _vm.accounts.length, 'col-lg-6': 2 === _vm.accounts.length, 'col-lg-4': _vm.accounts.length > 2 }},[_c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-header\"},[_c('h3',{staticClass:\"card-title\"},[_c('a',{attrs:{\"href\":account.uri}},[_vm._v(_vm._s(account.title))])])]),_vm._v(\" \"),_c('div',{staticClass:\"card-body table-responsive p-0\"},[(1===_vm.accounts.length)?_c('transaction-list-large',{attrs:{\"transactions\":account.transactions,\"account_id\":account.id}}):_vm._e(),_vm._v(\" \"),(2===_vm.accounts.length)?_c('transaction-list-medium',{attrs:{\"transactions\":account.transactions,\"account_id\":account.id}}):_vm._e(),_vm._v(\" \"),(_vm.accounts.length > 2)?_c('transaction-list-small',{attrs:{\"transactions\":account.transactions,\"account_id\":account.id}}):_vm._e()],1)])])}),0)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainBillsList.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainBillsList.vue?vue&type=script&lang=js&\"","\n\n\n\n","import { render, staticRenderFns } from \"./MainBillsList.vue?vue&type=template&id=59904730&\"\nimport script from \"./MainBillsList.vue?vue&type=script&lang=js&\"\nexport * from \"./MainBillsList.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-header\"},[_c('h3',{staticClass:\"card-title\"},[_vm._v(_vm._s(_vm.$t('firefly.bills')))])]),_vm._v(\" \"),_c('div',{staticClass:\"card-body table-responsive p-0\"},[_c('table',{staticClass:\"table table-striped\"},[_c('caption',{staticStyle:{\"display\":\"none\"}},[_vm._v(_vm._s(_vm.$t('firefly.bills')))]),_vm._v(\" \"),_c('thead',[_c('tr',[_c('th',{staticStyle:{\"width\":\"35%\"},attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('list.name')))]),_vm._v(\" \"),_c('th',{staticStyle:{\"width\":\"25%\"},attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('list.next_expected_match')))])])]),_vm._v(\" \"),_c('tbody',_vm._l((this.bills),function(bill){return _c('tr',[_c('td',[_c('a',{attrs:{\"href\":'./bills/show' + bill.id,\"title\":bill.attributes.name}},[_vm._v(_vm._s(bill.attributes.name))]),_vm._v(\"\\n ~\"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: bill.attributes.currency_code}).format((parseFloat(bill.attributes.amount_min) +\n parseFloat(bill.attributes.amount_max)) / 2))+\"\\n \"),_c('br')]),_vm._v(\" \"),_c('td',_vm._l((bill.attributes.pay_dates),function(payDate){return _c('span',[_vm._v(\"\\n \"+_vm._s(new Intl.DateTimeFormat(_vm.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(new Date(payDate)))+\"\\n \"),_c('br')])}),0)])}),0)])]),_vm._v(\" \"),_c('div',{staticClass:\"card-footer\"},[_c('a',{staticClass:\"btn btn-default button-sm\",attrs:{\"href\":\"./bills\"}},[_c('i',{staticClass:\"far fa-money-bill-alt\"}),_vm._v(\" \"+_vm._s(_vm.$t('firefly.go_to_bills')))])])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./BudgetLimitRow.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./BudgetLimitRow.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n","import { render, staticRenderFns } from \"./BudgetLimitRow.vue?vue&type=template&id=20d55ede&scoped=true&\"\nimport script from \"./BudgetLimitRow.vue?vue&type=script&lang=js&\"\nexport * from \"./BudgetLimitRow.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"20d55ede\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('tr',[_c('td',{staticStyle:{\"width\":\"25%\"}},[_c('a',{attrs:{\"href\":'./budgets/show/' + _vm.budgetLimit.budget_id}},[_vm._v(_vm._s(_vm.budgetLimit.budget_name))])]),_vm._v(\" \"),_c('td',{staticStyle:{\"vertical-align\":\"middle\"}},[_c('div',{staticClass:\"progress progress active\"},[_c('div',{staticClass:\"progress-bar bg-success progress-bar-striped\",style:('width: '+ _vm.budgetLimit.pctGreen + '%;'),attrs:{\"role\":\"progressbar\",\"aria-valuenow\":_vm.budgetLimit.pctGreen,\"aria-valuemin\":\"0\",\"aria-valuemax\":\"100\"}},[(_vm.budgetLimit.pctGreen > 35)?_c('span',[_vm._v(\"\\n Spent\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: _vm.budgetLimit.currency_code}).format(_vm.budgetLimit.spent))+\"\\n of\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: _vm.budgetLimit.currency_code}).format(_vm.budgetLimit.amount))+\"\\n \")]):_vm._e()]),_vm._v(\" \"),_c('div',{staticClass:\"progress-bar bg-warning progress-bar-striped\",style:('width: '+ _vm.budgetLimit.pctOrange + '%;'),attrs:{\"role\":\"progressbar\",\"aria-valuenow\":_vm.budgetLimit.pctOrange,\"aria-valuemin\":\"0\",\"aria-valuemax\":\"100\"}},[(_vm.budgetLimit.pctRed <= 50 && _vm.budgetLimit.pctOrange > 35)?_c('span',[_vm._v(\"\\n Spent\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: _vm.budgetLimit.currency_code}).format(_vm.budgetLimit.spent))+\"\\n of\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: _vm.budgetLimit.currency_code}).format(_vm.budgetLimit.amount))+\"\\n \")]):_vm._e()]),_vm._v(\" \"),_c('div',{staticClass:\"progress-bar bg-danger progress-bar-striped\",style:('width: '+ _vm.budgetLimit.pctRed + '%;'),attrs:{\"role\":\"progressbar\",\"aria-valuenow\":_vm.budgetLimit.pctRed,\"aria-valuemin\":\"0\",\"aria-valuemax\":\"100\"}},[(_vm.budgetLimit.pctOrange <= 50 && _vm.budgetLimit.pctRed > 35)?_c('span',[_vm._v(\"\\n Spent\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: _vm.budgetLimit.currency_code}).format(_vm.budgetLimit.spent))+\"\\n of\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: _vm.budgetLimit.currency_code}).format(_vm.budgetLimit.amount))+\"\\n \")]):_vm._e()])]),_vm._v(\" \"),_c('small',{staticClass:\"d-none d-lg-block\"},[_vm._v(\"\\n \"+_vm._s(new Intl.DateTimeFormat(_vm.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(_vm.budgetLimit.start))+\"\\n →\\n \"+_vm._s(new Intl.DateTimeFormat(_vm.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(_vm.budgetLimit.end))+\"\\n \")])]),_vm._v(\" \"),_c('td',{staticClass:\"align-middle d-none d-lg-table-cell\",staticStyle:{\"width\":\"10%\"}},[(parseFloat(_vm.budgetLimit.amount) + parseFloat(_vm.budgetLimit.spent) > 0)?_c('span',{staticClass:\"text-success\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {\n style: 'currency',\n currency: _vm.budgetLimit.currency_code\n }).format(parseFloat(_vm.budgetLimit.amount) + parseFloat(_vm.budgetLimit.spent)))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(0.0 === parseFloat(_vm.budgetLimit.amount) + parseFloat(_vm.budgetLimit.spent))?_c('span',{staticClass:\"text-muted\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: _vm.budgetLimit.currency_code}).format(0))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(parseFloat(_vm.budgetLimit.amount) + parseFloat(_vm.budgetLimit.spent) < 0)?_c('span',{staticClass:\"text-danger\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {\n style: 'currency',\n currency: _vm.budgetLimit.currency_code\n }).format(parseFloat(_vm.budgetLimit.amount) + parseFloat(_vm.budgetLimit.spent)))+\"\\n \")]):_vm._e()])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./BudgetRow.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./BudgetRow.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./BudgetListGroup.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./BudgetListGroup.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n","import { render, staticRenderFns } from \"./BudgetRow.vue?vue&type=template&id=3e0b277e&scoped=true&\"\nimport script from \"./BudgetRow.vue?vue&type=script&lang=js&\"\nexport * from \"./BudgetRow.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"3e0b277e\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('tr',[_c('td',{staticStyle:{\"width\":\"25%\"}},[_c('a',{attrs:{\"href\":'./budgets/show/' + _vm.budget.id}},[_vm._v(_vm._s(_vm.budget.name))])]),_vm._v(\" \"),_c('td',{staticClass:\"align-middle text-right\"},[_c('span',{staticClass:\"text-danger\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: _vm.budget.currency_code}).format(parseFloat(_vm.budget.spent)))+\"\\n \")])])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainBudgetList.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainBudgetList.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n","import { render, staticRenderFns } from \"./BudgetListGroup.vue?vue&type=template&id=1480b15a&scoped=true&\"\nimport script from \"./BudgetListGroup.vue?vue&type=script&lang=js&\"\nexport * from \"./BudgetListGroup.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"1480b15a\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-header\"},[_c('h3',{staticClass:\"card-title\"},[_vm._v(_vm._s(_vm.title))])]),_vm._v(\" \"),_c('div',{staticClass:\"card-body table-responsive p-0\"},[_c('table',{staticClass:\"table table-sm\"},[_c('tbody',[_vm._l((_vm.budgetLimits),function(budgetLimit,key){return _c('BudgetLimitRow',{key:key,attrs:{\"budgetLimit\":budgetLimit}})}),_vm._v(\" \"),_vm._l((_vm.budgets),function(budget,key){return _c('BudgetRow',{key:key,attrs:{\"budget\":budget}})})],2)])]),_vm._v(\" \"),_c('div',{staticClass:\"card-footer\"},[_c('a',{staticClass:\"btn btn-default button-sm\",attrs:{\"href\":\"./budgets\"}},[_c('i',{staticClass:\"far fa-money-bill-alt\"}),_vm._v(\" \"+_vm._s(_vm.$t('firefly.go_to_budgets')))])])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import { render, staticRenderFns } from \"./MainBudgetList.vue?vue&type=template&id=f0d512f0&scoped=true&\"\nimport script from \"./MainBudgetList.vue?vue&type=script&lang=js&\"\nexport * from \"./MainBudgetList.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"f0d512f0\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',{staticClass:\"row\"},[(_vm.budgetLimits.daily.length > 0)?_c('div',{staticClass:\"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12\"},[_c('BudgetListGroup',{attrs:{\"title\":_vm.$t('firefly.daily_budgets'),\"budgetLimits\":_vm.budgetLimits.daily}})],1):_vm._e(),_vm._v(\" \"),(_vm.budgetLimits.weekly.length > 0)?_c('div',{staticClass:\"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12\"},[_c('BudgetListGroup',{attrs:{\"title\":_vm.$t('firefly.weekly_budgets'),\"budgetLimits\":_vm.budgetLimits.weekly}})],1):_vm._e(),_vm._v(\" \"),(_vm.budgetLimits.monthly.length > 0)?_c('div',{staticClass:\"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12\"},[_c('BudgetListGroup',{attrs:{\"title\":_vm.$t('firefly.monthly_budgets'),\"budgetLimits\":_vm.budgetLimits.monthly}})],1):_vm._e(),_vm._v(\" \"),(_vm.budgetLimits.quarterly.length > 0)?_c('div',{staticClass:\"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12\"},[_c('BudgetListGroup',{attrs:{\"title\":_vm.$t('firefly.quarterly_budgets'),\"budgetLimits\":_vm.budgetLimits.quarterly}})],1):_vm._e(),_vm._v(\" \"),(_vm.budgetLimits.half_year.length > 0)?_c('div',{staticClass:\"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12\"},[_c('BudgetListGroup',{attrs:{\"title\":_vm.$t('firefly.half_year_budgets'),\"budgetLimits\":_vm.budgetLimits.half_year}})],1):_vm._e(),_vm._v(\" \"),(_vm.budgetLimits.yearly.length > 0)?_c('div',{staticClass:\"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12\"},[_c('BudgetListGroup',{attrs:{\"title\":_vm.$t('firefly.yearly_budgets'),\"budgetLimits\":_vm.budgetLimits.yearly}})],1):_vm._e(),_vm._v(\" \"),(_vm.budgetLimits.other.length > 0 || _vm.rawBudgets.length > 0)?_c('div',{staticClass:\"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12\"},[_c('BudgetListGroup',{attrs:{\"title\":_vm.$t('firefly.other_budgets'),\"budgetLimits\":_vm.budgetLimits.other,\"budgets\":_vm.rawBudgets}})],1):_vm._e()])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainCreditList.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainCreditList.vue?vue&type=script&lang=js&\"","\n\n\n\n\n","import { render, staticRenderFns } from \"./MainCreditList.vue?vue&type=template&id=bf82cc48&\"\nimport script from \"./MainCreditList.vue?vue&type=script&lang=js&\"\nexport * from \"./MainCreditList.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-header\"},[_c('h3',{staticClass:\"card-title\"},[_vm._v(_vm._s(_vm.$t('firefly.revenue_accounts')))])]),_vm._v(\" \"),_c('div',{staticClass:\"card-body table-responsive p-0\"},[_c('table',{staticClass:\"table table-sm\"},[_c('tbody',_vm._l((_vm.income),function(entry){return _c('tr',[_c('td',{staticStyle:{\"width\":\"20%\"}},[_c('a',{attrs:{\"href\":'./accounts/show/' + entry.id}},[_vm._v(_vm._s(entry.name))])]),_vm._v(\" \"),_c('td',{staticClass:\"align-middle\"},[(entry.pct > 0)?_c('div',{staticClass:\"progress\"},[_c('div',{staticClass:\"progress-bar progress-bar-striped bg-success\",style:({ width: entry.pct + '%'}),attrs:{\"role\":\"progressbar\",\"aria-valuenow\":entry.pct,\"aria-valuemin\":\"0\",\"aria-valuemax\":\"100\"}},[(entry.pct > 20)?_c('span',[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: entry.currency_code}).format(entry.difference_float))+\"\\n \")]):_vm._e()]),_vm._v(\" \"),(entry.pct <= 20)?_c('span',[_vm._v(\" \\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: entry.currency_code}).format(entry.difference_float))+\"\\n \")]):_vm._e()]):_vm._e()])])}),0)])]),_vm._v(\" \"),_c('div',{staticClass:\"card-footer\"},[_c('a',{staticClass:\"btn btn-default button-sm\",attrs:{\"href\":\"./transactions/deposit\"}},[_c('i',{staticClass:\"far fa-money-bill-alt\"}),_vm._v(\" \"+_vm._s(_vm.$t('firefly.go_to_deposits')))])])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainDebitList.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainDebitList.vue?vue&type=script&lang=js&\"","\n\n\n\n\n","import { render, staticRenderFns } from \"./MainDebitList.vue?vue&type=template&id=30a09b68&\"\nimport script from \"./MainDebitList.vue?vue&type=script&lang=js&\"\nexport * from \"./MainDebitList.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-header\"},[_c('h3',{staticClass:\"card-title\"},[_vm._v(_vm._s(_vm.$t('firefly.expense_accounts')))])]),_vm._v(\" \"),_c('div',{staticClass:\"card-body table-responsive p-0\"},[_c('table',{staticClass:\"table table-sm\"},[_c('tbody',_vm._l((_vm.expenses),function(entry){return _c('tr',[_c('td',{staticStyle:{\"width\":\"20%\"}},[_c('a',{attrs:{\"href\":'./accounts/show/' + entry.id}},[_vm._v(_vm._s(entry.name))])]),_vm._v(\" \"),_c('td',{staticClass:\"align-middle\"},[(entry.pct > 0)?_c('div',{staticClass:\"progress\"},[_c('div',{staticClass:\"progress-bar progress-bar-striped bg-danger\",style:({ width: entry.pct + '%'}),attrs:{\"role\":\"progressbar\",\"aria-valuenow\":entry.pct,\"aria-valuemin\":\"0\",\"aria-valuemax\":\"100\"}},[(entry.pct > 20)?_c('span',[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: entry.currency_code}).format(entry.difference_float))+\"\\n \")]):_vm._e()]),_vm._v(\" \"),(entry.pct <= 20)?_c('span',[_vm._v(\" \\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: entry.currency_code}).format(entry.difference_float))+\"\\n \")]):_vm._e()]):_vm._e()])])}),0)])]),_vm._v(\" \"),_c('div',{staticClass:\"card-footer\"},[_c('a',{staticClass:\"btn btn-default button-sm\",attrs:{\"href\":\"./transactions/withdrawal\"}},[_c('i',{staticClass:\"far fa-money-bill-alt\"}),_vm._v(\" \"+_vm._s(_vm.$t('firefly.go_to_withdrawals')))])])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainPiggyList.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainPiggyList.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n\n","import { render, staticRenderFns } from \"./MainPiggyList.vue?vue&type=template&id=14a8a9a0&scoped=true&\"\nimport script from \"./MainPiggyList.vue?vue&type=script&lang=js&\"\nexport * from \"./MainPiggyList.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"14a8a9a0\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-header\"},[_c('h3',{staticClass:\"card-title\"},[_vm._v(_vm._s(_vm.$t('firefly.piggy_banks')))])]),_vm._v(\" \"),_c('div',{staticClass:\"card-body table-responsive p-0\"},[_c('table',{staticClass:\"table table-striped\"},[_c('caption',{staticStyle:{\"display\":\"none\"}},[_vm._v(_vm._s(_vm.$t('firefly.piggy_banks')))]),_vm._v(\" \"),_c('thead',[_c('tr',[_c('th',{staticStyle:{\"width\":\"35%\"},attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('list.piggy_bank')))]),_vm._v(\" \"),_c('th',{staticStyle:{\"width\":\"40%\"},attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('list.percentage'))+\" \"),_c('small',[_vm._v(\"/ \"+_vm._s(_vm.$t('list.amount')))])])])]),_vm._v(\" \"),_c('tbody',_vm._l((this.piggy_banks),function(piggy){return _c('tr',[_c('td',[_vm._v(_vm._s(piggy.attributes.name)+\"\\n \"),(piggy.attributes.object_group_title)?_c('small',{staticClass:\"text-muted\"},[_c('br'),_vm._v(\"\\n \"+_vm._s(piggy.attributes.object_group_title)+\"\\n \")]):_vm._e()]),_vm._v(\" \"),_c('td',[_c('div',{staticClass:\"progress-group\"},[_c('div',{staticClass:\"progress progress-sm\"},[(piggy.attributes.pct < 100)?_c('div',{staticClass:\"progress-bar progress-bar-striped primary\",style:({'width': piggy.attributes.pct + '%'})}):_vm._e(),_vm._v(\" \"),(100 === piggy.attributes.pct)?_c('div',{staticClass:\"progress-bar progress-bar-striped bg-success\",style:({'width': piggy.attributes.pct + '%'})}):_vm._e()])]),_vm._v(\" \"),_c('span',{staticClass:\"text-success\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: piggy.attributes.currency_code}).format(piggy.attributes.current_amount))+\"\\n \")]),_vm._v(\"\\n of\\n \"),_c('span',{staticClass:\"text-success\"},[_vm._v(_vm._s(Intl.NumberFormat(_vm.locale, {\n style: 'currency',\n currency: piggy.attributes.currency_code\n }).format(piggy.attributes.target_amount)))])])])}),0)])]),_vm._v(\" \"),_c('div',{staticClass:\"card-footer\"},[_c('a',{staticClass:\"btn btn-default button-sm\",attrs:{\"href\":\"./piggy-banks\"}},[_c('i',{staticClass:\"far fa-money-bill-alt\"}),_vm._v(\" \"+_vm._s(_vm.$t('firefly.go_to_piggies')))])])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionListLarge.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionListLarge.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n\n","import { render, staticRenderFns } from \"./TransactionListLarge.vue?vue&type=template&id=3b50021b&scoped=true&\"\nimport script from \"./TransactionListLarge.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionListLarge.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"3b50021b\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('table',{staticClass:\"table table-striped table-sm\"},[_c('caption',{staticStyle:{\"display\":\"none\"}},[_vm._v(_vm._s(_vm.$t('firefly.transaction_table_description')))]),_vm._v(\" \"),_c('thead',[_c('tr',[_c('th',{staticClass:\"text-left\",attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('firefly.description')))]),_vm._v(\" \"),_c('th',{attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('firefly.opposing_account')))]),_vm._v(\" \"),_c('th',{staticClass:\"text-right\",attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('firefly.amount')))]),_vm._v(\" \"),_c('th',{attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('firefly.category')))]),_vm._v(\" \"),_c('th',{attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('firefly.budget')))])])]),_vm._v(\" \"),_c('tbody',_vm._l((this.transactions),function(transaction){return _c('tr',[_c('td',[_c('a',{attrs:{\"href\":'transactions/show/' + transaction.id,\"title\":transaction.date}},[(transaction.attributes.transactions.length > 1)?_c('span',[_vm._v(_vm._s(transaction.attributes.group_title))]):_vm._e(),_vm._v(\" \"),(1===transaction.attributes.transactions.length)?_c('span',[_vm._v(_vm._s(transaction.attributes.transactions[0].description))]):_vm._e()])]),_vm._v(\" \"),_c('td',_vm._l((transaction.attributes.transactions),function(tr){return _c('span',[('withdrawal' === tr.type)?_c('a',{attrs:{\"href\":'accounts/show/' + tr.destination_id}},[_vm._v(_vm._s(tr.destination_name))]):_vm._e(),_vm._v(\" \"),('deposit' === tr.type)?_c('a',{attrs:{\"href\":'accounts/show/' + tr.source_id}},[_vm._v(_vm._s(tr.source_name))]):_vm._e(),_vm._v(\" \"),('transfer' === tr.type && tr.source_id === _vm.account_id)?_c('a',{attrs:{\"href\":'accounts/show/' + tr.destination_id}},[_vm._v(_vm._s(tr.destination_name))]):_vm._e(),_vm._v(\" \"),('transfer' === tr.type && tr.destination_id === _vm.account_id)?_c('a',{attrs:{\"href\":'accounts/show/' + tr.source_id}},[_vm._v(_vm._s(tr.source_name))]):_vm._e(),_vm._v(\" \"),_c('br')])}),0),_vm._v(\" \"),_c('td',{staticStyle:{\"text-align\":\"right\"}},_vm._l((transaction.attributes.transactions),function(tr){return _c('span',[('withdrawal' === tr.type)?_c('span',{staticClass:\"text-danger\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat('en-US', {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1))),_c('br')]):_vm._e(),_vm._v(\" \"),('deposit' === tr.type)?_c('span',{staticClass:\"text-success\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat('en-US', {style: 'currency', currency: tr.currency_code}).format(tr.amount))),_c('br')]):_vm._e(),_vm._v(\" \"),('transfer' === tr.type && tr.source_id === _vm.account_id)?_c('span',{staticClass:\"text-info\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat('en-US', {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1))),_c('br')]):_vm._e(),_vm._v(\" \"),('transfer' === tr.type && tr.destination_id === _vm.account_id)?_c('span',{staticClass:\"text-info\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat('en-US', {style: 'currency', currency: tr.currency_code}).format(tr.amount))),_c('br')]):_vm._e()])}),0),_vm._v(\" \"),_c('td',_vm._l((transaction.attributes.transactions),function(tr){return _c('span',[(0!==tr.category_id)?_c('a',{attrs:{\"href\":'categories/show/' + tr.category_id}},[_vm._v(_vm._s(tr.category_name))]):_vm._e(),_c('br')])}),0),_vm._v(\" \"),_c('td',_vm._l((transaction.attributes.transactions),function(tr){return _c('span',[(0!==tr.budget_id)?_c('a',{attrs:{\"href\":'budgets/show/' + tr.budget_id}},[_vm._v(_vm._s(tr.budget_name))]):_vm._e(),_c('br')])}),0)])}),0)])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionListMedium.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionListMedium.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n\n","import { render, staticRenderFns } from \"./TransactionListMedium.vue?vue&type=template&id=0c7078c9&scoped=true&\"\nimport script from \"./TransactionListMedium.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionListMedium.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"0c7078c9\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('table',{staticClass:\"table table-striped table-sm\"},[_c('caption',{staticStyle:{\"display\":\"none\"}},[_vm._v(_vm._s(_vm.$t('firefly.transaction_table_description')))]),_vm._v(\" \"),_c('thead',[_c('tr',[_c('th',{staticClass:\"text-left\",attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('firefly.description')))]),_vm._v(\" \"),_c('th',{attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('firefly.opposing_account')))]),_vm._v(\" \"),_c('th',{staticClass:\"text-right\",attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('firefly.amount')))])])]),_vm._v(\" \"),_c('tbody',_vm._l((this.transactions),function(transaction){return _c('tr',[_c('td',[_c('a',{attrs:{\"href\":'transactions/show/' + transaction.id,\"title\":transaction.date}},[(transaction.attributes.transactions.length > 1)?_c('span',[_vm._v(_vm._s(transaction.attributes.group_title))]):_vm._e(),_vm._v(\" \"),(1===transaction.attributes.transactions.length)?_c('span',[_vm._v(_vm._s(transaction.attributes.transactions[0].description))]):_vm._e()])]),_vm._v(\" \"),_c('td',_vm._l((transaction.attributes.transactions),function(tr){return _c('span',[('withdrawal' === tr.type)?_c('a',{attrs:{\"href\":'accounts/show/' + tr.destination_id}},[_vm._v(_vm._s(tr.destination_name))]):_vm._e(),_vm._v(\" \"),('deposit' === tr.type)?_c('a',{attrs:{\"href\":'accounts/show/' + tr.source_id}},[_vm._v(_vm._s(tr.source_name))]):_vm._e(),_vm._v(\" \"),('transfer' === tr.type && tr.source_id === _vm.account_id)?_c('a',{attrs:{\"href\":'accounts/show/' + tr.destination_id}},[_vm._v(_vm._s(tr.destination_name))]):_vm._e(),_vm._v(\" \"),('transfer' === tr.type && tr.destination_id === _vm.account_id)?_c('a',{attrs:{\"href\":'accounts/show/' + tr.source_id}},[_vm._v(_vm._s(tr.source_name))]):_vm._e(),_vm._v(\" \"),_c('br')])}),0),_vm._v(\" \"),_c('td',{staticStyle:{\"text-align\":\"right\"}},_vm._l((transaction.attributes.transactions),function(tr){return _c('span',[('withdrawal' === tr.type)?_c('span',{staticClass:\"text-danger\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat('en-US', {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1))),_c('br')]):_vm._e(),_vm._v(\" \"),('deposit' === tr.type)?_c('span',{staticClass:\"text-success\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat('en-US', {style: 'currency', currency: tr.currency_code}).format(tr.amount))),_c('br')]):_vm._e(),_vm._v(\" \"),('transfer' === tr.type && tr.source_id === _vm.account_id)?_c('span',{staticClass:\"text-info\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat('en-US', {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1))),_c('br')]):_vm._e(),_vm._v(\" \"),('transfer' === tr.type && tr.destination_id === _vm.account_id)?_c('span',{staticClass:\"text-info\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat('en-US', {style: 'currency', currency: tr.currency_code}).format(tr.amount))),_c('br')]):_vm._e()])}),0)])}),0)])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionListSmall.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionListSmall.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n\n","import { render, staticRenderFns } from \"./TransactionListSmall.vue?vue&type=template&id=fae12aa6&scoped=true&\"\nimport script from \"./TransactionListSmall.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionListSmall.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"fae12aa6\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('table',{staticClass:\"table table-striped table-sm\"},[_c('caption',{staticStyle:{\"display\":\"none\"}},[_vm._v(_vm._s(_vm.$t('firefly.transaction_table_description')))]),_vm._v(\" \"),_c('thead',[_c('tr',[_c('th',{staticClass:\"text-left\",attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('firefly.description')))]),_vm._v(\" \"),_c('th',{staticClass:\"text-right\",attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('firefly.amount')))])])]),_vm._v(\" \"),_c('tbody',_vm._l((this.transactions),function(transaction){return _c('tr',[_c('td',[_c('a',{attrs:{\"href\":'transactions/show/' + transaction.id,\"title\":new Intl.DateTimeFormat(_vm.locale, { year: 'numeric', month: 'long', day: 'numeric' }).format(new Date(transaction.attributes.transactions[0].date))}},[(transaction.attributes.transactions.length > 1)?_c('span',[_vm._v(_vm._s(transaction.attributes.group_title))]):_vm._e(),_vm._v(\" \"),(1===transaction.attributes.transactions.length)?_c('span',[_vm._v(_vm._s(transaction.attributes.transactions[0].description))]):_vm._e()])]),_vm._v(\" \"),_c('td',{staticStyle:{\"text-align\":\"right\"}},_vm._l((transaction.attributes.transactions),function(tr){return _c('span',[('withdrawal' === tr.type)?_c('span',{staticClass:\"text-danger\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1))),_c('br')]):_vm._e(),_vm._v(\" \"),('deposit' === tr.type)?_c('span',{staticClass:\"text-success\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount))),_c('br')]):_vm._e(),_vm._v(\" \"),('transfer' === tr.type && tr.source_id === _vm.account_id)?_c('span',{staticClass:\"text-info\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1))),_c('br')]):_vm._e(),_vm._v(\" \"),('transfer' === tr.type && tr.destination_id === _vm.account_id)?_c('span',{staticClass:\"text-info\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount))),_c('br')]):_vm._e()])}),0)])}),0)])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Calendar.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Calendar.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n","import { render, staticRenderFns } from \"./Calendar.vue?vue&type=template&id=03e62f58&scoped=true&\"\nimport script from \"./Calendar.vue?vue&type=script&lang=js&\"\nexport * from \"./Calendar.vue?vue&type=script&lang=js&\"\nimport style0 from \"./Calendar.vue?vue&type=style&index=0&id=03e62f58&scoped=true&lang=css&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"03e62f58\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_vm._v(\"Start\")]),_vm._v(\" \"),_c('div',{staticClass:\"col-8\"},[_vm._v(_vm._s(new Intl.DateTimeFormat(_vm.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(_vm.range.start)))])]),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_vm._v(\"End\")]),_vm._v(\" \"),_c('div',{staticClass:\"col-8\"},[_vm._v(_vm._s(new Intl.DateTimeFormat(_vm.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(_vm.range.end)))])]),_vm._v(\" \"),_c('date-picker',{attrs:{\"mode\":\"date\",\"rows\":\"2\",\"is-range\":\"\"},scopedSlots:_vm._u([{key:\"default\",fn:function(ref){\nvar inputValue = ref.inputValue;\nvar inputEvents = ref.inputEvents;\nvar isDragging = ref.isDragging;\nvar togglePopover = ref.togglePopover;\nreturn [_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('div',{staticClass:\"btn-group btn-group-sm d-flex\"},[_c('button',{staticClass:\"btn btn-secondary btn-sm\",on:{\"click\":function($event){return togglePopover({ placement: 'auto-start', positionFixed:true })}}},[_c('i',{staticClass:\"fas fa-calendar-alt\"})]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-secondary\"},[_c('i',{staticClass:\"fas fa-history\"})]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-secondary dropdown-toggle\",attrs:{\"type\":\"button\",\"id\":\"dropdownMenuButton\",\"data-toggle\":\"dropdown\",\"aria-haspopup\":\"true\",\"aria-expanded\":\"false\"}},[_c('i',{staticClass:\"fas fa-list\"})]),_vm._v(\" \"),_c('div',{staticClass:\"dropdown-menu\",attrs:{\"aria-labelledby\":\"dropdownMenuButton\"}},[_c('a',{staticClass:\"dropdown-item\",attrs:{\"href\":\"#\"}},[_vm._v(\"(prev period)\")]),_vm._v(\" \"),_c('a',{staticClass:\"dropdown-item\",attrs:{\"href\":\"#\"}},[_vm._v(\"(next period)\")]),_vm._v(\" \"),_c('a',{staticClass:\"dropdown-item\",attrs:{\"href\":\"#\"}},[_vm._v(\"(this week?)\")])])]),_vm._v(\" \"),_c('input',_vm._g({class:isDragging ? 'text-gray-600' : 'text-gray-900',attrs:{\"type\":\"hidden\"},domProps:{\"value\":inputValue.start}},inputEvents.start)),_vm._v(\" \"),_c('input',_vm._g({class:isDragging ? 'text-gray-600' : 'text-gray-900',attrs:{\"type\":\"hidden\"},domProps:{\"value\":inputValue.end}},inputEvents.end))])])]}}]),model:{value:(_vm.range),callback:function ($$v) {_vm.range=$$v},expression:\"range\"}})],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainCategoryList.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainCategoryList.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n","import { render, staticRenderFns } from \"./MainCategoryList.vue?vue&type=template&id=00bc733f&scoped=true&\"\nimport script from \"./MainCategoryList.vue?vue&type=script&lang=js&\"\nexport * from \"./MainCategoryList.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"00bc733f\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-header\"},[_c('h3',{staticClass:\"card-title\"},[_vm._v(_vm._s(_vm.$t('firefly.categories')))])]),_vm._v(\" \"),_c('div',{staticClass:\"card-body table-responsive p-0\"},[_c('table',{staticClass:\"table table-sm\"},[_c('tbody',_vm._l((_vm.sortedList),function(category){return _c('tr',[_c('td',{staticStyle:{\"width\":\"20%\"}},[_c('a',{attrs:{\"href\":'./categories/show/' + category.id}},[_vm._v(_vm._s(category.name))])]),_vm._v(\" \"),_c('td',{staticClass:\"align-middle\"},[(category.spentPct > 0)?_c('div',{staticClass:\"progress\"},[_c('div',{staticClass:\"progress-bar progress-bar-striped bg-danger\",style:({ width: category.spentPct + '%'}),attrs:{\"role\":\"progressbar\",\"aria-valuenow\":category.spentPct,\"aria-valuemin\":\"0\",\"aria-valuemax\":\"100\"}},[(category.spentPct > 20)?_c('span',[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: category.currency_code}).format(category.spent))+\"\\n \")]):_vm._e()]),_vm._v(\" \"),(category.spentPct <= 20)?_c('span',[_vm._v(\" \\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: category.currency_code}).format(category.spent))+\"\\n \")]):_vm._e()]):_vm._e(),_vm._v(\" \"),(category.earnedPct > 0)?_c('div',{staticClass:\"progress justify-content-end\",attrs:{\"title\":\"hello2\"}},[(category.earnedPct <= 20)?_c('span',[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: category.currency_code}).format(category.earned))+\"\\n  \")]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"progress-bar progress-bar-striped bg-success\",style:({ width: category.earnedPct + '%'}),attrs:{\"role\":\"progressbar\",\"aria-valuenow\":category.earnedPct,\"aria-valuemin\":\"0\",\"aria-valuemax\":\"100\",\"title\":\"hello\"}},[(category.earnedPct > 20)?_c('span',[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: category.currency_code}).format(category.earned))+\"\\n \")]):_vm._e()])]):_vm._e()])])}),0)])])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","/*\n * dashboard.js\n * Copyright (c) 2020 james@firefly-iii.org\n *\n * This file is part of Firefly III (https://github.com/firefly-iii).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see .\n */\n\nimport Dashboard from \"../components/dashboard/Dashboard\";\nimport TopBoxes from \"../components/dashboard/TopBoxes\";\nimport MainAccount from \"../components/dashboard/MainAccount\";\nimport MainAccountList from \"../components/dashboard/MainAccountList\";\nimport MainBillsList from \"../components/dashboard/MainBillsList\";\nimport MainBudgetList from \"../components/dashboard/MainBudgetList\";\nimport MainCreditList from \"../components/dashboard/MainCreditList\";\nimport MainDebitList from \"../components/dashboard/MainDebitList\";\nimport MainPiggyList from \"../components/dashboard/MainPiggyList\";\nimport TransactionListLarge from \"../components/transactions/TransactionListLarge\";\nimport TransactionListMedium from \"../components/transactions/TransactionListMedium\";\nimport TransactionListSmall from \"../components/transactions/TransactionListSmall\";\nimport DatePicker from 'v-calendar/lib/components/date-picker.umd'\nimport Calendar from \"../components/dashboard/Calendar\";\nimport MainCategoryList from \"../components/dashboard/MainCategoryList\";\nimport Vue from \"vue\";\nimport Vuex from 'vuex'\nimport store from '../components/store';\n\n/**\n * First we will load Axios via bootstrap.js\n * jquery and bootstrap-sass preloaded in app.js\n * vue, uiv and vuei18n are in app_vue.js\n */\n\nrequire('../bootstrap');\nrequire('chart.js');\n\nVue.component('transaction-list-large', TransactionListLarge);\nVue.component('transaction-list-medium', TransactionListMedium);\nVue.component('transaction-list-small', TransactionListSmall);\n\n// components as an example\nVue.component('date-picker', DatePicker)\nVue.component('dashboard', Dashboard);\nVue.component('top-boxes', TopBoxes);\nVue.component('main-account', MainAccount);\nVue.component('main-account-list', MainAccountList);\nVue.component('main-bills-list', MainBillsList);\nVue.component('main-budget-list', MainBudgetList);\nVue.component('main-category-list', MainCategoryList);\nVue.component('main-debit-list', MainDebitList);\nVue.component('main-credit-list', MainCreditList);\nVue.component('main-piggy-list', MainPiggyList);\n\nVue.use(Vuex);\n\nlet i18n = require('../i18n');\nlet props = {};\n\nnew Vue({\n i18n,\n store,\n el: \"#dashboard\",\n render: (createElement) => {\n return createElement(Dashboard, {props: props});\n },\n beforeCreate() {\n this.$store.commit('initialiseStore');\n this.$store.dispatch('updateCurrencyPreference');\n },\n });\n\nnew Vue({\n i18n,\n store,\n el: \"#calendar\",\n render: (createElement) => {\n return createElement(Calendar, {props: props});\n },\n // TODO init store as well?\n });","\nvar content = require(\"!!../../../node_modules/css-loader/index.js??ref--6-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src/index.js??ref--6-2!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Calendar.vue?vue&type=style&index=0&id=03e62f58&scoped=true&lang=css&\");\n\nif(typeof content === 'string') content = [[module.id, content, '']];\n\nvar transform;\nvar insertInto;\n\n\n\nvar options = {\"hmr\":true}\n\noptions.transform = transform\noptions.insertInto = undefined;\n\nvar update = require(\"!../../../node_modules/style-loader/lib/addStyles.js\")(content, options);\n\nif(content.locals) module.exports = content.locals;\n\nif(module.hot) {\n\tmodule.hot.accept(\"!!../../../node_modules/css-loader/index.js??ref--6-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src/index.js??ref--6-2!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Calendar.vue?vue&type=style&index=0&id=03e62f58&scoped=true&lang=css&\", function() {\n\t\tvar newContent = require(\"!!../../../node_modules/css-loader/index.js??ref--6-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src/index.js??ref--6-2!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Calendar.vue?vue&type=style&index=0&id=03e62f58&scoped=true&lang=css&\");\n\n\t\tif(typeof newContent === 'string') newContent = [[module.id, newContent, '']];\n\n\t\tvar locals = (function(a, b) {\n\t\t\tvar key, idx = 0;\n\n\t\t\tfor(key in a) {\n\t\t\t\tif(!b || a[key] !== b[key]) return false;\n\t\t\t\tidx++;\n\t\t\t}\n\n\t\t\tfor(key in b) idx--;\n\n\t\t\treturn idx === 0;\n\t\t}(content.locals, newContent.locals));\n\n\t\tif(!locals) throw new Error('Aborting CSS HMR due to changed css-modules locals.');\n\n\t\tupdate(newContent);\n\t});\n\n\tmodule.hot.dispose(function() { update(); });\n}"],"sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack:///./src/components/dashboard/Calendar.vue?78f3","webpack:///./node_modules/moment/locale sync ^\\.\\/.*$","webpack:///./src/components/dashboard/Calendar.vue?9bbc","webpack:///./src/components/dashboard/Calendar.vue?b42b","webpack:///./src/components/dashboard/Dashboard.vue?c016","webpack:///./src/components/dashboard/Dashboard.vue?e9df","webpack:///src/components/dashboard/Dashboard.vue","webpack:///./src/components/dashboard/Dashboard.vue","webpack:///src/components/dashboard/TopBoxes.vue","webpack:///./src/components/dashboard/TopBoxes.vue?c977","webpack:///./src/components/dashboard/TopBoxes.vue","webpack:///./src/components/dashboard/TopBoxes.vue?83ff","webpack:///./src/components/charts/DataConverter.vue?d682","webpack:///src/components/charts/DataConverter.vue","webpack:///./src/components/charts/DataConverter.vue","webpack:///./src/components/charts/DefaultLineOptions.vue?36d0","webpack:///src/components/charts/DefaultLineOptions.vue","webpack:///./src/components/charts/DefaultLineOptions.vue","webpack:///./src/components/charts/DefaultLineOptions.vue?6f90","webpack:///src/components/dashboard/MainAccountChart.vue","webpack:///./src/components/dashboard/MainAccountChart.vue?f54b","webpack:///./src/components/dashboard/MainAccountChart.vue","webpack:///src/components/dashboard/MainAccount.vue","webpack:///./src/components/dashboard/MainAccount.vue?1e69","webpack:///./src/components/dashboard/MainAccount.vue","webpack:///./src/components/dashboard/MainAccount.vue?3c35","webpack:///src/components/dashboard/MainAccountList.vue","webpack:///./src/components/dashboard/MainAccountList.vue?889b","webpack:///./src/components/dashboard/MainAccountList.vue","webpack:///./src/components/dashboard/MainAccountList.vue?1697","webpack:///src/components/dashboard/MainBillsList.vue","webpack:///./src/components/dashboard/MainBillsList.vue?536e","webpack:///./src/components/dashboard/MainBillsList.vue","webpack:///./src/components/dashboard/MainBillsList.vue?17cf","webpack:///./src/components/dashboard/BudgetLimitRow.vue?c9a7","webpack:///src/components/dashboard/BudgetLimitRow.vue","webpack:///./src/components/dashboard/BudgetLimitRow.vue","webpack:///./src/components/dashboard/BudgetLimitRow.vue?00b9","webpack:///./src/components/dashboard/BudgetRow.vue?09f9","webpack:///src/components/dashboard/BudgetRow.vue","webpack:///./src/components/dashboard/BudgetListGroup.vue?7954","webpack:///src/components/dashboard/BudgetListGroup.vue","webpack:///./src/components/dashboard/BudgetRow.vue","webpack:///./src/components/dashboard/BudgetRow.vue?d9e9","webpack:///./src/components/dashboard/BudgetListGroup.vue","webpack:///./src/components/dashboard/BudgetListGroup.vue?f3cb","webpack:///src/components/dashboard/MainBudgetList.vue","webpack:///./src/components/dashboard/MainBudgetList.vue?9843","webpack:///./src/components/dashboard/MainBudgetList.vue","webpack:///./src/components/dashboard/MainBudgetList.vue?52c1","webpack:///src/components/dashboard/MainCreditList.vue","webpack:///./src/components/dashboard/MainCreditList.vue?53af","webpack:///./src/components/dashboard/MainCreditList.vue","webpack:///./src/components/dashboard/MainCreditList.vue?c3f7","webpack:///src/components/dashboard/MainDebitList.vue","webpack:///./src/components/dashboard/MainDebitList.vue?587b","webpack:///./src/components/dashboard/MainDebitList.vue","webpack:///./src/components/dashboard/MainDebitList.vue?eda3","webpack:///./src/components/dashboard/MainPiggyList.vue?7bae","webpack:///src/components/dashboard/MainPiggyList.vue","webpack:///./src/components/dashboard/MainPiggyList.vue","webpack:///./src/components/dashboard/MainPiggyList.vue?60b5","webpack:///./src/components/transactions/TransactionListLarge.vue?3500","webpack:///src/components/transactions/TransactionListLarge.vue","webpack:///./src/components/transactions/TransactionListLarge.vue","webpack:///./src/components/transactions/TransactionListLarge.vue?2c1b","webpack:///./src/components/transactions/TransactionListMedium.vue?d4b8","webpack:///src/components/transactions/TransactionListMedium.vue","webpack:///./src/components/transactions/TransactionListMedium.vue","webpack:///./src/components/transactions/TransactionListMedium.vue?f894","webpack:///./src/components/transactions/TransactionListSmall.vue?5a3e","webpack:///src/components/transactions/TransactionListSmall.vue","webpack:///./src/components/transactions/TransactionListSmall.vue","webpack:///./src/components/transactions/TransactionListSmall.vue?0eee","webpack:///src/components/dashboard/Calendar.vue","webpack:///./src/components/dashboard/Calendar.vue?b9d0","webpack:///./src/components/dashboard/Calendar.vue","webpack:///./src/components/dashboard/Calendar.vue?7987","webpack:///src/components/dashboard/MainCategoryList.vue","webpack:///./src/components/dashboard/MainCategoryList.vue?9f14","webpack:///./src/components/dashboard/MainCategoryList.vue","webpack:///./src/components/dashboard/MainCategoryList.vue?5708","webpack:///./src/pages/dashboard.js"],"names":["content","module","i","options","transform","undefined","locals","exports","map","webpackContext","req","id","webpackContextResolve","__webpack_require__","o","e","Error","code","keys","Object","resolve","push","name","_vm","this","_h","$createElement","_c","_self","_v","staticClass","props","data","summary","balances","billsPaid","billsUnpaid","leftToSpend","netWorth","loading","error","ready","computed","start","end","prefCurrencyBalances","filterOnCurrency","notPrefCurrencyBalances","filterOnNotCurrency","prefBillsUnpaid","notPrefBillsUnpaid","prefLeftToSpend","notPrefLeftToSpend","prefNetWorth","notPrefNetWorth","currencyCode","$store","getters","currencyId","watch","datesReady","value","prepareComponent","created","methods","array","hasOwnProperty","key","currency_id","ret","length","axios","get","startStr","endStr","buildComponent","getBalanceEntries","getBillsEntries","getLeftToSpend","getNetWorth","hasCurrency","getKeyedEntries","expected","substr","result","_m","_e","_s","$t","_l","balance","attrs","sub_title","value_parsed","index","bill","left","nw","staticStyle","dataSet","newDataSet","locale","localStorage","local","convertChart","count","labels","datasets","getLabels","getDataSets","colorizeBarData","fillColors","colourSet","setKey","dataset","fill","backgroundColor","borderColor","colorizeLineData","convertLabelsToDate","labelKey","Intl","DateTimeFormat","format","unixTimeZero","firstSet","entries","entryLabel","oldSet","newSet","label","type","currency_symbol","currency_code","yAxisID","formatLabel","sections","words","str","String","split","temp","forEach","item","concat","maxwidth","getDefaultOptions","self","legend","display","animation","duration","responsive","maintainAspectRatio","elements","line","cubicInterpolationMode","scales","xAxes","yAxes","ticks","callback","NumberFormat","beginAtZero","tooltips","mode","callbacks","tooltipItem","datasetIndex","nrString","extends","mixins","reactiveProp","mounted","renderChart","chartData","components","dataCollection","chartOptions","DefaultLineOptions","initialiseChart","url","accounts","initialiseList","loadAccounts","accountIds","test","loadSingleAccount","accountId","loadTransactions","account","class","title","parseFloat","current_balance","style","currency","transactions","bills","initialiseBills","renderPaidDate","obj","transaction_group_id","loadBills","attributes","pay_dates","active","amount_min","amount_max","object_group_title","paidDate","domProps","payDate","paid_dates","year","month","day","Date","budgetLimit","default","budget","budget_id","budget_name","pctGreen","spent","amount","pctOrange","pctRed","budgetLimits","Array","budgets","budgetList","daily","weekly","monthly","quarterly","half_year","yearly","other","rawBudgets","getBudgets","income","max","getIncome","parseIncome","mainKey","parseInt","difference_float","current","pct","entry","width","expenses","getExpenses","parseExpenses","piggy_banks","loadPiggyBanks","piggy","left_to_save","sort","a","b","current_amount","target_amount","account_id","Number","transaction","date","group_title","description","tr","destination_id","destination_name","source_id","source_name","category_id","category_name","range","defaultRange","periods","resetDate","defaultStart","defaultEnd","setStart","setEnd","customDate","setDate","getDate","scopedSlots","_u","fn","ref","inputValue","inputEvents","isDragging","togglePopover","on","$event","placement","positionFixed","period","_g","model","$$v","expression","categories","sortedList","earned","getCategories","category","spentPct","earnedPct","require","Vue","component","TransactionListLarge","TransactionListMedium","TransactionListSmall","DatePicker","Dashboard","TopBoxes","MainAccount","MainAccountList","MainBillsList","MainBudgetList","MainCategoryList","MainDebitList","MainCreditList","MainPiggyList","use","Vuex","i18n","store","el","render","createElement","beforeCreate","commit","dispatch","Calendar"],"mappings":"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","file":"/public/js/dashboard.js","sourcesContent":["\nvar content = require(\"!!../../../node_modules/css-loader/index.js??ref--6-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src/index.js??ref--6-2!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Calendar.vue?vue&type=style&index=0&id=51abf689&scoped=true&lang=css&\");\n\nif(typeof content === 'string') content = [[module.id, content, '']];\n\nvar transform;\nvar insertInto;\n\n\n\nvar options = {\"hmr\":true}\n\noptions.transform = transform\noptions.insertInto = undefined;\n\nvar update = require(\"!../../../node_modules/style-loader/lib/addStyles.js\")(content, options);\n\nif(content.locals) module.exports = content.locals;\n\nif(module.hot) {\n\tmodule.hot.accept(\"!!../../../node_modules/css-loader/index.js??ref--6-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src/index.js??ref--6-2!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Calendar.vue?vue&type=style&index=0&id=51abf689&scoped=true&lang=css&\", function() {\n\t\tvar newContent = require(\"!!../../../node_modules/css-loader/index.js??ref--6-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src/index.js??ref--6-2!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Calendar.vue?vue&type=style&index=0&id=51abf689&scoped=true&lang=css&\");\n\n\t\tif(typeof newContent === 'string') newContent = [[module.id, newContent, '']];\n\n\t\tvar locals = (function(a, b) {\n\t\t\tvar key, idx = 0;\n\n\t\t\tfor(key in a) {\n\t\t\t\tif(!b || a[key] !== b[key]) return false;\n\t\t\t\tidx++;\n\t\t\t}\n\n\t\t\tfor(key in b) idx--;\n\n\t\t\treturn idx === 0;\n\t\t}(content.locals, newContent.locals));\n\n\t\tif(!locals) throw new Error('Aborting CSS HMR due to changed css-modules locals.');\n\n\t\tupdate(newContent);\n\t});\n\n\tmodule.hot.dispose(function() { update(); });\n}","var map = {\n\t\"./af\": 59,\n\t\"./af.js\": 59,\n\t\"./ar\": 60,\n\t\"./ar-dz\": 61,\n\t\"./ar-dz.js\": 61,\n\t\"./ar-kw\": 62,\n\t\"./ar-kw.js\": 62,\n\t\"./ar-ly\": 63,\n\t\"./ar-ly.js\": 63,\n\t\"./ar-ma\": 64,\n\t\"./ar-ma.js\": 64,\n\t\"./ar-sa\": 65,\n\t\"./ar-sa.js\": 65,\n\t\"./ar-tn\": 66,\n\t\"./ar-tn.js\": 66,\n\t\"./ar.js\": 60,\n\t\"./az\": 67,\n\t\"./az.js\": 67,\n\t\"./be\": 68,\n\t\"./be.js\": 68,\n\t\"./bg\": 69,\n\t\"./bg.js\": 69,\n\t\"./bm\": 70,\n\t\"./bm.js\": 70,\n\t\"./bn\": 71,\n\t\"./bn-bd\": 72,\n\t\"./bn-bd.js\": 72,\n\t\"./bn.js\": 71,\n\t\"./bo\": 73,\n\t\"./bo.js\": 73,\n\t\"./br\": 74,\n\t\"./br.js\": 74,\n\t\"./bs\": 75,\n\t\"./bs.js\": 75,\n\t\"./ca\": 76,\n\t\"./ca.js\": 76,\n\t\"./cs\": 77,\n\t\"./cs.js\": 77,\n\t\"./cv\": 78,\n\t\"./cv.js\": 78,\n\t\"./cy\": 79,\n\t\"./cy.js\": 79,\n\t\"./da\": 80,\n\t\"./da.js\": 80,\n\t\"./de\": 81,\n\t\"./de-at\": 82,\n\t\"./de-at.js\": 82,\n\t\"./de-ch\": 83,\n\t\"./de-ch.js\": 83,\n\t\"./de.js\": 81,\n\t\"./dv\": 84,\n\t\"./dv.js\": 84,\n\t\"./el\": 85,\n\t\"./el.js\": 85,\n\t\"./en-au\": 86,\n\t\"./en-au.js\": 86,\n\t\"./en-ca\": 87,\n\t\"./en-ca.js\": 87,\n\t\"./en-gb\": 88,\n\t\"./en-gb.js\": 88,\n\t\"./en-ie\": 89,\n\t\"./en-ie.js\": 89,\n\t\"./en-il\": 90,\n\t\"./en-il.js\": 90,\n\t\"./en-in\": 91,\n\t\"./en-in.js\": 91,\n\t\"./en-nz\": 92,\n\t\"./en-nz.js\": 92,\n\t\"./en-sg\": 93,\n\t\"./en-sg.js\": 93,\n\t\"./eo\": 94,\n\t\"./eo.js\": 94,\n\t\"./es\": 95,\n\t\"./es-do\": 96,\n\t\"./es-do.js\": 96,\n\t\"./es-mx\": 97,\n\t\"./es-mx.js\": 97,\n\t\"./es-us\": 98,\n\t\"./es-us.js\": 98,\n\t\"./es.js\": 95,\n\t\"./et\": 99,\n\t\"./et.js\": 99,\n\t\"./eu\": 100,\n\t\"./eu.js\": 100,\n\t\"./fa\": 101,\n\t\"./fa.js\": 101,\n\t\"./fi\": 102,\n\t\"./fi.js\": 102,\n\t\"./fil\": 103,\n\t\"./fil.js\": 103,\n\t\"./fo\": 104,\n\t\"./fo.js\": 104,\n\t\"./fr\": 105,\n\t\"./fr-ca\": 106,\n\t\"./fr-ca.js\": 106,\n\t\"./fr-ch\": 107,\n\t\"./fr-ch.js\": 107,\n\t\"./fr.js\": 105,\n\t\"./fy\": 108,\n\t\"./fy.js\": 108,\n\t\"./ga\": 109,\n\t\"./ga.js\": 109,\n\t\"./gd\": 110,\n\t\"./gd.js\": 110,\n\t\"./gl\": 111,\n\t\"./gl.js\": 111,\n\t\"./gom-deva\": 112,\n\t\"./gom-deva.js\": 112,\n\t\"./gom-latn\": 113,\n\t\"./gom-latn.js\": 113,\n\t\"./gu\": 114,\n\t\"./gu.js\": 114,\n\t\"./he\": 115,\n\t\"./he.js\": 115,\n\t\"./hi\": 116,\n\t\"./hi.js\": 116,\n\t\"./hr\": 117,\n\t\"./hr.js\": 117,\n\t\"./hu\": 118,\n\t\"./hu.js\": 118,\n\t\"./hy-am\": 119,\n\t\"./hy-am.js\": 119,\n\t\"./id\": 120,\n\t\"./id.js\": 120,\n\t\"./is\": 121,\n\t\"./is.js\": 121,\n\t\"./it\": 122,\n\t\"./it-ch\": 123,\n\t\"./it-ch.js\": 123,\n\t\"./it.js\": 122,\n\t\"./ja\": 124,\n\t\"./ja.js\": 124,\n\t\"./jv\": 125,\n\t\"./jv.js\": 125,\n\t\"./ka\": 126,\n\t\"./ka.js\": 126,\n\t\"./kk\": 127,\n\t\"./kk.js\": 127,\n\t\"./km\": 128,\n\t\"./km.js\": 128,\n\t\"./kn\": 129,\n\t\"./kn.js\": 129,\n\t\"./ko\": 130,\n\t\"./ko.js\": 130,\n\t\"./ku\": 131,\n\t\"./ku.js\": 131,\n\t\"./ky\": 132,\n\t\"./ky.js\": 132,\n\t\"./lb\": 133,\n\t\"./lb.js\": 133,\n\t\"./lo\": 134,\n\t\"./lo.js\": 134,\n\t\"./lt\": 135,\n\t\"./lt.js\": 135,\n\t\"./lv\": 136,\n\t\"./lv.js\": 136,\n\t\"./me\": 137,\n\t\"./me.js\": 137,\n\t\"./mi\": 138,\n\t\"./mi.js\": 138,\n\t\"./mk\": 139,\n\t\"./mk.js\": 139,\n\t\"./ml\": 140,\n\t\"./ml.js\": 140,\n\t\"./mn\": 141,\n\t\"./mn.js\": 141,\n\t\"./mr\": 142,\n\t\"./mr.js\": 142,\n\t\"./ms\": 143,\n\t\"./ms-my\": 144,\n\t\"./ms-my.js\": 144,\n\t\"./ms.js\": 143,\n\t\"./mt\": 145,\n\t\"./mt.js\": 145,\n\t\"./my\": 146,\n\t\"./my.js\": 146,\n\t\"./nb\": 147,\n\t\"./nb.js\": 147,\n\t\"./ne\": 148,\n\t\"./ne.js\": 148,\n\t\"./nl\": 149,\n\t\"./nl-be\": 150,\n\t\"./nl-be.js\": 150,\n\t\"./nl.js\": 149,\n\t\"./nn\": 151,\n\t\"./nn.js\": 151,\n\t\"./oc-lnc\": 152,\n\t\"./oc-lnc.js\": 152,\n\t\"./pa-in\": 153,\n\t\"./pa-in.js\": 153,\n\t\"./pl\": 154,\n\t\"./pl.js\": 154,\n\t\"./pt\": 155,\n\t\"./pt-br\": 156,\n\t\"./pt-br.js\": 156,\n\t\"./pt.js\": 155,\n\t\"./ro\": 157,\n\t\"./ro.js\": 157,\n\t\"./ru\": 158,\n\t\"./ru.js\": 158,\n\t\"./sd\": 159,\n\t\"./sd.js\": 159,\n\t\"./se\": 160,\n\t\"./se.js\": 160,\n\t\"./si\": 161,\n\t\"./si.js\": 161,\n\t\"./sk\": 162,\n\t\"./sk.js\": 162,\n\t\"./sl\": 163,\n\t\"./sl.js\": 163,\n\t\"./sq\": 164,\n\t\"./sq.js\": 164,\n\t\"./sr\": 165,\n\t\"./sr-cyrl\": 166,\n\t\"./sr-cyrl.js\": 166,\n\t\"./sr.js\": 165,\n\t\"./ss\": 167,\n\t\"./ss.js\": 167,\n\t\"./sv\": 168,\n\t\"./sv.js\": 168,\n\t\"./sw\": 169,\n\t\"./sw.js\": 169,\n\t\"./ta\": 170,\n\t\"./ta.js\": 170,\n\t\"./te\": 171,\n\t\"./te.js\": 171,\n\t\"./tet\": 172,\n\t\"./tet.js\": 172,\n\t\"./tg\": 173,\n\t\"./tg.js\": 173,\n\t\"./th\": 174,\n\t\"./th.js\": 174,\n\t\"./tk\": 175,\n\t\"./tk.js\": 175,\n\t\"./tl-ph\": 176,\n\t\"./tl-ph.js\": 176,\n\t\"./tlh\": 177,\n\t\"./tlh.js\": 177,\n\t\"./tr\": 178,\n\t\"./tr.js\": 178,\n\t\"./tzl\": 179,\n\t\"./tzl.js\": 179,\n\t\"./tzm\": 180,\n\t\"./tzm-latn\": 181,\n\t\"./tzm-latn.js\": 181,\n\t\"./tzm.js\": 180,\n\t\"./ug-cn\": 182,\n\t\"./ug-cn.js\": 182,\n\t\"./uk\": 183,\n\t\"./uk.js\": 183,\n\t\"./ur\": 184,\n\t\"./ur.js\": 184,\n\t\"./uz\": 185,\n\t\"./uz-latn\": 186,\n\t\"./uz-latn.js\": 186,\n\t\"./uz.js\": 185,\n\t\"./vi\": 187,\n\t\"./vi.js\": 187,\n\t\"./x-pseudo\": 188,\n\t\"./x-pseudo.js\": 188,\n\t\"./yo\": 189,\n\t\"./yo.js\": 189,\n\t\"./zh-cn\": 190,\n\t\"./zh-cn.js\": 190,\n\t\"./zh-hk\": 191,\n\t\"./zh-hk.js\": 191,\n\t\"./zh-mo\": 192,\n\t\"./zh-mo.js\": 192,\n\t\"./zh-tw\": 193,\n\t\"./zh-tw.js\": 193\n};\n\n\nfunction webpackContext(req) {\n\tvar id = webpackContextResolve(req);\n\treturn __webpack_require__(id);\n}\nfunction webpackContextResolve(req) {\n\tif(!__webpack_require__.o(map, req)) {\n\t\tvar e = new Error(\"Cannot find module '\" + req + \"'\");\n\t\te.code = 'MODULE_NOT_FOUND';\n\t\tthrow e;\n\t}\n\treturn map[req];\n}\nwebpackContext.keys = function webpackContextKeys() {\n\treturn Object.keys(map);\n};\nwebpackContext.resolve = webpackContextResolve;\nmodule.exports = webpackContext;\nwebpackContext.id = 246;","export * from \"-!../../../node_modules/style-loader/index.js!../../../node_modules/css-loader/index.js??ref--6-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src/index.js??ref--6-2!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Calendar.vue?vue&type=style&index=0&id=51abf689&scoped=true&lang=css&\"","exports = module.exports = require(\"../../../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \".dropdown-item[data-v-51abf689],.dropdown-item[data-v-51abf689]:hover{color:#212529}\", \"\"]);\n\n// exports\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('top-boxes'),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('main-account')],1)]),_vm._v(\" \"),_c('main-account-list'),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('main-budget-list')],1)]),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('main-category-list')],1)]),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col-lg-6 col-md-12 col-sm-12 col-xs-12\"},[_c('main-debit-list')],1),_vm._v(\" \"),_c('div',{staticClass:\"col-lg-6 col-md-12 col-sm-12 col-xs-12\"},[_c('main-credit-list')],1)]),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col-lg-6 col-md-12 col-sm-12 col-xs-12\"},[_c('main-piggy-list')],1),_vm._v(\" \"),_c('div',{staticClass:\"col-lg-6 col-md-12 col-sm-12 col-xs-12\"},[_c('main-bills-list')],1)])],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Dashboard.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Dashboard.vue?vue&type=script&lang=js&\"","\n\n\n\n\n","import { render, staticRenderFns } from \"./Dashboard.vue?vue&type=template&id=9d50d3a2&\"\nimport script from \"./Dashboard.vue?vue&type=script&lang=js&\"\nexport * from \"./Dashboard.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TopBoxes.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TopBoxes.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./TopBoxes.vue?vue&type=template&id=5c6cdcc5&\"\nimport script from \"./TopBoxes.vue?vue&type=script&lang=js&\"\nexport * from \"./TopBoxes.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('div',{staticClass:\"info-box\"},[_vm._m(0),_vm._v(\" \"),_c('div',{staticClass:\"info-box-content\"},[(!_vm.loading && !_vm.error)?_c('span',{staticClass:\"info-box-text\"},[_vm._v(_vm._s(_vm.$t(\"firefly.balance\")))]):_vm._e(),_vm._v(\" \"),(_vm.loading && !_vm.error)?_c('span',{staticClass:\"info-box-text\"},[_c('i',{staticClass:\"fas fa-spinner fa-spin\"})]):_vm._e(),_vm._v(\" \"),(_vm.error)?_c('span',{staticClass:\"info-box-text\"},[_c('i',{staticClass:\"fas fa-exclamation-triangle text-danger\"})]):_vm._e(),_vm._v(\" \"),_vm._l((_vm.prefCurrencyBalances),function(balance){return _c('span',{staticClass:\"info-box-number\",attrs:{\"title\":balance.sub_title}},[_vm._v(_vm._s(balance.value_parsed))])}),_vm._v(\" \"),_vm._m(1),_vm._v(\" \"),_c('span',{staticClass:\"progress-description\"},[_vm._l((_vm.notPrefCurrencyBalances),function(balance,index){return _c('span',{attrs:{\"title\":balance.sub_title}},[_vm._v(\"\\n \"+_vm._s(balance.value_parsed)),(index+1 !== _vm.notPrefCurrencyBalances.length)?_c('span',[_vm._v(\", \")]):_vm._e()])}),_vm._v(\" \"),(0===_vm.notPrefCurrencyBalances.length)?_c('span',[_vm._v(\" \")]):_vm._e()],2)],2)])]),_vm._v(\" \"),_c('div',{staticClass:\"col\"},[_c('div',{staticClass:\"info-box\"},[_vm._m(2),_vm._v(\" \"),_c('div',{staticClass:\"info-box-content\"},[(!_vm.loading && !_vm.error)?_c('span',{staticClass:\"info-box-text\"},[_vm._v(_vm._s(_vm.$t('firefly.bills_to_pay')))]):_vm._e(),_vm._v(\" \"),(_vm.loading && !_vm.error)?_c('span',{staticClass:\"info-box-text\"},[_c('i',{staticClass:\"fas fa-spinner fa-spin\"})]):_vm._e(),_vm._v(\" \"),(_vm.error)?_c('span',{staticClass:\"info-box-text\"},[_c('i',{staticClass:\"fas fa-exclamation-triangle text-danger\"})]):_vm._e(),_vm._v(\" \"),_vm._l((_vm.prefBillsUnpaid),function(balance){return _c('span',{staticClass:\"info-box-number\"},[_vm._v(_vm._s(balance.value_parsed))])}),_vm._v(\" \"),_vm._m(3),_vm._v(\" \"),_c('span',{staticClass:\"progress-description\"},[_vm._l((_vm.notPrefBillsUnpaid),function(bill,index){return _c('span',[_vm._v(\"\\n \"+_vm._s(bill.value_parsed)),(index+1 !== _vm.notPrefBillsUnpaid.length)?_c('span',[_vm._v(\", \")]):_vm._e()])}),_vm._v(\" \"),(0===_vm.notPrefBillsUnpaid.length)?_c('span',[_vm._v(\" \")]):_vm._e()],2)],2)])]),_vm._v(\" \"),_c('div',{staticClass:\"col\"},[_c('div',{staticClass:\"info-box\"},[_vm._m(4),_vm._v(\" \"),_c('div',{staticClass:\"info-box-content\"},[(!_vm.loading && !_vm.error)?_c('span',{staticClass:\"info-box-text\"},[_vm._v(_vm._s(_vm.$t('firefly.left_to_spend')))]):_vm._e(),_vm._v(\" \"),(_vm.loading && !_vm.error)?_c('span',{staticClass:\"info-box-text\"},[_c('i',{staticClass:\"fas fa-spinner fa-spin\"})]):_vm._e(),_vm._v(\" \"),(_vm.error)?_c('span',{staticClass:\"info-box-text\"},[_c('i',{staticClass:\"fas fa-exclamation-triangle text-danger\"})]):_vm._e(),_vm._v(\" \"),_vm._l((_vm.prefLeftToSpend),function(left){return _c('span',{staticClass:\"info-box-number\",attrs:{\"title\":left.sub_title}},[_vm._v(_vm._s(left.value_parsed))])}),_vm._v(\" \"),_vm._m(5),_vm._v(\" \"),_c('span',{staticClass:\"progress-description\"},[_vm._l((_vm.notPrefLeftToSpend),function(left,index){return _c('span',[_vm._v(\"\\n \"+_vm._s(left.value_parsed)),(index+1 !== _vm.notPrefLeftToSpend.length)?_c('span',[_vm._v(\", \")]):_vm._e()])}),_vm._v(\" \"),(0===_vm.notPrefLeftToSpend.length)?_c('span',[_vm._v(\" \")]):_vm._e()],2)],2)])]),_vm._v(\" \"),_c('div',{staticClass:\"col\"},[_c('div',{staticClass:\"info-box\"},[_vm._m(6),_vm._v(\" \"),_c('div',{staticClass:\"info-box-content\"},[(!_vm.loading && !_vm.error)?_c('span',{staticClass:\"info-box-text\"},[_vm._v(_vm._s(_vm.$t('firefly.net_worth')))]):_vm._e(),_vm._v(\" \"),(_vm.loading && !_vm.error)?_c('span',{staticClass:\"info-box-text\"},[_c('i',{staticClass:\"fas fa-spinner fa-spin\"})]):_vm._e(),_vm._v(\" \"),(_vm.error)?_c('span',{staticClass:\"info-box-text\"},[_c('i',{staticClass:\"fas fa-exclamation-triangle text-danger\"})]):_vm._e(),_vm._v(\" \"),_vm._l((_vm.prefNetWorth),function(nw){return _c('span',{staticClass:\"info-box-number\",attrs:{\"title\":nw.sub_title}},[_vm._v(_vm._s(nw.value_parsed))])}),_vm._v(\" \"),_vm._m(7),_vm._v(\" \"),_c('span',{staticClass:\"progress-description\"},[_vm._l((_vm.notPrefNetWorth),function(nw,index){return _c('span',[_vm._v(\"\\n \"+_vm._s(nw.value_parsed)),(index+1 !== _vm.notPrefNetWorth.length)?_c('span',[_vm._v(\", \")]):_vm._e()])}),_vm._v(\" \"),(0===_vm.notPrefNetWorth.length)?_c('span',[_vm._v(\" \")]):_vm._e()],2)],2)])])])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('span',{staticClass:\"info-box-icon\"},[_c('i',{staticClass:\"far fa-bookmark text-info\"})])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"progress bg-info\"},[_c('div',{staticClass:\"progress-bar\",staticStyle:{\"width\":\"0\"}})])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('span',{staticClass:\"info-box-icon\"},[_c('i',{staticClass:\"far fa-calendar-alt text-teal\"})])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"progress bg-teal\"},[_c('div',{staticClass:\"progress-bar\",staticStyle:{\"width\":\"0\"}})])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('span',{staticClass:\"info-box-icon\"},[_c('i',{staticClass:\"fas fa-money-bill text-success\"})])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"progress bg-success\"},[_c('div',{staticClass:\"progress-bar\",staticStyle:{\"width\":\"0\"}})])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('span',{staticClass:\"info-box-icon\"},[_c('i',{staticClass:\"fas fa-money-bill text-success\"})])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"progress bg-success\"},[_c('div',{staticClass:\"progress-bar\",staticStyle:{\"width\":\"0\"}})])}]\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./DataConverter.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./DataConverter.vue?vue&type=script&lang=js&\"","\n\n\n","var render, staticRenderFns\nimport script from \"./DataConverter.vue?vue&type=script&lang=js&\"\nexport * from \"./DataConverter.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./DefaultLineOptions.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./DefaultLineOptions.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n\n\n","import { render, staticRenderFns } from \"./DefaultLineOptions.vue?vue&type=template&id=5f7e90aa&scoped=true&\"\nimport script from \"./DefaultLineOptions.vue?vue&type=script&lang=js&\"\nexport * from \"./DefaultLineOptions.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"5f7e90aa\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c(\"div\")}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainAccountChart.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainAccountChart.vue?vue&type=script&lang=js&\"","var render, staticRenderFns\nimport script from \"./MainAccountChart.vue?vue&type=script&lang=js&\"\nexport * from \"./MainAccountChart.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainAccount.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainAccount.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./MainAccount.vue?vue&type=template&id=42021b7e&\"\nimport script from \"./MainAccount.vue?vue&type=script&lang=js&\"\nexport * from \"./MainAccount.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-header\"},[_c('h3',{staticClass:\"card-title\"},[_vm._v(_vm._s(_vm.$t('firefly.yourAccounts')))])]),_vm._v(\" \"),_c('div',{staticClass:\"card-body\"},[(!_vm.loading)?_c('div',[(!_vm.loading && !_vm.error)?_c('MainAccountChart',{attrs:{\"chart-data\":_vm.dataCollection,\"options\":_vm.chartOptions}}):_vm._e()],1):_vm._e(),_vm._v(\" \"),(_vm.loading && !_vm.error)?_c('div',{staticClass:\"text-center\"},[_c('i',{staticClass:\"fas fa-spinner fa-spin\"})]):_vm._e(),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"text-center\"},[_c('i',{staticClass:\"fas fa-exclamation-triangle text-danger\"})]):_vm._e()]),_vm._v(\" \"),_c('div',{staticClass:\"card-footer\"},[_c('a',{staticClass:\"btn btn-default button-sm\",attrs:{\"href\":\"./accounts/asset\"}},[_c('i',{staticClass:\"far fa-money-bill-alt\"}),_vm._v(\" \"+_vm._s(_vm.$t('firefly.go_to_asset_accounts')))])])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainAccountList.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainAccountList.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./MainAccountList.vue?vue&type=template&id=5d6bb842&scoped=true&\"\nimport script from \"./MainAccountList.vue?vue&type=script&lang=js&\"\nexport * from \"./MainAccountList.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"5d6bb842\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[(_vm.loading && !_vm.error)?_c('div',{staticClass:\"row\"},[_vm._m(0)]):_vm._e(),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"row\"},[_vm._m(1)]):_vm._e(),_vm._v(\" \"),(!_vm.loading && !_vm.error)?_c('div',{staticClass:\"row\"},_vm._l((_vm.accounts),function(account){return _c('div',{class:{ 'col-lg-12': 1 === _vm.accounts.length, 'col-lg-6': 2 === _vm.accounts.length, 'col-lg-4': _vm.accounts.length > 2 }},[_c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-header\"},[_c('h3',{staticClass:\"card-title\"},[_c('a',{attrs:{\"href\":account.url}},[_vm._v(_vm._s(account.title))])]),_vm._v(\" \"),_c('div',{staticClass:\"card-tools\"},[_c('span',{class:parseFloat(account.current_balance) < 0 ? 'text-danger' : 'text-success'},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: account.currency_code}).format(parseFloat(account.current_balance)))+\"\\n \")])])]),_vm._v(\" \"),_c('div',{staticClass:\"card-body table-responsive p-0\"},[_c('div',[(1===_vm.accounts.length)?_c('transaction-list-large',{attrs:{\"account_id\":account.id,\"transactions\":account.transactions}}):_vm._e(),_vm._v(\" \"),(2===_vm.accounts.length)?_c('transaction-list-medium',{attrs:{\"account_id\":account.id,\"transactions\":account.transactions}}):_vm._e(),_vm._v(\" \"),(_vm.accounts.length > 2)?_c('transaction-list-small',{attrs:{\"account_id\":account.id,\"transactions\":account.transactions}}):_vm._e()],1)])])])}),0):_vm._e()])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"col\"},[_c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-body\"},[_c('div',{staticClass:\"text-center\"},[_c('i',{staticClass:\"fas fa-spinner fa-spin\"})])])])])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"col\"},[_c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-body\"},[_c('div',{staticClass:\"text-center\"},[_c('i',{staticClass:\"fas fa-exclamation-triangle text-danger\"})])])])])}]\n\nexport { render, staticRenderFns }","\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainBillsList.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainBillsList.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./MainBillsList.vue?vue&type=template&id=64c4c037&\"\nimport script from \"./MainBillsList.vue?vue&type=script&lang=js&\"\nexport * from \"./MainBillsList.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-header\"},[_c('h3',{staticClass:\"card-title\"},[_vm._v(_vm._s(_vm.$t('firefly.bills')))])]),_vm._v(\" \"),(_vm.loading && !_vm.error)?_c('div',{staticClass:\"card-body\"},[_vm._m(0)]):_vm._e(),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"card-body\"},[_vm._m(1)]):_vm._e(),_vm._v(\" \"),(!_vm.loading && !_vm.error)?_c('div',{staticClass:\"card-body table-responsive p-0\"},[_c('table',{staticClass:\"table table-striped\"},[_c('caption',{staticStyle:{\"display\":\"none\"}},[_vm._v(_vm._s(_vm.$t('firefly.bills')))]),_vm._v(\" \"),_c('thead',[_c('tr',[_c('th',{staticStyle:{\"width\":\"35%\"},attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('list.name')))]),_vm._v(\" \"),_c('th',{staticStyle:{\"width\":\"25%\"},attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('list.next_expected_match')))])])]),_vm._v(\" \"),_c('tbody',_vm._l((this.bills),function(bill){return _c('tr',[_c('td',[_c('a',{attrs:{\"href\":'./bills/show/' + bill.id,\"title\":bill.attributes.name}},[_vm._v(_vm._s(bill.attributes.name))]),_vm._v(\"\\n (~ \"),_c('span',{staticClass:\"text-danger\"},[_vm._v(_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: bill.attributes.currency_code}).format((parseFloat(bill.attributes.amount_min) +\n parseFloat(bill.attributes.amount_max)) / -2)))]),_vm._v(\")\\n \"),(bill.attributes.object_group_title)?_c('small',{staticClass:\"text-muted\"},[_c('br'),_vm._v(\"\\n \"+_vm._s(bill.attributes.object_group_title)+\"\\n \")]):_vm._e()]),_vm._v(\" \"),_c('td',[_vm._l((bill.attributes.paid_dates),function(paidDate){return _c('span',[_c('span',{domProps:{\"innerHTML\":_vm._s(_vm.renderPaidDate(paidDate))}}),_c('br')])}),_vm._v(\" \"),_vm._l((bill.attributes.pay_dates),function(payDate){return (0===bill.attributes.paid_dates.length)?_c('span',[_vm._v(\"\\n \"+_vm._s(new Intl.DateTimeFormat(_vm.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(new Date(payDate)))),_c('br')]):_vm._e()})],2)])}),0)])]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"card-footer\"},[_c('a',{staticClass:\"btn btn-default button-sm\",attrs:{\"href\":\"./bills\"}},[_c('i',{staticClass:\"far fa-money-bill-alt\"}),_vm._v(\" \"+_vm._s(_vm.$t('firefly.go_to_bills')))])])])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"text-center\"},[_c('i',{staticClass:\"fas fa-spinner fa-spin\"})])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"text-center\"},[_c('i',{staticClass:\"fas fa-exclamation-triangle text-danger\"})])}]\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./BudgetLimitRow.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./BudgetLimitRow.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n","import { render, staticRenderFns } from \"./BudgetLimitRow.vue?vue&type=template&id=6b6de222&scoped=true&\"\nimport script from \"./BudgetLimitRow.vue?vue&type=script&lang=js&\"\nexport * from \"./BudgetLimitRow.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"6b6de222\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('tr',[_c('td',{staticStyle:{\"width\":\"25%\"}},[_c('a',{attrs:{\"href\":'./budgets/show/' + _vm.budgetLimit.budget_id}},[_vm._v(_vm._s(_vm.budgetLimit.budget_name))])]),_vm._v(\" \"),_c('td',{staticStyle:{\"vertical-align\":\"middle\"}},[_c('div',{staticClass:\"progress progress active\"},[_c('div',{staticClass:\"progress-bar bg-success progress-bar-striped\",style:('width: '+ _vm.budgetLimit.pctGreen + '%;'),attrs:{\"aria-valuenow\":_vm.budgetLimit.pctGreen,\"aria-valuemax\":\"100\",\"aria-valuemin\":\"0\",\"role\":\"progressbar\"}},[(_vm.budgetLimit.pctGreen > 35)?_c('span',[_vm._v(\"\\n Spent\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: _vm.budgetLimit.currency_code}).format(_vm.budgetLimit.spent))+\"\\n of\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: _vm.budgetLimit.currency_code}).format(_vm.budgetLimit.amount))+\"\\n \")]):_vm._e()]),_vm._v(\" \"),_c('div',{staticClass:\"progress-bar bg-warning progress-bar-striped\",style:('width: '+ _vm.budgetLimit.pctOrange + '%;'),attrs:{\"aria-valuenow\":_vm.budgetLimit.pctOrange,\"aria-valuemax\":\"100\",\"aria-valuemin\":\"0\",\"role\":\"progressbar\"}},[(_vm.budgetLimit.pctRed <= 50 && _vm.budgetLimit.pctOrange > 35)?_c('span',[_vm._v(\"\\n Spent\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: _vm.budgetLimit.currency_code}).format(_vm.budgetLimit.spent))+\"\\n of\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: _vm.budgetLimit.currency_code}).format(_vm.budgetLimit.amount))+\"\\n \")]):_vm._e()]),_vm._v(\" \"),_c('div',{staticClass:\"progress-bar bg-danger progress-bar-striped\",style:('width: '+ _vm.budgetLimit.pctRed + '%;'),attrs:{\"aria-valuenow\":_vm.budgetLimit.pctRed,\"aria-valuemax\":\"100\",\"aria-valuemin\":\"0\",\"role\":\"progressbar\"}},[(_vm.budgetLimit.pctOrange <= 50 && _vm.budgetLimit.pctRed > 35)?_c('span',{staticClass:\"text-muted\"},[_vm._v(\"\\n Spent\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: _vm.budgetLimit.currency_code}).format(_vm.budgetLimit.spent))+\"\\n of\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: _vm.budgetLimit.currency_code}).format(_vm.budgetLimit.amount))+\"\\n \")]):_vm._e()]),_vm._v(\" \"),(_vm.budgetLimit.pctGreen <= 35 && 0 === _vm.budgetLimit.pctOrange && 0 === _vm.budgetLimit.pctRed && 0 !== _vm.budgetLimit.pctGreen)?_c('span',[_vm._v(\"\\n  \\n Spent\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: _vm.budgetLimit.currency_code}).format(_vm.budgetLimit.spent))+\"\\n of\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: _vm.budgetLimit.currency_code}).format(_vm.budgetLimit.amount))+\"\\n \")]):_vm._e()]),_vm._v(\" \"),_c('small',{staticClass:\"d-none d-lg-block\"},[_vm._v(\"\\n \"+_vm._s(new Intl.DateTimeFormat(_vm.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(_vm.budgetLimit.start))+\"\\n →\\n \"+_vm._s(new Intl.DateTimeFormat(_vm.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(_vm.budgetLimit.end))+\"\\n \")])]),_vm._v(\" \"),_c('td',{staticClass:\"align-middle d-none d-lg-table-cell\",staticStyle:{\"width\":\"10%\"}},[(parseFloat(_vm.budgetLimit.amount) + parseFloat(_vm.budgetLimit.spent) > 0)?_c('span',{staticClass:\"text-success\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {\n style: 'currency',\n currency: _vm.budgetLimit.currency_code\n }).format(parseFloat(_vm.budgetLimit.amount) + parseFloat(_vm.budgetLimit.spent)))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(0.0 === parseFloat(_vm.budgetLimit.amount) + parseFloat(_vm.budgetLimit.spent))?_c('span',{staticClass:\"text-muted\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: _vm.budgetLimit.currency_code}).format(0))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(parseFloat(_vm.budgetLimit.amount) + parseFloat(_vm.budgetLimit.spent) < 0)?_c('span',{staticClass:\"text-danger\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {\n style: 'currency',\n currency: _vm.budgetLimit.currency_code\n }).format(parseFloat(_vm.budgetLimit.amount) + parseFloat(_vm.budgetLimit.spent)))+\"\\n \")]):_vm._e()])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./BudgetRow.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./BudgetRow.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./BudgetListGroup.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./BudgetListGroup.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n","import { render, staticRenderFns } from \"./BudgetRow.vue?vue&type=template&id=2fc8f640&scoped=true&\"\nimport script from \"./BudgetRow.vue?vue&type=script&lang=js&\"\nexport * from \"./BudgetRow.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"2fc8f640\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('tr',[_c('td',{staticStyle:{\"width\":\"25%\"}},[_c('a',{attrs:{\"href\":'./budgets/show/' + _vm.budget.id}},[_vm._v(_vm._s(_vm.budget.name))])]),_vm._v(\" \"),_c('td',{staticClass:\"align-middle text-right\"},[_c('span',{staticClass:\"text-danger\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: _vm.budget.currency_code}).format(parseFloat(_vm.budget.spent)))+\"\\n \")])])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import { render, staticRenderFns } from \"./BudgetListGroup.vue?vue&type=template&id=658dd996&scoped=true&\"\nimport script from \"./BudgetListGroup.vue?vue&type=script&lang=js&\"\nexport * from \"./BudgetListGroup.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"658dd996\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-header\"},[_c('h3',{staticClass:\"card-title\"},[_vm._v(_vm._s(_vm.title))])]),_vm._v(\" \"),_c('div',{staticClass:\"card-body table-responsive p-0\"},[_c('table',{staticClass:\"table table-sm\"},[_c('tbody',[_vm._l((_vm.budgetLimits),function(budgetLimit,key){return _c('BudgetLimitRow',{key:key,attrs:{\"budgetLimit\":budgetLimit}})}),_vm._v(\" \"),_vm._l((_vm.budgets),function(budget,key){return _c('BudgetRow',{key:key,attrs:{\"budget\":budget}})})],2)])]),_vm._v(\" \"),_c('div',{staticClass:\"card-footer\"},[_c('a',{staticClass:\"btn btn-default button-sm\",attrs:{\"href\":\"./budgets\"}},[_c('i',{staticClass:\"far fa-money-bill-alt\"}),_vm._v(\" \"+_vm._s(_vm.$t('firefly.go_to_budgets')))])])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainBudgetList.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainBudgetList.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./MainBudgetList.vue?vue&type=template&id=03d11977&scoped=true&\"\nimport script from \"./MainBudgetList.vue?vue&type=script&lang=js&\"\nexport * from \"./MainBudgetList.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"03d11977\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[(!_vm.loading)?_c('div',{staticClass:\"row\"},[(_vm.budgetLimits.daily.length > 0)?_c('div',{staticClass:\"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12\"},[_c('BudgetListGroup',{attrs:{\"budgetLimits\":_vm.budgetLimits.daily,\"title\":_vm.$t('firefly.daily_budgets')}})],1):_vm._e(),_vm._v(\" \"),(_vm.budgetLimits.weekly.length > 0)?_c('div',{staticClass:\"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12\"},[_c('BudgetListGroup',{attrs:{\"budgetLimits\":_vm.budgetLimits.weekly,\"title\":_vm.$t('firefly.weekly_budgets')}})],1):_vm._e(),_vm._v(\" \"),(_vm.budgetLimits.monthly.length > 0)?_c('div',{staticClass:\"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12\"},[_c('BudgetListGroup',{attrs:{\"budgetLimits\":_vm.budgetLimits.monthly,\"title\":_vm.$t('firefly.monthly_budgets')}})],1):_vm._e(),_vm._v(\" \"),(_vm.budgetLimits.quarterly.length > 0)?_c('div',{staticClass:\"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12\"},[_c('BudgetListGroup',{attrs:{\"budgetLimits\":_vm.budgetLimits.quarterly,\"title\":_vm.$t('firefly.quarterly_budgets')}})],1):_vm._e(),_vm._v(\" \"),(_vm.budgetLimits.half_year.length > 0)?_c('div',{staticClass:\"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12\"},[_c('BudgetListGroup',{attrs:{\"budgetLimits\":_vm.budgetLimits.half_year,\"title\":_vm.$t('firefly.half_year_budgets')}})],1):_vm._e(),_vm._v(\" \"),(_vm.budgetLimits.yearly.length > 0)?_c('div',{staticClass:\"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12\"},[_c('BudgetListGroup',{attrs:{\"budgetLimits\":_vm.budgetLimits.yearly,\"title\":_vm.$t('firefly.yearly_budgets')}})],1):_vm._e(),_vm._v(\" \"),(_vm.budgetLimits.other.length > 0 || _vm.rawBudgets.length > 0)?_c('div',{staticClass:\"col-xl-6 col-lg-12 col-md-12 col-sm-12 col-xs-12\"},[_c('BudgetListGroup',{attrs:{\"budgetLimits\":_vm.budgetLimits.other,\"budgets\":_vm.rawBudgets,\"title\":_vm.$t('firefly.other_budgets')}})],1):_vm._e()]):_vm._e(),_vm._v(\" \"),(_vm.loading && !_vm.error)?_c('div',{staticClass:\"row\"},[_vm._m(0)]):_vm._e()])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"col\"},[_c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-body\"},[_c('div',{staticClass:\"text-center\"},[_c('i',{staticClass:\"fas fa-spinner fa-spin\"})])])])])}]\n\nexport { render, staticRenderFns }","\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainCreditList.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainCreditList.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./MainCreditList.vue?vue&type=template&id=15163f02&\"\nimport script from \"./MainCreditList.vue?vue&type=script&lang=js&\"\nexport * from \"./MainCreditList.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-header\"},[_c('h3',{staticClass:\"card-title\"},[_vm._v(_vm._s(_vm.$t('firefly.revenue_accounts')))])]),_vm._v(\" \"),(_vm.loading && !_vm.error)?_c('div',{staticClass:\"card-body\"},[_vm._m(0)]):_vm._e(),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"card-body\"},[_vm._m(1)]):_vm._e(),_vm._v(\" \"),(!_vm.loading && !_vm.error)?_c('div',{staticClass:\"card-body table-responsive p-0\"},[_c('table',{staticClass:\"table table-sm\"},[_c('tbody',_vm._l((_vm.income),function(entry){return _c('tr',[_c('td',{staticStyle:{\"width\":\"20%\"}},[_c('a',{attrs:{\"href\":'./accounts/show/' + entry.id}},[_vm._v(_vm._s(entry.name))])]),_vm._v(\" \"),_c('td',{staticClass:\"align-middle\"},[(entry.pct > 0)?_c('div',{staticClass:\"progress\"},[_c('div',{staticClass:\"progress-bar progress-bar-striped bg-success\",style:({ width: entry.pct + '%'}),attrs:{\"aria-valuenow\":entry.pct,\"aria-valuemax\":\"100\",\"aria-valuemin\":\"0\",\"role\":\"progressbar\"}},[(entry.pct > 20)?_c('span',[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: entry.currency_code}).format(entry.difference_float))+\"\\n \")]):_vm._e()]),_vm._v(\" \"),(entry.pct <= 20)?_c('span',[_vm._v(\" \\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: entry.currency_code}).format(entry.difference_float))+\"\\n \")]):_vm._e()]):_vm._e()])])}),0)])]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"card-footer\"},[_c('a',{staticClass:\"btn btn-default button-sm\",attrs:{\"href\":\"./transactions/deposit\"}},[_c('i',{staticClass:\"far fa-money-bill-alt\"}),_vm._v(\" \"+_vm._s(_vm.$t('firefly.go_to_deposits')))])])])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"text-center\"},[_c('i',{staticClass:\"fas fa-spinner fa-spin\"})])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"text-center\"},[_c('i',{staticClass:\"fas fa-exclamation-triangle text-danger\"})])}]\n\nexport { render, staticRenderFns }","\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainDebitList.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainDebitList.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./MainDebitList.vue?vue&type=template&id=0b0b6d16&\"\nimport script from \"./MainDebitList.vue?vue&type=script&lang=js&\"\nexport * from \"./MainDebitList.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-header\"},[_c('h3',{staticClass:\"card-title\"},[_vm._v(_vm._s(_vm.$t('firefly.expense_accounts')))])]),_vm._v(\" \"),(_vm.loading && !_vm.error)?_c('div',{staticClass:\"card-body\"},[_vm._m(0)]):_vm._e(),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"card-body\"},[_vm._m(1)]):_vm._e(),_vm._v(\" \"),(!_vm.loading && !_vm.error)?_c('div',{staticClass:\"card-body table-responsive p-0\"},[_c('table',{staticClass:\"table table-sm\"},[_c('tbody',_vm._l((_vm.expenses),function(entry){return _c('tr',[_c('td',{staticStyle:{\"width\":\"20%\"}},[_c('a',{attrs:{\"href\":'./accounts/show/' + entry.id}},[_vm._v(_vm._s(entry.name))])]),_vm._v(\" \"),_c('td',{staticClass:\"align-middle\"},[(entry.pct > 0)?_c('div',{staticClass:\"progress\"},[_c('div',{staticClass:\"progress-bar progress-bar-striped bg-danger\",style:({ width: entry.pct + '%'}),attrs:{\"aria-valuenow\":entry.pct,\"aria-valuemax\":\"100\",\"aria-valuemin\":\"0\",\"role\":\"progressbar\"}},[(entry.pct > 20)?_c('span',[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: entry.currency_code}).format(entry.difference_float))+\"\\n \")]):_vm._e()]),_vm._v(\" \"),(entry.pct <= 20)?_c('span',[_vm._v(\" \\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: entry.currency_code}).format(entry.difference_float))+\"\\n \")]):_vm._e()]):_vm._e()])])}),0)])]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"card-footer\"},[_c('a',{staticClass:\"btn btn-default button-sm\",attrs:{\"href\":\"./transactions/withdrawal\"}},[_c('i',{staticClass:\"far fa-money-bill-alt\"}),_vm._v(\" \"+_vm._s(_vm.$t('firefly.go_to_withdrawals')))])])])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"text-center\"},[_c('i',{staticClass:\"fas fa-spinner fa-spin\"})])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"text-center\"},[_c('i',{staticClass:\"fas fa-exclamation-triangle text-danger\"})])}]\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainPiggyList.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainPiggyList.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n\n","import { render, staticRenderFns } from \"./MainPiggyList.vue?vue&type=template&id=c17c9a5a&scoped=true&\"\nimport script from \"./MainPiggyList.vue?vue&type=script&lang=js&\"\nexport * from \"./MainPiggyList.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"c17c9a5a\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-header\"},[_c('h3',{staticClass:\"card-title\"},[_vm._v(_vm._s(_vm.$t('firefly.piggy_banks')))])]),_vm._v(\" \"),(_vm.loading && !_vm.error)?_c('div',{staticClass:\"card-body\"},[_vm._m(0)]):_vm._e(),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"card-body\"},[_vm._m(1)]):_vm._e(),_vm._v(\" \"),(!_vm.loading && !_vm.error)?_c('div',{staticClass:\"card-body table-responsive p-0\"},[_c('table',{staticClass:\"table table-striped\"},[_c('caption',{staticStyle:{\"display\":\"none\"}},[_vm._v(_vm._s(_vm.$t('firefly.piggy_banks')))]),_vm._v(\" \"),_c('thead',[_c('tr',[_c('th',{staticStyle:{\"width\":\"35%\"},attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('list.piggy_bank')))]),_vm._v(\" \"),_c('th',{staticStyle:{\"width\":\"40%\"},attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('list.percentage'))+\" \"),_c('small',[_vm._v(\"/ \"+_vm._s(_vm.$t('list.amount')))])])])]),_vm._v(\" \"),_c('tbody',_vm._l((this.piggy_banks),function(piggy){return _c('tr',[_c('td',[_c('a',{attrs:{\"href\":'./piggy-banks/show/' + piggy.id,\"title\":piggy.attributes.name}},[_vm._v(_vm._s(piggy.attributes.name))]),_vm._v(\" \"),(piggy.attributes.object_group_title)?_c('small',{staticClass:\"text-muted\"},[_c('br'),_vm._v(\"\\n \"+_vm._s(piggy.attributes.object_group_title)+\"\\n \")]):_vm._e()]),_vm._v(\" \"),_c('td',[_c('div',{staticClass:\"progress-group\"},[_c('div',{staticClass:\"progress progress-sm\"},[(piggy.attributes.pct < 100)?_c('div',{staticClass:\"progress-bar progress-bar-striped primary\",style:({'width': piggy.attributes.pct + '%'})}):_vm._e(),_vm._v(\" \"),(100 === piggy.attributes.pct)?_c('div',{staticClass:\"progress-bar progress-bar-striped bg-success\",style:({'width': piggy.attributes.pct + '%'})}):_vm._e()])]),_vm._v(\" \"),_c('span',{staticClass:\"text-success\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: piggy.attributes.currency_code}).format(piggy.attributes.current_amount))+\"\\n \")]),_vm._v(\"\\n of\\n \"),_c('span',{staticClass:\"text-success\"},[_vm._v(_vm._s(Intl.NumberFormat(_vm.locale, {\n style: 'currency',\n currency: piggy.attributes.currency_code\n }).format(piggy.attributes.target_amount)))])])])}),0)])]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"card-footer\"},[_c('a',{staticClass:\"btn btn-default button-sm\",attrs:{\"href\":\"./piggy-banks\"}},[_c('i',{staticClass:\"far fa-money-bill-alt\"}),_vm._v(\" \"+_vm._s(_vm.$t('firefly.go_to_piggies')))])])])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"text-center\"},[_c('i',{staticClass:\"fas fa-spinner fa-spin\"})])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"text-center\"},[_c('i',{staticClass:\"fas fa-exclamation-triangle text-danger\"})])}]\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionListLarge.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionListLarge.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n\n","import { render, staticRenderFns } from \"./TransactionListLarge.vue?vue&type=template&id=3aeaec71&scoped=true&\"\nimport script from \"./TransactionListLarge.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionListLarge.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"3aeaec71\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('table',{staticClass:\"table table-striped table-sm\"},[_c('caption',{staticStyle:{\"display\":\"none\"}},[_vm._v(_vm._s(_vm.$t('firefly.transaction_table_description')))]),_vm._v(\" \"),_c('thead',[_c('tr',[_c('th',{staticClass:\"text-left\",attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('firefly.description')))]),_vm._v(\" \"),_c('th',{attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('firefly.opposing_account')))]),_vm._v(\" \"),_c('th',{staticClass:\"text-right\",attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('firefly.amount')))]),_vm._v(\" \"),_c('th',{attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('firefly.category')))]),_vm._v(\" \"),_c('th',{attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('firefly.budget')))])])]),_vm._v(\" \"),_c('tbody',_vm._l((this.transactions),function(transaction){return _c('tr',[_c('td',[_c('a',{attrs:{\"href\":'transactions/show/' + transaction.id,\"title\":transaction.date}},[(transaction.attributes.transactions.length > 1)?_c('span',[_vm._v(_vm._s(transaction.attributes.group_title))]):_vm._e(),_vm._v(\" \"),(1===transaction.attributes.transactions.length)?_c('span',[_vm._v(_vm._s(transaction.attributes.transactions[0].description))]):_vm._e()])]),_vm._v(\" \"),_c('td',_vm._l((transaction.attributes.transactions),function(tr){return _c('span',[('withdrawal' === tr.type)?_c('a',{attrs:{\"href\":'accounts/show/' + tr.destination_id}},[_vm._v(_vm._s(tr.destination_name))]):_vm._e(),_vm._v(\" \"),('deposit' === tr.type)?_c('a',{attrs:{\"href\":'accounts/show/' + tr.source_id}},[_vm._v(_vm._s(tr.source_name))]):_vm._e(),_vm._v(\" \"),('transfer' === tr.type && tr.source_id === _vm.account_id)?_c('a',{attrs:{\"href\":'accounts/show/' + tr.destination_id}},[_vm._v(_vm._s(tr.destination_name))]):_vm._e(),_vm._v(\" \"),('transfer' === tr.type && tr.destination_id === _vm.account_id)?_c('a',{attrs:{\"href\":'accounts/show/' + tr.source_id}},[_vm._v(_vm._s(tr.source_name))]):_vm._e(),_vm._v(\" \"),_c('br')])}),0),_vm._v(\" \"),_c('td',{staticStyle:{\"text-align\":\"right\"}},_vm._l((transaction.attributes.transactions),function(tr){return _c('span',[('withdrawal' === tr.type)?_c('span',{staticClass:\"text-danger\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1))),_c('br')]):_vm._e(),_vm._v(\" \"),('deposit' === tr.type)?_c('span',{staticClass:\"text-success\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount))),_c('br')]):_vm._e(),_vm._v(\" \"),('transfer' === tr.type && tr.source_id === _vm.account_id)?_c('span',{staticClass:\"text-info\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1))),_c('br')]):_vm._e(),_vm._v(\" \"),('transfer' === tr.type && tr.destination_id === _vm.account_id)?_c('span',{staticClass:\"text-info\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount))),_c('br')]):_vm._e()])}),0),_vm._v(\" \"),_c('td',_vm._l((transaction.attributes.transactions),function(tr){return _c('span',[(0!==tr.category_id)?_c('a',{attrs:{\"href\":'categories/show/' + tr.category_id}},[_vm._v(_vm._s(tr.category_name))]):_vm._e(),_c('br')])}),0),_vm._v(\" \"),_c('td',_vm._l((transaction.attributes.transactions),function(tr){return _c('span',[(0!==tr.budget_id)?_c('a',{attrs:{\"href\":'budgets/show/' + tr.budget_id}},[_vm._v(_vm._s(tr.budget_name))]):_vm._e(),_c('br')])}),0)])}),0)])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionListMedium.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionListMedium.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n\n","import { render, staticRenderFns } from \"./TransactionListMedium.vue?vue&type=template&id=c9de6006&scoped=true&\"\nimport script from \"./TransactionListMedium.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionListMedium.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"c9de6006\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('table',{staticClass:\"table table-striped table-sm\"},[_c('caption',{staticStyle:{\"display\":\"none\"}},[_vm._v(_vm._s(_vm.$t('firefly.transaction_table_description')))]),_vm._v(\" \"),_c('thead',[_c('tr',[_c('th',{staticClass:\"text-left\",attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('firefly.description')))]),_vm._v(\" \"),_c('th',{attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('firefly.opposing_account')))]),_vm._v(\" \"),_c('th',{staticClass:\"text-right\",attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('firefly.amount')))])])]),_vm._v(\" \"),_c('tbody',_vm._l((this.transactions),function(transaction){return _c('tr',[_c('td',[_c('a',{attrs:{\"href\":'transactions/show/' + transaction.id,\"title\":transaction.date}},[(transaction.attributes.transactions.length > 1)?_c('span',[_vm._v(_vm._s(transaction.attributes.group_title))]):_vm._e(),_vm._v(\" \"),(1===transaction.attributes.transactions.length)?_c('span',[_vm._v(_vm._s(transaction.attributes.transactions[0].description))]):_vm._e()])]),_vm._v(\" \"),_c('td',_vm._l((transaction.attributes.transactions),function(tr){return _c('span',[('withdrawal' === tr.type)?_c('a',{attrs:{\"href\":'accounts/show/' + tr.destination_id}},[_vm._v(_vm._s(tr.destination_name))]):_vm._e(),_vm._v(\" \"),('deposit' === tr.type)?_c('a',{attrs:{\"href\":'accounts/show/' + tr.source_id}},[_vm._v(_vm._s(tr.source_name))]):_vm._e(),_vm._v(\" \"),('transfer' === tr.type && tr.source_id === _vm.account_id)?_c('a',{attrs:{\"href\":'accounts/show/' + tr.destination_id}},[_vm._v(_vm._s(tr.destination_name))]):_vm._e(),_vm._v(\" \"),('transfer' === tr.type && tr.destination_id === _vm.account_id)?_c('a',{attrs:{\"href\":'accounts/show/' + tr.source_id}},[_vm._v(_vm._s(tr.source_name))]):_vm._e(),_vm._v(\" \"),_c('br')])}),0),_vm._v(\" \"),_c('td',{staticStyle:{\"text-align\":\"right\"}},_vm._l((transaction.attributes.transactions),function(tr){return _c('span',[('withdrawal' === tr.type)?_c('span',{staticClass:\"text-danger\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1))),_c('br')]):_vm._e(),_vm._v(\" \"),('deposit' === tr.type)?_c('span',{staticClass:\"text-success\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount))),_c('br')]):_vm._e(),_vm._v(\" \"),('transfer' === tr.type && tr.source_id === _vm.account_id)?_c('span',{staticClass:\"text-info\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1))),_c('br')]):_vm._e(),_vm._v(\" \"),('transfer' === tr.type && tr.destination_id === _vm.account_id)?_c('span',{staticClass:\"text-info\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount))),_c('br')]):_vm._e()])}),0)])}),0)])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionListSmall.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionListSmall.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n\n","import { render, staticRenderFns } from \"./TransactionListSmall.vue?vue&type=template&id=aa431dd2&scoped=true&\"\nimport script from \"./TransactionListSmall.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionListSmall.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"aa431dd2\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('table',{staticClass:\"table table-striped table-sm\"},[_c('caption',{staticStyle:{\"display\":\"none\"}},[_vm._v(_vm._s(_vm.$t('firefly.transaction_table_description')))]),_vm._v(\" \"),_c('thead',[_c('tr',[_c('th',{staticClass:\"text-left\",attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('firefly.description')))]),_vm._v(\" \"),_c('th',{staticClass:\"text-right\",attrs:{\"scope\":\"col\"}},[_vm._v(_vm._s(_vm.$t('firefly.amount')))])])]),_vm._v(\" \"),_c('tbody',_vm._l((this.transactions),function(transaction){return _c('tr',[_c('td',[_c('a',{attrs:{\"href\":'transactions/show/' + transaction.id,\"title\":new Intl.DateTimeFormat(_vm.locale, { year: 'numeric', month: 'long', day: 'numeric' }).format(new Date(transaction.attributes.transactions[0].date))}},[(transaction.attributes.transactions.length > 1)?_c('span',[_vm._v(_vm._s(transaction.attributes.group_title))]):_vm._e(),_vm._v(\" \"),(1===transaction.attributes.transactions.length)?_c('span',[_vm._v(_vm._s(transaction.attributes.transactions[0].description))]):_vm._e()])]),_vm._v(\" \"),_c('td',{staticStyle:{\"text-align\":\"right\"}},_vm._l((transaction.attributes.transactions),function(tr){return _c('span',[('withdrawal' === tr.type)?_c('span',{staticClass:\"text-danger\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1))),_c('br')]):_vm._e(),_vm._v(\" \"),('deposit' === tr.type)?_c('span',{staticClass:\"text-success\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount))),_c('br')]):_vm._e(),_vm._v(\" \"),('transfer' === tr.type && tr.source_id === _vm.account_id)?_c('span',{staticClass:\"text-info\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount * -1))),_c('br')]):_vm._e(),_vm._v(\" \"),('transfer' === tr.type && tr.destination_id === _vm.account_id)?_c('span',{staticClass:\"text-info\"},[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: tr.currency_code}).format(tr.amount))),_c('br')]):_vm._e()])}),0)])}),0)])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Calendar.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Calendar.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./Calendar.vue?vue&type=template&id=51abf689&scoped=true&\"\nimport script from \"./Calendar.vue?vue&type=script&lang=js&\"\nexport * from \"./Calendar.vue?vue&type=script&lang=js&\"\nimport style0 from \"./Calendar.vue?vue&type=style&index=0&id=51abf689&scoped=true&lang=css&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"51abf689\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_vm._v(\"Start\")]),_vm._v(\" \"),_c('div',{staticClass:\"col-8\"},[_vm._v(_vm._s(new Intl.DateTimeFormat(_vm.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(_vm.range.start)))])]),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_vm._v(\"End\")]),_vm._v(\" \"),_c('div',{staticClass:\"col-8\"},[_vm._v(_vm._s(new Intl.DateTimeFormat(_vm.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(_vm.range.end)))])]),_vm._v(\" \"),_c('date-picker',{attrs:{\"rows\":2,\"is-range\":\"\",\"mode\":\"date\"},scopedSlots:_vm._u([{key:\"default\",fn:function(ref){\nvar inputValue = ref.inputValue;\nvar inputEvents = ref.inputEvents;\nvar isDragging = ref.isDragging;\nvar togglePopover = ref.togglePopover;\nreturn [_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('div',{staticClass:\"btn-group btn-group-sm d-flex\"},[_c('button',{staticClass:\"btn btn-secondary btn-sm\",attrs:{\"title\":_vm.$t('firefly.custom_period')},on:{\"click\":function($event){return togglePopover({ placement: 'auto-start', positionFixed: true })}}},[_c('i',{staticClass:\"fas fa-calendar-alt\"})]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-secondary\",attrs:{\"title\":_vm.$t('firefly.reset_to_current')},on:{\"click\":_vm.resetDate}},[_c('i',{staticClass:\"fas fa-history\"})]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-secondary dropdown-toggle\",attrs:{\"id\":\"dropdownMenuButton\",\"title\":_vm.$t('firefly.select_period'),\"aria-expanded\":\"false\",\"aria-haspopup\":\"true\",\"data-toggle\":\"dropdown\",\"type\":\"button\"}},[_c('i',{staticClass:\"fas fa-list\"})]),_vm._v(\" \"),_c('div',{staticClass:\"dropdown-menu\",attrs:{\"aria-labelledby\":\"dropdownMenuButton\"}},_vm._l((_vm.periods),function(period){return _c('a',{staticClass:\"dropdown-item\",attrs:{\"href\":\"#\"},on:{\"click\":function($event){return _vm.customDate(period.start, period.end)}}},[_vm._v(_vm._s(period.title))])}),0)]),_vm._v(\" \"),_c('input',_vm._g({class:isDragging ? 'text-gray-600' : 'text-gray-900',attrs:{\"type\":\"hidden\"},domProps:{\"value\":inputValue.start}},inputEvents.start)),_vm._v(\" \"),_c('input',_vm._g({class:isDragging ? 'text-gray-600' : 'text-gray-900',attrs:{\"type\":\"hidden\"},domProps:{\"value\":inputValue.end}},inputEvents.end))])])]}}]),model:{value:(_vm.range),callback:function ($$v) {_vm.range=$$v},expression:\"range\"}})],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainCategoryList.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./MainCategoryList.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./MainCategoryList.vue?vue&type=template&id=2a51418c&scoped=true&\"\nimport script from \"./MainCategoryList.vue?vue&type=script&lang=js&\"\nexport * from \"./MainCategoryList.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"2a51418c\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-header\"},[_c('h3',{staticClass:\"card-title\"},[_vm._v(_vm._s(_vm.$t('firefly.categories')))])]),_vm._v(\" \"),(_vm.loading && !_vm.error)?_c('div',{staticClass:\"card-body\"},[_vm._m(0)]):_vm._e(),_vm._v(\" \"),(_vm.error)?_c('div',{staticClass:\"card-body\"},[_vm._m(1)]):_vm._e(),_vm._v(\" \"),(!_vm.loading && !_vm.error)?_c('div',{staticClass:\"card-body table-responsive p-0\"},[_c('table',{staticClass:\"table table-sm\"},[_c('tbody',_vm._l((_vm.sortedList),function(category){return _c('tr',[_c('td',{staticStyle:{\"width\":\"20%\"}},[_c('a',{attrs:{\"href\":'./categories/show/' + category.id}},[_vm._v(_vm._s(category.name))])]),_vm._v(\" \"),_c('td',{staticClass:\"align-middle\"},[(category.spentPct > 0)?_c('div',{staticClass:\"progress\"},[_c('div',{staticClass:\"progress-bar progress-bar-striped bg-danger\",style:({ width: category.spentPct + '%'}),attrs:{\"aria-valuenow\":category.spentPct,\"aria-valuemax\":\"100\",\"aria-valuemin\":\"0\",\"role\":\"progressbar\"}},[(category.spentPct > 20)?_c('span',[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: category.currency_code}).format(category.spent))+\"\\n \")]):_vm._e()]),_vm._v(\" \"),(category.spentPct <= 20)?_c('span',[_vm._v(\" \\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: category.currency_code}).format(category.spent))+\"\\n \")]):_vm._e()]):_vm._e(),_vm._v(\" \"),(category.earnedPct > 0)?_c('div',{staticClass:\"progress justify-content-end\",attrs:{\"title\":\"hello2\"}},[(category.earnedPct <= 20)?_c('span',[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: category.currency_code}).format(category.earned))+\"\\n  \")]):_vm._e(),_vm._v(\" \"),_c('div',{staticClass:\"progress-bar progress-bar-striped bg-success\",style:({ width: category.earnedPct + '%'}),attrs:{\"aria-valuenow\":category.earnedPct,\"aria-valuemax\":\"100\",\"aria-valuemin\":\"0\",\"role\":\"progressbar\",\"title\":\"hello\"}},[(category.earnedPct > 20)?_c('span',[_vm._v(\"\\n \"+_vm._s(Intl.NumberFormat(_vm.locale, {style: 'currency', currency: category.currency_code}).format(category.earned))+\"\\n \")]):_vm._e()])]):_vm._e()])])}),0)])]):_vm._e()])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"text-center\"},[_c('i',{staticClass:\"fas fa-spinner fa-spin\"})])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"text-center\"},[_c('i',{staticClass:\"fas fa-exclamation-triangle text-danger\"})])}]\n\nexport { render, staticRenderFns }","/*\n * dashboard.js\n * Copyright (c) 2020 james@firefly-iii.org\n *\n * This file is part of Firefly III (https://github.com/firefly-iii).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see .\n */\n\nimport Dashboard from \"../components/dashboard/Dashboard\";\nimport TopBoxes from \"../components/dashboard/TopBoxes\";\nimport MainAccount from \"../components/dashboard/MainAccount\";\nimport MainAccountList from \"../components/dashboard/MainAccountList\";\nimport MainBillsList from \"../components/dashboard/MainBillsList\";\nimport MainBudgetList from \"../components/dashboard/MainBudgetList\";\nimport MainCreditList from \"../components/dashboard/MainCreditList\";\nimport MainDebitList from \"../components/dashboard/MainDebitList\";\nimport MainPiggyList from \"../components/dashboard/MainPiggyList\";\nimport TransactionListLarge from \"../components/transactions/TransactionListLarge\";\nimport TransactionListMedium from \"../components/transactions/TransactionListMedium\";\nimport TransactionListSmall from \"../components/transactions/TransactionListSmall\";\nimport DatePicker from 'v-calendar/lib/components/date-picker.umd'\nimport Calendar from \"../components/dashboard/Calendar\";\nimport MainCategoryList from \"../components/dashboard/MainCategoryList\";\nimport Vue from \"vue\";\nimport Vuex from 'vuex'\nimport store from '../components/store';\n\n/**\n * First we will load Axios via bootstrap.js\n * jquery and bootstrap-sass preloaded in app.js\n * vue, uiv and vuei18n are in app_vue.js\n */\n\nrequire('../bootstrap');\nrequire('chart.js');\n\nVue.component('transaction-list-large', TransactionListLarge);\nVue.component('transaction-list-medium', TransactionListMedium);\nVue.component('transaction-list-small', TransactionListSmall);\n\n// components as an example\nVue.component('date-picker', DatePicker)\nVue.component('dashboard', Dashboard);\nVue.component('top-boxes', TopBoxes);\nVue.component('main-account', MainAccount);\nVue.component('main-account-list', MainAccountList);\nVue.component('main-bills-list', MainBillsList);\nVue.component('main-budget-list', MainBudgetList);\nVue.component('main-category-list', MainCategoryList);\nVue.component('main-debit-list', MainDebitList);\nVue.component('main-credit-list', MainCreditList);\nVue.component('main-piggy-list', MainPiggyList);\n\nVue.use(Vuex);\n\nlet i18n = require('../i18n');\nlet props = {};\n\nnew Vue({\n i18n,\n store,\n el: \"#dashboard\",\n render: (createElement) => {\n return createElement(Dashboard, {props: props});\n },\n beforeCreate() {\n this.$store.commit('initialiseStore');\n this.$store.dispatch('updateCurrencyPreference');\n this.$store.dispatch('dashboard/index/initialiseStore');\n },\n });\nnew Vue({\n i18n,\n store,\n el: \"#calendar\",\n render: (createElement) => {\n return createElement(Calendar, {props: props});\n },\n // TODO init store as well?\n });"],"sourceRoot":""} \ No newline at end of file diff --git a/public/v2/js/empty.js b/public/v2/js/empty.js index f0ebc77521..0540d5da4c 100755 --- a/public/v2/js/empty.js +++ b/public/v2/js/empty.js @@ -1,2 +1,2 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[5],{289:function(n,o,p){n.exports=p(290)},290:function(n,o,p){p(15)}},[[289,0,1]]]); +(window.webpackJsonp=window.webpackJsonp||[]).push([[5],{299:function(n,o,p){n.exports=p(300)},300:function(n,o,p){p(16)}},[[299,0,1]]]); //# sourceMappingURL=empty.js.map \ No newline at end of file diff --git a/public/v2/js/new-user/index.js b/public/v2/js/new-user/index.js index 38242fa8f3..4140de5fb8 100755 --- a/public/v2/js/new-user/index.js +++ b/public/v2/js/new-user/index.js @@ -1,2 +1,2 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[6],{291:function(a,e,t){a.exports=t(409)},409:function(a,e,t){"use strict";t.r(e);var s={name:"Index"},n=t(1),i=Object(n.a)(s,(function(){var a=this.$createElement;this._self._c;return this._m(0)}),[function(){var a=this,e=a.$createElement,t=a._self._c||e;return t("div",{staticClass:"row"},[t("div",{staticClass:"col"},[t("div",{attrs:{id:"accordion"}},[t("div",{staticClass:"card card-primary"},[t("div",{staticClass:"card-header"},[t("h4",{staticClass:"card-title"},[t("a",{attrs:{"data-toggle":"collapse","data-parent":"#accordion",href:"#collapseOne"}},[a._v("\n Create new accounts\n ")])])]),a._v(" "),t("div",{staticClass:"panel-collapse collapse show",attrs:{id:"collapseOne"}},[t("div",{staticClass:"card-body"},[t("div",{staticClass:"row"},[t("div",{staticClass:"col"},[t("p",[a._v("Explain")])])]),a._v(" "),t("div",{staticClass:"row"},[t("div",{staticClass:"col-lg-4"},[a._v("\n A\n ")]),a._v(" "),t("div",{staticClass:"col-lg-8"},[a._v("\n B\n ")])])])])]),a._v(" "),t("div",{staticClass:"card card-secondary"},[t("div",{staticClass:"card-header"},[t("h4",{staticClass:"card-title"},[t("a",{attrs:{"data-toggle":"collapse","data-parent":"#accordion",href:"#collapseTwo"}},[a._v("\n Collapsible Group Danger\n ")])])]),a._v(" "),t("div",{staticClass:"panel-collapse collapse",attrs:{id:"collapseTwo"}},[t("div",{staticClass:"card-body"},[a._v("\n Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid.\n 3\n wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt\n laborum\n eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee\n nulla\n assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred\n nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft\n beer\n farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus\n labore sustainable VHS.\n ")])])]),a._v(" "),t("div",{staticClass:"card card-secondary"},[t("div",{staticClass:"card-header"},[t("h4",{staticClass:"card-title"},[t("a",{attrs:{"data-toggle":"collapse","data-parent":"#accordion",href:"#collapseThree"}},[a._v("\n Collapsible Group Success\n ")])])]),a._v(" "),t("div",{staticClass:"panel-collapse collapse",attrs:{id:"collapseThree"}},[t("div",{staticClass:"card-body"},[a._v("\n Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid.\n 3\n wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt\n laborum\n eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee\n nulla\n assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred\n nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft\n beer\n farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus\n labore sustainable VHS.\n ")])])])])])])}],!1,null,"5c520d02",null).exports;t(15);var c=t(19),r={};new Vue({i18n:c,render:function(a){return a(i,{props:r})}}).$mount("#newuser")}},[[291,0,1]]]); +(window.webpackJsonp=window.webpackJsonp||[]).push([[6],{301:function(a,e,t){a.exports=t(427)},427:function(a,e,t){"use strict";t.r(e);var s={name:"Index"},n=t(1),i=Object(n.a)(s,(function(){var a=this.$createElement;this._self._c;return this._m(0)}),[function(){var a=this,e=a.$createElement,t=a._self._c||e;return t("div",{staticClass:"row"},[t("div",{staticClass:"col"},[t("div",{attrs:{id:"accordion"}},[t("div",{staticClass:"card card-primary"},[t("div",{staticClass:"card-header"},[t("h4",{staticClass:"card-title"},[t("a",{attrs:{"data-parent":"#accordion","data-toggle":"collapse",href:"#collapseOne"}},[a._v("\n Create new accounts\n ")])])]),a._v(" "),t("div",{staticClass:"panel-collapse collapse show",attrs:{id:"collapseOne"}},[t("div",{staticClass:"card-body"},[t("div",{staticClass:"row"},[t("div",{staticClass:"col"},[t("p",[a._v("Explain")])])]),a._v(" "),t("div",{staticClass:"row"},[t("div",{staticClass:"col-lg-4"},[a._v("\n A\n ")]),a._v(" "),t("div",{staticClass:"col-lg-8"},[a._v("\n B\n ")])])])])]),a._v(" "),t("div",{staticClass:"card card-secondary"},[t("div",{staticClass:"card-header"},[t("h4",{staticClass:"card-title"},[t("a",{attrs:{"data-parent":"#accordion","data-toggle":"collapse",href:"#collapseTwo"}},[a._v("\n Collapsible Group Danger\n ")])])]),a._v(" "),t("div",{staticClass:"panel-collapse collapse",attrs:{id:"collapseTwo"}},[t("div",{staticClass:"card-body"},[a._v("\n Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid.\n 3\n wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt\n laborum\n eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee\n nulla\n assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred\n nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft\n beer\n farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus\n labore sustainable VHS.\n ")])])]),a._v(" "),t("div",{staticClass:"card card-secondary"},[t("div",{staticClass:"card-header"},[t("h4",{staticClass:"card-title"},[t("a",{attrs:{"data-parent":"#accordion","data-toggle":"collapse",href:"#collapseThree"}},[a._v("\n Collapsible Group Success\n ")])])]),a._v(" "),t("div",{staticClass:"panel-collapse collapse",attrs:{id:"collapseThree"}},[t("div",{staticClass:"card-body"},[a._v("\n Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid.\n 3\n wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt\n laborum\n eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee\n nulla\n assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred\n nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft\n beer\n farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus\n labore sustainable VHS.\n ")])])])])])])}],!1,null,"2d2bc9db",null).exports;t(16);var c=t(19),r={};new Vue({i18n:c,render:function(a){return a(i,{props:r})}}).$mount("#newuser")}},[[301,0,1]]]); //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/public/v2/js/new-user/index.js.map b/public/v2/js/new-user/index.js.map index 0aa393120e..e5b5c6a24e 100755 --- a/public/v2/js/new-user/index.js.map +++ b/public/v2/js/new-user/index.js.map @@ -1 +1 @@ -{"version":3,"sources":["webpack:///./src/components/new-user/Index.vue?4a52","webpack:///./src/components/new-user/Index.vue?1d4f","webpack:///src/components/new-user/Index.vue","webpack:///./src/components/new-user/Index.vue","webpack:///./src/pages/new-user/index.js"],"names":["name","_h","this","$createElement","_self","_c","_m","_vm","staticClass","attrs","_v","require","i18n","props","Vue","render","createElement","Index","$mount"],"mappings":"uIAAA,ICAqM,ECyGrM,CACEA,KAAM,S,OCxFO,EAXC,YACd,GHRW,WAAa,IAAiBC,EAATC,KAAgBC,eAAhBD,KAA0CE,MAAMC,GAAO,OAAvDH,KAAkEI,GAAG,KACjF,CAAC,WAAa,IAAIC,EAAIL,KAASD,EAAGM,EAAIJ,eAAmBE,EAAGE,EAAIH,MAAMC,IAAIJ,EAAG,OAAOI,EAAG,MAAM,CAACG,YAAY,OAAO,CAACH,EAAG,MAAM,CAACG,YAAY,OAAO,CAACH,EAAG,MAAM,CAACI,MAAM,CAAC,GAAK,cAAc,CAACJ,EAAG,MAAM,CAACG,YAAY,qBAAqB,CAACH,EAAG,MAAM,CAACG,YAAY,eAAe,CAACH,EAAG,KAAK,CAACG,YAAY,cAAc,CAACH,EAAG,IAAI,CAACI,MAAM,CAAC,cAAc,WAAW,cAAc,aAAa,KAAO,iBAAiB,CAACF,EAAIG,GAAG,mEAAmEH,EAAIG,GAAG,KAAKL,EAAG,MAAM,CAACG,YAAY,+BAA+BC,MAAM,CAAC,GAAK,gBAAgB,CAACJ,EAAG,MAAM,CAACG,YAAY,aAAa,CAACH,EAAG,MAAM,CAACG,YAAY,OAAO,CAACH,EAAG,MAAM,CAACG,YAAY,OAAO,CAACH,EAAG,IAAI,CAACE,EAAIG,GAAG,iBAAiBH,EAAIG,GAAG,KAAKL,EAAG,MAAM,CAACG,YAAY,OAAO,CAACH,EAAG,MAAM,CAACG,YAAY,YAAY,CAACD,EAAIG,GAAG,iDAAiDH,EAAIG,GAAG,KAAKL,EAAG,MAAM,CAACG,YAAY,YAAY,CAACD,EAAIG,GAAG,yDAAyDH,EAAIG,GAAG,KAAKL,EAAG,MAAM,CAACG,YAAY,uBAAuB,CAACH,EAAG,MAAM,CAACG,YAAY,eAAe,CAACH,EAAG,KAAK,CAACG,YAAY,cAAc,CAACH,EAAG,IAAI,CAACI,MAAM,CAAC,cAAc,WAAW,cAAc,aAAa,KAAO,iBAAiB,CAACF,EAAIG,GAAG,wEAAwEH,EAAIG,GAAG,KAAKL,EAAG,MAAM,CAACG,YAAY,0BAA0BC,MAAM,CAAC,GAAK,gBAAgB,CAACJ,EAAG,MAAM,CAACG,YAAY,aAAa,CAACD,EAAIG,GAAG,gzBAAgzBH,EAAIG,GAAG,KAAKL,EAAG,MAAM,CAACG,YAAY,uBAAuB,CAACH,EAAG,MAAM,CAACG,YAAY,eAAe,CAACH,EAAG,KAAK,CAACG,YAAY,cAAc,CAACH,EAAG,IAAI,CAACI,MAAM,CAAC,cAAc,WAAW,cAAc,aAAa,KAAO,mBAAmB,CAACF,EAAIG,GAAG,yEAAyEH,EAAIG,GAAG,KAAKL,EAAG,MAAM,CAACG,YAAY,0BAA0BC,MAAM,CAAC,GAAK,kBAAkB,CAACJ,EAAG,MAAM,CAACG,YAAY,aAAa,CAACD,EAAIG,GAAG,yzBGUllF,EACA,KACA,WACA,M,QCMFC,EAAQ,IAKR,IAAIC,EAAOD,EAAQ,IAEfE,EAAQ,GACZ,IAAIC,IAAI,CACIF,OACAG,OAFJ,SAEWC,GACH,OAAOA,EAAcC,EAAO,CAACJ,MAAOA,OAEzCK,OAAO,c","file":"/public/js/new-user/index.js","sourcesContent":["var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _vm._m(0)}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('div',{attrs:{\"id\":\"accordion\"}},[_c('div',{staticClass:\"card card-primary\"},[_c('div',{staticClass:\"card-header\"},[_c('h4',{staticClass:\"card-title\"},[_c('a',{attrs:{\"data-toggle\":\"collapse\",\"data-parent\":\"#accordion\",\"href\":\"#collapseOne\"}},[_vm._v(\"\\n Create new accounts\\n \")])])]),_vm._v(\" \"),_c('div',{staticClass:\"panel-collapse collapse show\",attrs:{\"id\":\"collapseOne\"}},[_c('div',{staticClass:\"card-body\"},[_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('p',[_vm._v(\"Explain\")])])]),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col-lg-4\"},[_vm._v(\"\\n A\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"col-lg-8\"},[_vm._v(\"\\n B\\n \")])])])])]),_vm._v(\" \"),_c('div',{staticClass:\"card card-secondary\"},[_c('div',{staticClass:\"card-header\"},[_c('h4',{staticClass:\"card-title\"},[_c('a',{attrs:{\"data-toggle\":\"collapse\",\"data-parent\":\"#accordion\",\"href\":\"#collapseTwo\"}},[_vm._v(\"\\n Collapsible Group Danger\\n \")])])]),_vm._v(\" \"),_c('div',{staticClass:\"panel-collapse collapse\",attrs:{\"id\":\"collapseTwo\"}},[_c('div',{staticClass:\"card-body\"},[_vm._v(\"\\n Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid.\\n 3\\n wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt\\n laborum\\n eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee\\n nulla\\n assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred\\n nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft\\n beer\\n farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus\\n labore sustainable VHS.\\n \")])])]),_vm._v(\" \"),_c('div',{staticClass:\"card card-secondary\"},[_c('div',{staticClass:\"card-header\"},[_c('h4',{staticClass:\"card-title\"},[_c('a',{attrs:{\"data-toggle\":\"collapse\",\"data-parent\":\"#accordion\",\"href\":\"#collapseThree\"}},[_vm._v(\"\\n Collapsible Group Success\\n \")])])]),_vm._v(\" \"),_c('div',{staticClass:\"panel-collapse collapse\",attrs:{\"id\":\"collapseThree\"}},[_c('div',{staticClass:\"card-body\"},[_vm._v(\"\\n Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid.\\n 3\\n wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt\\n laborum\\n eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee\\n nulla\\n assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred\\n nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft\\n beer\\n farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus\\n labore sustainable VHS.\\n \")])])])])])])}]\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Index.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Index.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n","import { render, staticRenderFns } from \"./Index.vue?vue&type=template&id=5c520d02&scoped=true&\"\nimport script from \"./Index.vue?vue&type=script&lang=js&\"\nexport * from \"./Index.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"5c520d02\",\n null\n \n)\n\nexport default component.exports","/*\n * index.js\n * Copyright (c) 2020 james@firefly-iii.org\n *\n * This file is part of Firefly III (https://github.com/firefly-iii).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see .\n */\n\nrequire('../../bootstrap');\n\nimport Index from \"../../components/new-user/Index\";\n\n// i18n\nlet i18n = require('../../i18n');\n\nlet props = {};\nnew Vue({\n i18n,\n render(createElement) {\n return createElement(Index, {props: props});\n }\n }).$mount('#newuser');\n"],"sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack:///./src/components/new-user/Index.vue?11f9","webpack:///./src/components/new-user/Index.vue?1d4f","webpack:///src/components/new-user/Index.vue","webpack:///./src/components/new-user/Index.vue","webpack:///./src/pages/new-user/index.js"],"names":["name","_h","this","$createElement","_self","_c","_m","_vm","staticClass","attrs","_v","require","i18n","props","Vue","render","createElement","Index","$mount"],"mappings":"uIAAA,ICAqM,ECyGrM,CACEA,KAAM,S,OCxFO,EAXC,YACd,GHRW,WAAa,IAAiBC,EAATC,KAAgBC,eAAhBD,KAA0CE,MAAMC,GAAO,OAAvDH,KAAkEI,GAAG,KACjF,CAAC,WAAa,IAAIC,EAAIL,KAASD,EAAGM,EAAIJ,eAAmBE,EAAGE,EAAIH,MAAMC,IAAIJ,EAAG,OAAOI,EAAG,MAAM,CAACG,YAAY,OAAO,CAACH,EAAG,MAAM,CAACG,YAAY,OAAO,CAACH,EAAG,MAAM,CAACI,MAAM,CAAC,GAAK,cAAc,CAACJ,EAAG,MAAM,CAACG,YAAY,qBAAqB,CAACH,EAAG,MAAM,CAACG,YAAY,eAAe,CAACH,EAAG,KAAK,CAACG,YAAY,cAAc,CAACH,EAAG,IAAI,CAACI,MAAM,CAAC,cAAc,aAAa,cAAc,WAAW,KAAO,iBAAiB,CAACF,EAAIG,GAAG,2DAA2DH,EAAIG,GAAG,KAAKL,EAAG,MAAM,CAACG,YAAY,+BAA+BC,MAAM,CAAC,GAAK,gBAAgB,CAACJ,EAAG,MAAM,CAACG,YAAY,aAAa,CAACH,EAAG,MAAM,CAACG,YAAY,OAAO,CAACH,EAAG,MAAM,CAACG,YAAY,OAAO,CAACH,EAAG,IAAI,CAACE,EAAIG,GAAG,iBAAiBH,EAAIG,GAAG,KAAKL,EAAG,MAAM,CAACG,YAAY,OAAO,CAACH,EAAG,MAAM,CAACG,YAAY,YAAY,CAACD,EAAIG,GAAG,yCAAyCH,EAAIG,GAAG,KAAKL,EAAG,MAAM,CAACG,YAAY,YAAY,CAACD,EAAIG,GAAG,iDAAiDH,EAAIG,GAAG,KAAKL,EAAG,MAAM,CAACG,YAAY,uBAAuB,CAACH,EAAG,MAAM,CAACG,YAAY,eAAe,CAACH,EAAG,KAAK,CAACG,YAAY,cAAc,CAACH,EAAG,IAAI,CAACI,MAAM,CAAC,cAAc,aAAa,cAAc,WAAW,KAAO,iBAAiB,CAACF,EAAIG,GAAG,gEAAgEH,EAAIG,GAAG,KAAKL,EAAG,MAAM,CAACG,YAAY,0BAA0BC,MAAM,CAAC,GAAK,gBAAgB,CAACJ,EAAG,MAAM,CAACG,YAAY,aAAa,CAACD,EAAIG,GAAG,gwBAAgwBH,EAAIG,GAAG,KAAKL,EAAG,MAAM,CAACG,YAAY,uBAAuB,CAACH,EAAG,MAAM,CAACG,YAAY,eAAe,CAACH,EAAG,KAAK,CAACG,YAAY,cAAc,CAACH,EAAG,IAAI,CAACI,MAAM,CAAC,cAAc,aAAa,cAAc,WAAW,KAAO,mBAAmB,CAACF,EAAIG,GAAG,iEAAiEH,EAAIG,GAAG,KAAKL,EAAG,MAAM,CAACG,YAAY,0BAA0BC,MAAM,CAAC,GAAK,kBAAkB,CAACJ,EAAG,MAAM,CAACG,YAAY,aAAa,CAACD,EAAIG,GAAG,ywBGU1/E,EACA,KACA,WACA,M,QCMFC,EAAQ,IAKR,IAAIC,EAAOD,EAAQ,IAEfE,EAAQ,GACZ,IAAIC,IAAI,CACIF,OACAG,OAFJ,SAEWC,GACH,OAAOA,EAAcC,EAAO,CAACJ,MAAOA,OAEzCK,OAAO,c","file":"/public/js/new-user/index.js","sourcesContent":["var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _vm._m(0)}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('div',{attrs:{\"id\":\"accordion\"}},[_c('div',{staticClass:\"card card-primary\"},[_c('div',{staticClass:\"card-header\"},[_c('h4',{staticClass:\"card-title\"},[_c('a',{attrs:{\"data-parent\":\"#accordion\",\"data-toggle\":\"collapse\",\"href\":\"#collapseOne\"}},[_vm._v(\"\\n Create new accounts\\n \")])])]),_vm._v(\" \"),_c('div',{staticClass:\"panel-collapse collapse show\",attrs:{\"id\":\"collapseOne\"}},[_c('div',{staticClass:\"card-body\"},[_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('p',[_vm._v(\"Explain\")])])]),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col-lg-4\"},[_vm._v(\"\\n A\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"col-lg-8\"},[_vm._v(\"\\n B\\n \")])])])])]),_vm._v(\" \"),_c('div',{staticClass:\"card card-secondary\"},[_c('div',{staticClass:\"card-header\"},[_c('h4',{staticClass:\"card-title\"},[_c('a',{attrs:{\"data-parent\":\"#accordion\",\"data-toggle\":\"collapse\",\"href\":\"#collapseTwo\"}},[_vm._v(\"\\n Collapsible Group Danger\\n \")])])]),_vm._v(\" \"),_c('div',{staticClass:\"panel-collapse collapse\",attrs:{\"id\":\"collapseTwo\"}},[_c('div',{staticClass:\"card-body\"},[_vm._v(\"\\n Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid.\\n 3\\n wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt\\n laborum\\n eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee\\n nulla\\n assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred\\n nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft\\n beer\\n farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus\\n labore sustainable VHS.\\n \")])])]),_vm._v(\" \"),_c('div',{staticClass:\"card card-secondary\"},[_c('div',{staticClass:\"card-header\"},[_c('h4',{staticClass:\"card-title\"},[_c('a',{attrs:{\"data-parent\":\"#accordion\",\"data-toggle\":\"collapse\",\"href\":\"#collapseThree\"}},[_vm._v(\"\\n Collapsible Group Success\\n \")])])]),_vm._v(\" \"),_c('div',{staticClass:\"panel-collapse collapse\",attrs:{\"id\":\"collapseThree\"}},[_c('div',{staticClass:\"card-body\"},[_vm._v(\"\\n Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid.\\n 3\\n wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt\\n laborum\\n eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee\\n nulla\\n assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred\\n nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft\\n beer\\n farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus\\n labore sustainable VHS.\\n \")])])])])])])}]\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Index.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Index.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n","import { render, staticRenderFns } from \"./Index.vue?vue&type=template&id=2d2bc9db&scoped=true&\"\nimport script from \"./Index.vue?vue&type=script&lang=js&\"\nexport * from \"./Index.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"2d2bc9db\",\n null\n \n)\n\nexport default component.exports","/*\n * index.js\n * Copyright (c) 2020 james@firefly-iii.org\n *\n * This file is part of Firefly III (https://github.com/firefly-iii).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see .\n */\n\nrequire('../../bootstrap');\n\nimport Index from \"../../components/new-user/Index\";\n\n// i18n\nlet i18n = require('../../i18n');\n\nlet props = {};\nnew Vue({\n i18n,\n render(createElement) {\n return createElement(Index, {props: props});\n }\n }).$mount('#newuser');\n"],"sourceRoot":""} \ No newline at end of file diff --git a/public/v2/js/register.js b/public/v2/js/register.js index c43fbdd00e..a71ebc7ab6 100755 --- a/public/v2/js/register.js +++ b/public/v2/js/register.js @@ -1,2 +1,2 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[7],{404:function(n,o,w){n.exports=w(405)},405:function(n,o,w){w(406)},406:function(n,o,w){window.$=window.jQuery=w(18)}},[[404,0,1]]]); +(window.webpackJsonp=window.webpackJsonp||[]).push([[7],{423:function(n,o,w){n.exports=w(424)},424:function(n,o,w){w(425)},425:function(n,o,w){window.$=window.jQuery=w(25)}},[[423,0,1]]]); //# sourceMappingURL=register.js.map \ No newline at end of file diff --git a/public/v2/js/transactions/create.js b/public/v2/js/transactions/create.js index 35688309ad..86e058581e 100755 --- a/public/v2/js/transactions/create.js +++ b/public/v2/js/transactions/create.js @@ -1,2 +1,2 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[8],{226:function(t,e,n){var a=n(403);"string"==typeof a&&(a=[[t.i,a,""]]);var r={hmr:!0,transform:void 0,insertInto:void 0};n(25)(a,r);a.locals&&(t.exports=a.locals)},294:function(t,e,n){t.exports=n(407)},402:function(t,e,n){"use strict";n(226)},403:function(t,e,n){(t.exports=n(24)(!1)).push([t.i,".vue-tags-input{max-width:100%!important;display:block}.ti-input,.vue-tags-input{width:100%;border-radius:.25rem}.ti-input{max-width:100%}.ti-new-tag-input{font-size:1rem}",""])},407:function(t,e,n){"use strict";n.r(e);var a=n(23),r=n(2),i=n(17),s=n(16);function o(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,a)}return n}function c(t){for(var e=1;e0,autocomplete:"off",name:"date[]",placeholder:t.localDate},domProps:{value:t.localDate},on:{submit:function(t){t.preventDefault()},input:function(e){e.target.composing||(t.localDate=e.target.value)}}}),t._v(" "),n("input",{directives:[{name:"model",rawName:"v-model",value:t.localTime,expression:"localTime"}],ref:"time",staticClass:"form-control",attrs:{type:"time",title:t.$t("firefly.time"),disabled:t.index>0,autocomplete:"off",name:"time[]",placeholder:t.localTime},domProps:{value:t.localTime},on:{submit:function(t){t.preventDefault()},input:function(e){e.target.composing||(t.localTime=e.target.value)}}})])])}),[],!1,null,"203ec282",null).exports;function w(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,a)}return n}function j(t){for(var e=1;e0?n("ul",{staticClass:"list-group"},t._l(t.value,(function(e){return n("li",{staticClass:"list-group-item"},[n("em",[t._v(t._s(t.getTextForLinkType(e.link_type_id)))]),t._v(" "),n("a",{attrs:{href:"./transaction/show/"+e.transaction_group_id}},[t._v(t._s(e.description))]),t._v(" "),"withdrawal"===e.type?n("span",[t._v("\n ("),n("span",{staticClass:"text-danger"},[t._v(t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(-1*parseFloat(e.amount))))]),t._v(")\n ")]):t._e(),t._v(" "),"deposit"===e.type?n("span",[t._v("\n ("),n("span",{staticClass:"text-success"},[t._v(t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(parseFloat(e.amount))))]),t._v(")\n ")]):t._e(),t._v(" "),"transfer"===e.type?n("span",[t._v("\n ("),n("span",{staticClass:"text-info"},[t._v(t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(parseFloat(e.amount))))]),t._v(")\n ")]):t._e(),t._v(" "),t._m(1,!0)])})),0):t._e(),t._v(" "),t.value.length>0?n("div",{staticClass:"form-text"},[t._m(2)]):t._e()])])]),t._v(" "),n("div",{staticClass:"modal",attrs:{tabindex:"-1",id:"linkModal"}},[n("div",{staticClass:"modal-dialog modal-lg"},[n("div",{staticClass:"modal-content"},[t._m(3),t._v(" "),n("div",{staticClass:"modal-body"},[n("div",{staticClass:"container-fluid"},[t._m(4),t._v(" "),n("div",{staticClass:"row"},[n("div",{staticClass:"col"},[n("form",{on:{submit:function(e){return e.preventDefault(),t.search(e)}}},[n("div",{staticClass:"input-group"},[n("input",{directives:[{name:"model",rawName:"v-model",value:t.query,expression:"query"}],staticClass:"form-control",attrs:{autocomplete:"off",maxlength:"255",type:"text",name:"search",id:"query",placeholder:"Search query"},domProps:{value:t.query},on:{input:function(e){e.target.composing||(t.query=e.target.value)}}}),t._v(" "),t._m(5)])])])]),t._v(" "),n("div",{staticClass:"row"},[n("div",{staticClass:"col"},[t.searching?n("span",[n("i",{staticClass:"fas fa-spinner fa-spin"})]):t._e(),t._v(" "),t.searchResults.length>0?n("h4",[t._v("Search results")]):t._e(),t._v(" "),t.searchResults.length>0?n("table",{staticClass:"table table-sm"},[t._m(6),t._v(" "),n("tbody",t._l(t.searchResults,(function(e){return n("tr",[n("td",[n("input",{directives:[{name:"model",rawName:"v-model",value:e.selected,expression:"result.selected"}],staticClass:"form-control",attrs:{type:"checkbox"},domProps:{checked:Array.isArray(e.selected)?t._i(e.selected,null)>-1:e.selected},on:{change:[function(n){var a=e.selected,r=n.target,i=!!r.checked;if(Array.isArray(a)){var s=t._i(a,null);r.checked?s<0&&t.$set(e,"selected",a.concat([null])):s>-1&&t.$set(e,"selected",a.slice(0,s).concat(a.slice(s+1)))}else t.$set(e,"selected",i)},function(e){return t.selectTransaction(e)}]}})]),t._v(" "),n("td",[n("select",{directives:[{name:"model",rawName:"v-model",value:e.link_type_id,expression:"result.link_type_id"}],staticClass:"form-control",on:{change:[function(n){var a=Array.prototype.filter.call(n.target.options,(function(t){return t.selected})).map((function(t){return"_value"in t?t._value:t.value}));t.$set(e,"link_type_id",n.target.multiple?a:a[0])},function(e){return t.selectLinkType(e)}]}},t._l(t.linkTypes,(function(e){return n("option",{attrs:{label:e.type},domProps:{value:e.id+"-"+e.direction}},[t._v(t._s(e.type)+"\n ")])})),0)]),t._v(" "),n("td",[n("a",{attrs:{href:"./transactions/show/"+e.transaction_group_id}},[t._v(t._s(e.description))]),t._v(" "),"withdrawal"===e.type?n("span",[t._v("\n ("),n("span",{staticClass:"text-danger"},[t._v(t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(-1*parseFloat(e.amount))))]),t._v(")\n ")]):t._e(),t._v(" "),"deposit"===e.type?n("span",[t._v("\n ("),n("span",{staticClass:"text-success"},[t._v(t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(parseFloat(e.amount))))]),t._v(")\n ")]):t._e(),t._v(" "),"transfer"===e.type?n("span",[t._v("\n ("),n("span",{staticClass:"text-info"},[t._v(t._s(Intl.NumberFormat(t.locale,{style:"currency",currency:e.currency_code}).format(parseFloat(e.amount))))]),t._v(")\n ")]):t._e(),t._v(" "),n("br"),t._v(" "),n("em",[n("a",{attrs:{href:"./accounts/show/"+e.source_id}},[t._v(t._s(e.source_name))]),t._v("\n →\n "),n("a",{attrs:{href:"./accounts/show/"+e.destination_id}},[t._v(t._s(e.destination_name))])])])])})),0)]):t._e()])])])]),t._v(" "),t._m(7)])])])])}),[function(){var t=this.$createElement,e=this._self._c||t;return e("button",{staticClass:"btn btn-default btn-xs",attrs:{"data-toggle":"modal","data-target":"#linkModal"}},[e("i",{staticClass:"fas fa-plus"}),this._v(" Add transaction link")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"btn-group btn-group-xs float-right"},[e("a",{staticClass:"btn btn-xs btn-default",attrs:{href:"#"}},[e("i",{staticClass:"far fa-edit"})]),this._v(" "),e("a",{staticClass:"btn btn-xs btn-danger",attrs:{href:"#"}},[e("i",{staticClass:"far fa-trash-alt"})])])},function(){var t=this.$createElement,e=this._self._c||t;return e("button",{staticClass:"btn btn-default",attrs:{"data-toggle":"modal","data-target":"#linkModal"}},[e("i",{staticClass:"fas fa-plus"})])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"modal-header"},[e("h5",{staticClass:"modal-title"},[this._v("Transaction thing dialog.")]),this._v(" "),e("button",{staticClass:"close",attrs:{type:"button","data-dismiss":"modal","aria-label":"Close"}},[e("span",{attrs:{"aria-hidden":"true"}},[this._v("×")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"row"},[e("div",{staticClass:"col"},[e("p",[this._v("\n Use this form to search for transactions you wish to link to this one. When in doubt, use "),e("code",[this._v("id:*")]),this._v(" where the ID is the number from\n the URL.\n ")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"input-group-append"},[e("button",{staticClass:"btn btn-default",attrs:{type:"submit"}},[e("i",{staticClass:"fas fa-search"}),this._v(" Search")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("thead",[e("tr",[e("th",{staticStyle:{width:"33%"},attrs:{colspan:"2"}},[this._v("Include?")]),this._v(" "),e("th",[this._v("Transaction")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"modal-footer"},[e("button",{staticClass:"btn btn-secondary",attrs:{type:"button","data-dismiss":"modal"}},[this._v("Close")])])}],!1,null,null,null).exports,be={name:"TransactionAttachments"},ye=Object(v.a)(be,(function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"form-group"},[e("div",{staticClass:"text-xs d-none d-lg-block d-xl-block"},[this._v("\n "+this._s(this.$t("firefly.attachments"))+"\n ")]),this._v(" "),e("div",{staticClass:"input-group"},[e("input",{staticClass:"form-control",attrs:{type:"file",multiple:"",name:"attachments[]",placeholder:this.$t("firefly.attachment")}})])])}),[],!1,null,"922408e0",null).exports;function _e(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,a)}return n}function ge(t){for(var e=1;e1?n("span",[t._v(t._s(t.$t("firefly.single_split"))+" "+t._s(a+1)+" / "+t._s(t.transactions.length))]):t._e()]),t._v(" "),t.transactions.length>1?n("div",{staticClass:"card-tools"},[n("button",{staticClass:"btn btn-xs btn-danger",attrs:{type:"button"},on:{click:function(e){return t.removeTransaction(a)}}},[n("i",{staticClass:"fa fa-trash"})])]):t._e()]),t._v(" "),n("div",{staticClass:"card-body"},[n("h4",[t._v(t._s(t.$t("firefly.basic_journal_information")))]),t._v(" "),n("div",{staticClass:"row"},[n("div",{staticClass:"col"},[t._v("\n Description:\n "),n("TransactionDescription",{attrs:{index:a},model:{value:e.description,callback:function(n){t.$set(e,"description",n)},expression:"transaction.description"}})],1)]),t._v(" "),n("div",{staticClass:"row"},[n("div",{staticClass:"col-xl-5 col-lg-5 col-md-10 col-sm-12 col-xs-12"},[n("TransactionAccount",{attrs:{direction:"source",index:a},model:{value:e.source_account,callback:function(n){t.$set(e,"source_account",n)},expression:"transaction.source_account"}})],1),t._v(" "),n("div",{staticClass:"col-xl-2 col-lg-2 col-md-2 col-sm-12 text-center d-none d-sm-block"},[n("SwitchAccount",{attrs:{index:a}})],1),t._v(" "),n("div",{staticClass:"col-xl-5 col-lg-5 col-md-12 col-sm-12 col-xs-12"},[n("TransactionAccount",{attrs:{direction:"destination",index:a},model:{value:e.destination_account,callback:function(n){t.$set(e,"destination_account",n)},expression:"transaction.destination_account"}})],1)]),t._v(" "),n("div",{staticClass:"row"},[n("div",{staticClass:"col-xl-5 col-lg-5 col-md-10 col-sm-12 col-xs-12"},[n("TransactionAmount",{attrs:{index:a}})],1),t._v(" "),n("div",{staticClass:"col-xl-2 col-lg-2 col-md-2 col-sm-12 text-center d-none d-sm-block"},[n("TransactionForeignCurrency",{attrs:{index:a}})],1),t._v(" "),n("div",{staticClass:"col-xl-5 col-lg-5 col-md-12 col-sm-12 col-xs-12"},[n("TransactionForeignAmount",{attrs:{index:a}})],1)]),t._v(" "),n("div",{staticClass:"row"},[n("div",{staticClass:"col-xl-5 col-lg-5 col-md-12 col-sm-12 col-xs-12"},[n("TransactionDate",{attrs:{index:a}})],1),t._v(" "),n("div",{staticClass:"col-xl-5 col-lg-5 col-md-12 col-sm-12 col-xs-12 offset-xl-2 offset-lg-2"},[n("TransactionCustomDates",{attrs:{index:a,"enabled-dates":t.customDateFields}})],1)]),t._v(" "),n("h4",[t._v(t._s(t.$t("firefly.transaction_journal_meta")))]),t._v(" "),n("div",{staticClass:"row"},[n("div",{staticClass:"col"},[n("TransactionBudget",{attrs:{index:a},model:{value:e.budget_id,callback:function(n){t.$set(e,"budget_id",n)},expression:"transaction.budget_id"}}),t._v(" "),n("TransactionCategory",{attrs:{index:a},model:{value:e.category,callback:function(n){t.$set(e,"category",n)},expression:"transaction.category"}})],1),t._v(" "),n("div",{staticClass:"col"},[n("TransactionBill",{attrs:{index:a},model:{value:e.bill_id,callback:function(n){t.$set(e,"bill_id",n)},expression:"transaction.bill_id"}}),t._v(" "),n("TransactionTags",{attrs:{index:a},model:{value:e.tags,callback:function(n){t.$set(e,"tags",n)},expression:"transaction.tags"}}),t._v(" "),n("TransactionPiggyBank",{attrs:{index:a},model:{value:e.piggy_bank_id,callback:function(n){t.$set(e,"piggy_bank_id",n)},expression:"transaction.piggy_bank_id"}})],1)]),t._v(" "),n("h4",[t._v(t._s(t.$t("firefly.transaction_journal_extra")))]),t._v(" "),n("div",{staticClass:"row"},[n("div",{staticClass:"col"},[n("TransactionInternalReference",{attrs:{index:a},model:{value:e.internal_reference,callback:function(n){t.$set(e,"internal_reference",n)},expression:"transaction.internal_reference"}}),t._v(" "),n("TransactionExternalUrl",{attrs:{index:a},model:{value:e.external_url,callback:function(n){t.$set(e,"external_url",n)},expression:"transaction.external_url"}}),t._v(" "),n("TransactionNotes",{attrs:{index:a},model:{value:e.notes,callback:function(n){t.$set(e,"notes",n)},expression:"transaction.notes"}})],1),t._v(" "),n("div",{staticClass:"col"},[n("TransactionAttachments",{attrs:{index:a},model:{value:e.attachments,callback:function(n){t.$set(e,"attachments",n)},expression:"transaction.attachments"}}),t._v(" "),n("TransactionLinks",{attrs:{index:a},model:{value:e.links,callback:function(n){t.$set(e,"links",n)},expression:"transaction.links"}})],1)])])])])])})),t._v(" "),n("div",{staticClass:"row"},[n("div",{staticClass:"col"},[n("button",{staticClass:"btn btn-primary",on:{click:t.addTransaction}},[t._v(t._s(t.$t("firefly.add_another_split")))])]),t._v(" "),n("div",{staticClass:"col"},[n("p",{staticClass:"float-right"},[n("button",{staticClass:"btn btn-success",attrs:{disabled:t.isSubmitting},on:{click:t.submitTransaction}},[t._v("Store transaction")]),t._v(" "),n("br")])])]),t._v(" "),t._m(0)],2)}),[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"row"},[e("div",{staticClass:"col float-right"},[e("p",{staticClass:"text-right"},[e("small",{staticClass:"text-muted"},[this._v("Create another another another "),e("input",{attrs:{type:"checkbox"}})]),e("br"),this._v(" "),e("small",{staticClass:"text-muted"},[this._v("Return here "),e("input",{attrs:{type:"checkbox"}})]),e("br")])])])}],!1,null,"7f815cd8",null).exports,Te=n(3),ke=n.n(Te);n(15),ke.a.config.productionTip=!1;var $e=n(19),De={};new ke.a({i18n:$e,store:a.a,render:function(t){return t(Pe,{props:De})},beforeCreate:function(){this.$store.commit("initialiseStore"),this.$store.dispatch("updateCurrencyPreference")}}).$mount("#transactions_create")}},[[294,0,1]]]); +(window.webpackJsonp=window.webpackJsonp||[]).push([[8],{304:function(t,e,i){t.exports=i(430)},430:function(t,e,i){"use strict";i.r(e);var n=i(17),s=i(3),a=i(39),r=i(22),o=i(40),c=i(38),d=i(7);function u(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,n)}return i}function l(t){for(var e=1;e0&&(t.group_title=this.groupTitle),this.transactions)this.transactions.hasOwnProperty(e)&&/^0$|^[1-9]\d*$/.test(e)&&e<=4294967294&&t.transactions.push(this.convertSplit(e,this.transactions[e]));return t.transactions.length>1&&""!==t.transactions[0].description&&(t.group_title=t.transactions[0].description),t.transactions.length>1&&(t=this.synchronizeAccounts(t)),t},synchronizeAccounts:function(t){for(var e in t.transactions)t.transactions.hasOwnProperty(e)&&/^0$|^[1-9]\d*$/.test(e)&&e<=4294967294&&("Transfer"===this.transactionType&&(t.transactions[e].source_name=null,t.transactions[e].destination_name=null,e>0&&(t.transactions[e].source_id=t.transactions[0].source_id,t.transactions[e].destination_id=t.transactions[0].destination_id)),"Deposit"===this.transactionType&&(t.transactions[e].destination_name=null,e>0&&(t.transactions[e].destination_id=t.transactions[0].destination_id)),"Withdrawal"===this.transactionType&&(t.transactions[e].source_name=null,e>0&&(t.transactions[e].source_id=t.transactions[0].source_id)));return t},switchAccounts:function(t){var e=this.transactions[t].source_account_id,i=this.transactions[t].source_account_name,n=this.transactions[t].source_account_type,s=this.transactions[t].destination_account_id,a=this.transactions[t].destination_account_name,r=this.transactions[t].destination_account_type;this.updateField({index:0,field:"source_account_id",value:s}),this.updateField({index:0,field:"source_account_name",value:a}),this.updateField({index:0,field:"source_account_type",value:r}),this.updateField({index:0,field:"destination_account_id",value:e}),this.updateField({index:0,field:"destination_account_name",value:i}),this.updateField({index:0,field:"destination_account_type",value:n}),this.calculateTransactionType(0)},convertSplit:function(t,e){var i,n,s,a,r="invalid";if(this.time instanceof Date&&!isNaN(this.time)&&this.date instanceof Date&&!isNaN(this.date)){var o=new Date(this.date);o.setHours(this.time.getHours()),o.setMinutes(this.time.getMinutes()),o.setSeconds(this.time.getSeconds()),r=Object(d.c)(o)}var c,u,l,p={description:e.description,date:r,type:this.transactionType,source_id:null!==(i=e.source_account_id)&&void 0!==i?i:null,source_name:null!==(n=e.source_account_name)&&void 0!==n?n:null,destination_id:null!==(s=e.destination_account_id)&&void 0!==s?s:null,destination_name:null!==(a=e.destination_account_name)&&void 0!==a?a:null,currency_id:e.currency_id,amount:e.amount,budget_id:e.budget_id,category_name:e.category,tags:e.tags,interest_date:e.interest_date,book_date:e.book_date,process_date:e.process_date,due_date:e.due_date,payment_date:e.payment_date,invoice_date:e.invoice_date,internal_reference:e.internal_reference,external_url:e.external_url,notes:e.notes,external_id:e.external_id,zoom_level:e.zoom_level,longitude:e.longitude,latitude:e.latitude,order:0,reconciled:!1};0!==e.piggy_bank_id&&(p.piggy_bank_id=e.piggy_bank_id),0!==e.bill_id&&(p.bill_id=e.bill_id),0!==e.foreign_currency_id&&""!==e.foreign_amount&&(p.foreign_currency_id=e.foreign_currency_id),""!==e.foreign_amount&&(p.foreign_amount=e.foreign_amount),c=this.transactionType?this.transactionType.toLowerCase():"any",u=this.transactions[0].source_account_type,l=this.transactions[0].destination_account_type,p.currency_id=e.source_account_currency_id,"any"===c&&["asset","Asset account","Loan","Debt","Mortgage"].includes(u)&&(c="withdrawal"),"any"===c&&["asset","Asset account","Loan","Debt","Mortgage"].includes(l)&&(c="deposit",p.currency_id=e.destination_account_currency_id),p.type=c;var _=[];for(var h in e.links)if(e.links.hasOwnProperty(h)&&/^0$|^[1-9]\d*$/.test(h)&&h<=4294967294){var f=e.links[h],m=f.link_type_id.split("-"),v="outward"===m[1]?0:parseInt(f.transaction_journal_id),y="inward"===m[1]?0:parseInt(f.transaction_journal_id),b={link_type_id:parseInt(m[0]),inward_id:v,outward_id:y};_.push(b)}return p.links=_,p},getAllowedOpposingTypes:function(){var t=this;axios.get("./api/v1/configuration/static/firefly.allowed_opposing_types").then((function(e){t.allowedOpposingTypes=e.data["firefly.allowed_opposing_types"]}))},getAccountToTransaction:function(){var t=this;axios.get("./api/v1/configuration/static/firefly.account_to_transaction").then((function(e){t.accountToTransaction=e.data["firefly.account_to_transaction"]}))},getCustomFields:function(){var t=this;axios.get("./api/v1/preferences/transaction_journal_optional_fields").then((function(e){t.customFields=e.data.data.attributes.data}))},setDestinationAllowedTypes:function(t){0!==t.length?this.destinationAllowedTypes=t:this.destinationAllowedTypes=this.defaultDestinationAllowedTypes},setSourceAllowedTypes:function(t){0!==t.length?this.sourceAllowedTypes=t:this.sourceAllowedTypes=this.defaultSourceAllowedTypes}})},v=i(1),y=Object(v.a)(m,(function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("div",[i("alert",{attrs:{message:t.errorMessage,type:"danger"}}),t._v(" "),i("alert",{attrs:{message:t.successMessage,type:"success"}}),t._v(" "),i("SplitPills",{attrs:{transactions:t.transactions}}),t._v(" "),i("div",{staticClass:"tab-content"},t._l(this.transactions,(function(e,n){return i("SplitForm",{key:n,attrs:{"allowed-opposing-types":t.allowedOpposingTypes,count:t.transactions.length,"custom-fields":t.customFields,date:t.date,"destination-allowed-types":t.destinationAllowedTypes,index:n,"source-allowed-types":t.sourceAllowedTypes,"submitted-transaction":t.submittedTransaction,time:t.time,transaction:e,"transaction-type":t.transactionType},on:{"uploaded-attachments":function(e){return t.uploadedAttachment(e)},"set-marker-location":function(e){return t.storeLocation(e)},"set-account":function(e){return t.storeAccountValue(e)},"switch-accounts":function(e){return t.switchAccounts(e)},"set-date":function(e){return t.storeDate(e)},"set-time":function(e){return t.storeTime(e)},"set-field":function(e){return t.storeField(e)},"remove-transaction":function(e){return t.removeTransaction(e)},"set-dest-types":function(e){return t.setDestinationAllowedTypes(e)},"set-src-types":function(e){return t.setSourceAllowedTypes(e)}}})})),1),t._v(" "),i("div",{staticClass:"row"},[i("div",{staticClass:"col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12"},[t.transactions.length>1?i("div",{staticClass:"card"},[i("div",{staticClass:"card-body"},[i("div",{staticClass:"row"},[i("div",{staticClass:"col"},[i("TransactionGroupTitle",{attrs:{errors:this.groupTitleErrors},on:{"set-group-title":function(e){return t.storeGroupTitle(e)}},model:{value:this.groupTitle,callback:function(e){t.$set(this,"groupTitle",e)},expression:"this.groupTitle"}})],1)])])]):t._e()]),t._v(" "),i("div",{staticClass:"col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12"},[i("div",{staticClass:"card card-primary"},[i("div",{staticClass:"card-body"},[i("div",{staticClass:"row"},[i("div",{staticClass:"col"},[i("div",{staticClass:"text-xs d-none d-lg-block d-xl-block"},[t._v("\n  \n ")]),t._v(" "),i("button",{staticClass:"btn btn-outline-primary btn-block",on:{click:t.addTransaction}},[i("i",{staticClass:"far fa-clone"}),t._v(" "+t._s(t.$t("firefly.add_another_split"))+"\n ")])]),t._v(" "),i("div",{staticClass:"col"},[i("div",{staticClass:"text-xs d-none d-lg-block d-xl-block"},[t._v("\n  \n ")]),t._v(" "),i("button",{staticClass:"btn btn-success btn-block",attrs:{disabled:!t.enableSubmit},on:{click:t.submitTransaction}},[t.enableSubmit?i("span",[i("i",{staticClass:"far fa-save"}),t._v(" "+t._s(t.$t("firefly.store_transaction")))]):t._e(),t._v(" "),t.enableSubmit?t._e():i("span",[i("i",{staticClass:"fas fa-spinner fa-spin"})])])])]),t._v(" "),i("div",{staticClass:"row"},[i("div",{staticClass:"col"},[t._v("\n  \n ")]),t._v(" "),i("div",{staticClass:"col"},[i("div",{staticClass:"form-check"},[i("input",{directives:[{name:"model",rawName:"v-model",value:t.createAnother,expression:"createAnother"}],staticClass:"form-check-input",attrs:{id:"createAnother",type:"checkbox"},domProps:{checked:Array.isArray(t.createAnother)?t._i(t.createAnother,null)>-1:t.createAnother},on:{change:function(e){var i=t.createAnother,n=e.target,s=!!n.checked;if(Array.isArray(i)){var a=t._i(i,null);n.checked?a<0&&(t.createAnother=i.concat([null])):a>-1&&(t.createAnother=i.slice(0,a).concat(i.slice(a+1)))}else t.createAnother=s}}}),t._v(" "),i("label",{staticClass:"form-check-label",attrs:{for:"createAnother"}},[i("span",{staticClass:"small"},[t._v(t._s(t.$t("firefly.create_another")))])])]),t._v(" "),i("div",{staticClass:"form-check"},[i("input",{directives:[{name:"model",rawName:"v-model",value:t.resetFormAfter,expression:"resetFormAfter"}],staticClass:"form-check-input",attrs:{id:"resetFormAfter",disabled:!t.createAnother,type:"checkbox"},domProps:{checked:Array.isArray(t.resetFormAfter)?t._i(t.resetFormAfter,null)>-1:t.resetFormAfter},on:{change:function(e){var i=t.resetFormAfter,n=e.target,s=!!n.checked;if(Array.isArray(i)){var a=t._i(i,null);n.checked?a<0&&(t.resetFormAfter=i.concat([null])):a>-1&&(t.resetFormAfter=i.slice(0,a).concat(i.slice(a+1)))}else t.resetFormAfter=s}}}),t._v(" "),i("label",{staticClass:"form-check-label",attrs:{for:"resetFormAfter"}},[i("span",{staticClass:"small"},[t._v(t._s(t.$t("firefly.reset_after")))])])])])])])])])])],1)}),[],!1,null,"64509782",null).exports,b=i(2),g=i.n(b);i(16),g.a.config.productionTip=!1;var T=i(19),A={};new g.a({i18n:T,store:n.a,render:function(t){return t(y,{props:A})},beforeCreate:function(){this.$store.commit("initialiseStore"),this.$store.dispatch("updateCurrencyPreference")}}).$mount("#transactions_create")}},[[304,0,1]]]); //# sourceMappingURL=create.js.map \ No newline at end of file diff --git a/public/v2/js/transactions/create.js.map b/public/v2/js/transactions/create.js.map index ec134880c9..5cba31b7ba 100755 --- a/public/v2/js/transactions/create.js.map +++ b/public/v2/js/transactions/create.js.map @@ -1 +1 @@ -{"version":3,"sources":["webpack:///./src/components/transactions/TransactionTags.vue?f6be","webpack:///./src/components/transactions/TransactionTags.vue?7125","webpack:///./src/components/transactions/TransactionTags.vue?9c61","webpack:///src/components/transactions/TransactionDescription.vue","webpack:///./src/components/transactions/TransactionDescription.vue?e9b4","webpack:///./src/components/transactions/TransactionDescription.vue","webpack:///./src/components/transactions/TransactionDescription.vue?3ec0","webpack:///src/components/transactions/TransactionDate.vue","webpack:///./src/components/transactions/TransactionDate.vue?cd54","webpack:///./src/components/transactions/TransactionDate.vue","webpack:///./src/components/transactions/TransactionDate.vue?b108","webpack:///src/components/transactions/TransactionBudget.vue","webpack:///./src/components/transactions/TransactionBudget.vue?582a","webpack:///./src/components/transactions/TransactionBudget.vue","webpack:///./src/components/transactions/TransactionBudget.vue?4263","webpack:///src/components/transactions/TransactionAccount.vue","webpack:///./src/components/transactions/TransactionAccount.vue?99df","webpack:///./src/components/transactions/TransactionAccount.vue","webpack:///./src/components/transactions/TransactionAccount.vue?9154","webpack:///src/components/transactions/SwitchAccount.vue","webpack:///./src/components/transactions/SwitchAccount.vue?76a5","webpack:///./src/components/transactions/SwitchAccount.vue","webpack:///./src/components/transactions/SwitchAccount.vue?a5fd","webpack:///src/components/transactions/TransactionAmount.vue","webpack:///./src/components/transactions/TransactionAmount.vue?2029","webpack:///./src/components/transactions/TransactionAmount.vue","webpack:///./src/components/transactions/TransactionAmount.vue?eb71","webpack:///src/components/transactions/TransactionForeignAmount.vue","webpack:///./src/components/transactions/TransactionForeignAmount.vue?a3d5","webpack:///./src/components/transactions/TransactionForeignAmount.vue","webpack:///./src/components/transactions/TransactionForeignAmount.vue?fc0b","webpack:///src/components/transactions/TransactionForeignCurrency.vue","webpack:///./src/components/transactions/TransactionForeignCurrency.vue?7457","webpack:///./src/components/transactions/TransactionForeignCurrency.vue","webpack:///./src/components/transactions/TransactionForeignCurrency.vue?5374","webpack:///src/components/transactions/TransactionCustomDates.vue","webpack:///./src/components/transactions/TransactionCustomDates.vue?a208","webpack:///./src/components/transactions/TransactionCustomDates.vue","webpack:///./src/components/transactions/TransactionCustomDates.vue?8e03","webpack:///src/components/transactions/TransactionCategory.vue","webpack:///./src/components/transactions/TransactionCategory.vue?7292","webpack:///./src/components/transactions/TransactionCategory.vue","webpack:///./src/components/transactions/TransactionCategory.vue?b017","webpack:///src/components/transactions/TransactionBill.vue","webpack:///./src/components/transactions/TransactionBill.vue?5529","webpack:///./src/components/transactions/TransactionBill.vue","webpack:///./src/components/transactions/TransactionBill.vue?e4f6","webpack:///src/components/transactions/TransactionTags.vue","webpack:///./src/components/transactions/TransactionTags.vue?0617","webpack:///./src/components/transactions/TransactionTags.vue","webpack:///./src/components/transactions/TransactionTags.vue?eaac","webpack:///src/components/transactions/TransactionPiggyBank.vue","webpack:///./src/components/transactions/TransactionPiggyBank.vue?183b","webpack:///./src/components/transactions/TransactionPiggyBank.vue","webpack:///./src/components/transactions/TransactionPiggyBank.vue?b238","webpack:///src/components/transactions/TransactionInternalReference.vue","webpack:///./src/components/transactions/TransactionInternalReference.vue?111c","webpack:///./src/components/transactions/TransactionInternalReference.vue","webpack:///./src/components/transactions/TransactionInternalReference.vue?7420","webpack:///src/components/transactions/TransactionExternalUrl.vue","webpack:///./src/components/transactions/TransactionExternalUrl.vue?1580","webpack:///./src/components/transactions/TransactionExternalUrl.vue","webpack:///./src/components/transactions/TransactionExternalUrl.vue?9794","webpack:///src/components/transactions/TransactionNotes.vue","webpack:///./src/components/transactions/TransactionNotes.vue?2e0f","webpack:///./src/components/transactions/TransactionNotes.vue","webpack:///./src/components/transactions/TransactionNotes.vue?52c7","webpack:///./src/components/transactions/TransactionLinks.vue?48a5","webpack:///src/components/transactions/TransactionLinks.vue","webpack:///./src/components/transactions/TransactionLinks.vue","webpack:///./src/components/transactions/TransactionLinks.vue?b2ae","webpack:///./src/components/transactions/TransactionAttachments.vue?11dd","webpack:///src/components/transactions/TransactionAttachments.vue","webpack:///./src/components/transactions/TransactionAttachments.vue","webpack:///./src/components/transactions/TransactionAttachments.vue?8705","webpack:///src/components/transactions/Create.vue","webpack:///./src/components/transactions/Create.vue?0a62","webpack:///./src/components/transactions/Create.vue","webpack:///./src/components/transactions/Create.vue?2044","webpack:///./src/pages/transactions/create.js"],"names":["content","module","i","options","transform","undefined","locals","exports","push","props","components","name","data","descriptions","initialSet","created","axios","get","this","getACURL","methods","clearDescription","value","document","getElementsByTagName","href","query","lookupDescription","watch","updateField","computed","_vm","_h","$createElement","_c","_self","staticClass","_v","_s","$t","attrs","item","description","on","model","callback","$$v","expression","slot","localDate","date","toISOString","split","set","current","setFullYear","newDate","getFullYear","setMonth","getMonth","setDate","getDate","localTime","getHours","slice","getMinutes","getSeconds","setHours","parseInt","parts","setMinutes","setSeconds","directives","rawName","ref","index","domProps","$event","preventDefault","target","composing","budgetList","collectData","getBudgets","parseBudgets","hasOwnProperty","key","test","$$selectedVal","Array","prototype","filter","call","o","selected","map","_value","multiple","_l","budget","id","accounts","accountTypes","selectedAccount","createInitialSet","types","join","clearAccount","lookupAccount","length","direction","sourceAllowedTypes","destinationAllowedTypes","name_with_balance","allowedOpposingTypes","type","opposingAccounts","setDestinationAllowedTypes","setSourceAllowedTypes","calcTransactionType","accountKey","$set","switchAccounts","transactionType","_e","currencySymbol","currencyId","transactions","source_account","currency_id","currency_symbol","destination_account","indexOf","updateCurrency","currencyPreference","symbol","$store","state","amount","selectedTransactionType","allCurrencies","selectableCurrencies","foreign_amount","foreign_currency_id","lockedCurrency","selectIsVisible","filterCurrencies","checkVisibility","getAllCurrencies","sourceId","destId","normalCurrencyId","currency","getFieldValue","setFieldValue","enabled","refInFor","categories","clearCategory","lookupCategory","selectedCategory","billList","getBills","parseBills","bill","VueTagsInput","autocompleteItems","debounce","tags","currentTag","updateTags","console","log","shortList","initItems","clearTimeout","setTimeout","this$1","newTags","piggyList","getPiggies","parsePiggies","piggy","_m","searchResults","include","locale","linkTypes","searching","getLinkTypes","getTextForLinkType","linkTypeId","selectTransaction","addToSelected","removeFromSelected","selectLinkType","updateSelected","transaction_journal_id","link_type_id","journalId","journal","splice","parseLinkTypes","attributes","inward","outward","linkTypeInward","linkTypeOutward","search","url","parseSearch","ii","transaction_group_id","isJournalSelected","getJournalLinkType","link_type_text","transaction","Intl","NumberFormat","style","currency_code","format","parseFloat","result","isArray","_i","$$a","$$el","$$c","checked","$$i","concat","linkType","source_id","source_name","destination_id","destination_name","staticStyle","TransactionAttachments","TransactionNotes","TransactionExternalUrl","TransactionInternalReference","TransactionPiggyBank","TransactionTags","TransactionLinks","TransactionBill","TransactionCategory","TransactionCustomDates","TransactionForeignCurrency","TransactionForeignAmount","storeAllowedOpposingTypes","storeAccountToTransaction","storeCustomDateFields","addTransaction","groupTitle","isSubmitting","linkSearchResults","removeTransaction","commit","then","interest_date","book_date","process_date","due_date","payment_date","invoice_date","fields","allDateFields","selectedDateFields","setAllowedOpposingTypes","window","setAccountToTransaction","accountToTransaction","submitTransaction","convertData","convertSplit","array","time","trim","budget_id","category","bill_id","piggy_bank_id","internal_reference","external_url","notes","links","currentSplit","customDateFields","require","Vue","config","productionTip","i18n","store","render","createElement","Create","beforeCreate","dispatch","$mount"],"mappings":"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","file":"/public/js/transactions/create.js","sourcesContent":["\nvar content = require(\"!!../../../node_modules/css-loader/index.js??ref--6-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src/index.js??ref--6-2!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionTags.vue?vue&type=style&index=0&lang=css&\");\n\nif(typeof content === 'string') content = [[module.id, content, '']];\n\nvar transform;\nvar insertInto;\n\n\n\nvar options = {\"hmr\":true}\n\noptions.transform = transform\noptions.insertInto = undefined;\n\nvar update = require(\"!../../../node_modules/style-loader/lib/addStyles.js\")(content, options);\n\nif(content.locals) module.exports = content.locals;\n\nif(module.hot) {\n\tmodule.hot.accept(\"!!../../../node_modules/css-loader/index.js??ref--6-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src/index.js??ref--6-2!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionTags.vue?vue&type=style&index=0&lang=css&\", function() {\n\t\tvar newContent = require(\"!!../../../node_modules/css-loader/index.js??ref--6-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src/index.js??ref--6-2!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionTags.vue?vue&type=style&index=0&lang=css&\");\n\n\t\tif(typeof newContent === 'string') newContent = [[module.id, newContent, '']];\n\n\t\tvar locals = (function(a, b) {\n\t\t\tvar key, idx = 0;\n\n\t\t\tfor(key in a) {\n\t\t\t\tif(!b || a[key] !== b[key]) return false;\n\t\t\t\tidx++;\n\t\t\t}\n\n\t\t\tfor(key in b) idx--;\n\n\t\t\treturn idx === 0;\n\t\t}(content.locals, newContent.locals));\n\n\t\tif(!locals) throw new Error('Aborting CSS HMR due to changed css-modules locals.');\n\n\t\tupdate(newContent);\n\t});\n\n\tmodule.hot.dispose(function() { update(); });\n}","export * from \"-!../../../node_modules/style-loader/index.js!../../../node_modules/css-loader/index.js??ref--6-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src/index.js??ref--6-2!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionTags.vue?vue&type=style&index=0&lang=css&\"","exports = module.exports = require(\"../../../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \".vue-tags-input{max-width:100%!important;display:block}.ti-input,.vue-tags-input{width:100%;border-radius:.25rem}.ti-input{max-width:100%}.ti-new-tag-input{font-size:1rem}\", \"\"]);\n\n// exports\n","\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionDescription.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionDescription.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./TransactionDescription.vue?vue&type=template&id=19697848&\"\nimport script from \"./TransactionDescription.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionDescription.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"text-xs d-none d-lg-block d-xl-block\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('firefly.description'))+\"\\n \")]),_vm._v(\" \"),_c('vue-typeahead-bootstrap',{attrs:{\"inputName\":\"description[]\",\"data\":_vm.descriptions,\"placeholder\":_vm.$t('firefly.description'),\"showOnFocus\":true,\"autofocus\":\"\",\"minMatchingChars\":3,\"serializer\":function (item) { return item.description; }},on:{\"input\":_vm.lookupDescription},model:{value:(_vm.value),callback:function ($$v) {_vm.value=$$v},expression:\"value\"}},[_c('template',{slot:\"append\"},[_c('div',{staticClass:\"input-group-append\"},[_c('button',{staticClass:\"btn btn-outline-secondary\",attrs:{\"type\":\"button\"},on:{\"click\":_vm.clearDescription}},[_c('i',{staticClass:\"far fa-trash-alt\"})])])])],2)],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionDate.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionDate.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./TransactionDate.vue?vue&type=template&id=203ec282&scoped=true&\"\nimport script from \"./TransactionDate.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionDate.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"203ec282\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"text-xs d-none d-lg-block d-xl-block\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('firefly.date_and_time'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"input-group\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.localDate),expression:\"localDate\"}],ref:\"date\",staticClass:\"form-control\",attrs:{\"type\":\"date\",\"title\":_vm.$t('firefly.date'),\"disabled\":_vm.index > 0,\"autocomplete\":\"off\",\"name\":\"date[]\",\"placeholder\":_vm.localDate},domProps:{\"value\":(_vm.localDate)},on:{\"submit\":function($event){$event.preventDefault();},\"input\":function($event){if($event.target.composing){ return; }_vm.localDate=$event.target.value}}}),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.localTime),expression:\"localTime\"}],ref:\"time\",staticClass:\"form-control\",attrs:{\"type\":\"time\",\"title\":_vm.$t('firefly.time'),\"disabled\":_vm.index > 0,\"autocomplete\":\"off\",\"name\":\"time[]\",\"placeholder\":_vm.localTime},domProps:{\"value\":(_vm.localTime)},on:{\"submit\":function($event){$event.preventDefault();},\"input\":function($event){if($event.target.composing){ return; }_vm.localTime=$event.target.value}}})])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionBudget.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionBudget.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./TransactionBudget.vue?vue&type=template&id=018c5281&\"\nimport script from \"./TransactionBudget.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionBudget.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"text-xs d-none d-lg-block d-xl-block\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('firefly.budget'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"input-group\"},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.value),expression:\"value\"}],ref:\"budget\",staticClass:\"form-control\",attrs:{\"title\":_vm.$t('firefly.budget'),\"autocomplete\":\"off\",\"name\":\"budget_id[]\"},on:{\"submit\":function($event){$event.preventDefault();},\"change\":function($event){var $$selectedVal = Array.prototype.filter.call($event.target.options,function(o){return o.selected}).map(function(o){var val = \"_value\" in o ? o._value : o.value;return val}); _vm.value=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},_vm._l((this.budgetList),function(budget){return _c('option',{attrs:{\"label\":budget.name},domProps:{\"value\":budget.id}},[_vm._v(_vm._s(budget.name))])}),0)])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionAccount.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionAccount.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./TransactionAccount.vue?vue&type=template&id=4f219d2d&scoped=true&\"\nimport script from \"./TransactionAccount.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionAccount.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"4f219d2d\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"text-xs d-none d-lg-block d-xl-block\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('firefly.' + this.direction + '_account'))+\"\\n \")]),_vm._v(\" \"),_c('vue-typeahead-bootstrap',{attrs:{\"data\":_vm.accounts,\"showOnFocus\":true,\"inputName\":_vm.direction + '[]',\"serializer\":function (item) { return item.name_with_balance; },\"minMatchingChars\":3,\"placeholder\":_vm.$t('firefly.' + this.direction + '_account')},on:{\"input\":_vm.lookupAccount,\"hit\":function($event){_vm.selectedAccount = $event}},model:{value:(_vm.value.name),callback:function ($$v) {_vm.$set(_vm.value, \"name\", $$v)},expression:\"value.name\"}},[_c('template',{slot:\"append\"},[_c('div',{staticClass:\"input-group-append\"},[_c('button',{staticClass:\"btn btn-outline-secondary\",attrs:{\"type\":\"button\"},on:{\"click\":_vm.clearAccount}},[_c('i',{staticClass:\"far fa-trash-alt\"})])])])],2)],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./SwitchAccount.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./SwitchAccount.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./SwitchAccount.vue?vue&type=template&id=7fc33f88&scoped=true&\"\nimport script from \"./SwitchAccount.vue?vue&type=script&lang=js&\"\nexport * from \"./SwitchAccount.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"7fc33f88\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"text-xs d-none d-lg-block d-xl-block\"},[('any' !== this.transactionType)?_c('span',{staticClass:\"text-muted\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('firefly.' + this.transactionType))+\"\\n \")]):_vm._e(),_vm._v(\" \"),('any' === this.transactionType)?_c('span',{staticClass:\"text-muted\"},[_vm._v(\" \")]):_vm._e()]),_vm._v(\" \"),_c('div',{staticClass:\"btn-group d-flex\"},[_c('button',{staticClass:\"btn btn-light\",on:{\"click\":_vm.switchAccounts}},[_vm._v(\"↔\")])])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionAmount.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionAmount.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./TransactionAmount.vue?vue&type=template&id=a9ad4872&scoped=true&\"\nimport script from \"./TransactionAmount.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionAmount.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"a9ad4872\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"text-xs\"},[_vm._v(_vm._s(_vm.$t('firefly.amount')))]),_vm._v(\" \"),_c('div',{staticClass:\"input-group\"},[_c('div',{staticClass:\"input-group-prepend\"},[_c('div',{staticClass:\"input-group-text\"},[_vm._v(_vm._s(_vm.currencySymbol))])]),_vm._v(\" \"),_c('input',{attrs:{\"type\":\"hidden\",\"name\":\"currency_id[]\"},domProps:{\"value\":_vm.currencyId}}),_vm._v(\" \"),_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.amount),expression:\"amount\"}],staticClass:\"form-control\",attrs:{\"title\":_vm.$t('firefly.amount'),\"autocomplete\":\"off\",\"name\":\"amount[]\",\"type\":\"number\",\"placeholder\":_vm.$t('firefly.amount')},domProps:{\"value\":(_vm.amount)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.amount=$event.target.value}}})])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionForeignAmount.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionForeignAmount.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./TransactionForeignAmount.vue?vue&type=template&id=422824c7&scoped=true&\"\nimport script from \"./TransactionForeignAmount.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionForeignAmount.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"422824c7\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"form-group\"},[_c('input',{attrs:{\"type\":\"hidden\",\"name\":\"foreign_currency_id[]\"},domProps:{\"value\":_vm.currencyId}}),_vm._v(\" \"),_c('div',{staticClass:\"text-xs\"},[_vm._v(_vm._s(_vm.$t('form.foreign_amount')))]),_vm._v(\" \"),_c('div',{staticClass:\"input-group\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.amount),expression:\"amount\"}],staticClass:\"form-control\",attrs:{\"title\":_vm.$t('form.foreign_amount'),\"autocomplete\":\"off\",\"disabled\":0===_vm.currencyId,\"name\":\"foreign_amount[]\",\"type\":\"number\",\"placeholder\":_vm.$t('form.foreign_amount')},domProps:{\"value\":(_vm.amount)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.amount=$event.target.value}}})])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionForeignCurrency.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionForeignCurrency.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./TransactionForeignCurrency.vue?vue&type=template&id=7008e22e&scoped=true&\"\nimport script from \"./TransactionForeignCurrency.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionForeignCurrency.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"7008e22e\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.selectIsVisible)?_c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"text-xs\"},[_vm._v(\" \")]),_vm._v(\" \"),_c('div',{staticClass:\"input-group\"},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.currencyId),expression:\"currencyId\"}],staticClass:\"form-control\",attrs:{\"name\":\"foreign_currency_id[]\"},on:{\"change\":function($event){var $$selectedVal = Array.prototype.filter.call($event.target.options,function(o){return o.selected}).map(function(o){var val = \"_value\" in o ? o._value : o.value;return val}); _vm.currencyId=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},_vm._l((_vm.selectableCurrencies),function(currency){return _c('option',{attrs:{\"label\":currency.name},domProps:{\"value\":currency.id}},[_vm._v(_vm._s(currency.name))])}),0)])]):_vm._e()}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionCustomDates.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionCustomDates.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./TransactionCustomDates.vue?vue&type=template&id=7d877503&scoped=true&\"\nimport script from \"./TransactionCustomDates.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionCustomDates.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"7d877503\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',_vm._l((_vm.enabledDates),function(enabled,name){return _c('div',{staticClass:\"form-group\"},[(enabled)?_c('div',{staticClass:\"text-xs d-none d-lg-block d-xl-block\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('form.' + name))+\"\\n \")]):_vm._e(),_vm._v(\" \"),(enabled)?_c('div',{staticClass:\"input-group\"},[_c('input',{ref:name,refInFor:true,staticClass:\"form-control\",attrs:{\"type\":\"date\",\"title\":_vm.$t('form.' + name),\"autocomplete\":\"off\",\"name\":name + '[]',\"placeholder\":_vm.$t('form.' + name)},domProps:{\"value\":_vm.getFieldValue(name)},on:{\"change\":function($event){return _vm.setFieldValue($event, name)},\"submit\":function($event){$event.preventDefault();}}})]):_vm._e()])}),0)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionCategory.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionCategory.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./TransactionCategory.vue?vue&type=template&id=e6a00688&\"\nimport script from \"./TransactionCategory.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionCategory.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"text-xs d-none d-lg-block d-xl-block\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('firefly.category'))+\"\\n \")]),_vm._v(\" \"),_c('vue-typeahead-bootstrap',{attrs:{\"inputName\":\"category[]\",\"data\":_vm.categories,\"placeholder\":_vm.$t('firefly.category'),\"showOnFocus\":true,\"minMatchingChars\":3,\"serializer\":function (item) { return item.name; }},on:{\"hit\":function($event){_vm.selectedCategory = $event},\"input\":_vm.lookupCategory},model:{value:(_vm.value),callback:function ($$v) {_vm.value=$$v},expression:\"value\"}},[_c('template',{slot:\"append\"},[_c('div',{staticClass:\"input-group-append\"},[_c('button',{staticClass:\"btn btn-outline-secondary\",attrs:{\"type\":\"button\"},on:{\"click\":_vm.clearCategory}},[_c('i',{staticClass:\"far fa-trash-alt\"})])])])],2)],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionBill.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionBill.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./TransactionBill.vue?vue&type=template&id=1b15fb48&\"\nimport script from \"./TransactionBill.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionBill.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"text-xs d-none d-lg-block d-xl-block\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('firefly.bill'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"input-group\"},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.value),expression:\"value\"}],ref:\"bill\",staticClass:\"form-control\",attrs:{\"title\":_vm.$t('firefly.bill'),\"autocomplete\":\"off\",\"name\":\"bill_id[]\"},on:{\"submit\":function($event){$event.preventDefault();},\"change\":function($event){var $$selectedVal = Array.prototype.filter.call($event.target.options,function(o){return o.selected}).map(function(o){var val = \"_value\" in o ? o._value : o.value;return val}); _vm.value=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},_vm._l((this.billList),function(bill){return _c('option',{attrs:{\"label\":bill.name},domProps:{\"value\":bill.id}},[_vm._v(_vm._s(bill.name))])}),0)])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionTags.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionTags.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./TransactionTags.vue?vue&type=template&id=554cc465&\"\nimport script from \"./TransactionTags.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionTags.vue?vue&type=script&lang=js&\"\nimport style0 from \"./TransactionTags.vue?vue&type=style&index=0&lang=css&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {\nvar this$1 = this;\nvar _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"text-xs d-none d-lg-block d-xl-block\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('firefly.tags'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"input-group\"},[_c('vue-tags-input',{attrs:{\"add-only-from-autocomplete\":false,\"autocomplete-items\":_vm.autocompleteItems,\"tags\":_vm.tags,\"title\":_vm.$t('firefly.tags'),\"placeholder\":_vm.$t('firefly.tags')},on:{\"tags-changed\":function (newTags) { return this$1.tags = newTags; }},model:{value:(_vm.currentTag),callback:function ($$v) {_vm.currentTag=$$v},expression:\"currentTag\"}})],1)])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionPiggyBank.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionPiggyBank.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./TransactionPiggyBank.vue?vue&type=template&id=86f479a6&\"\nimport script from \"./TransactionPiggyBank.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionPiggyBank.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"text-xs d-none d-lg-block d-xl-block\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('firefly.piggy_bank'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"input-group\"},[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.value),expression:\"value\"}],ref:\"piggy_bank_id\",staticClass:\"form-control\",attrs:{\"title\":_vm.$t('firefly.piggy_bank'),\"autocomplete\":\"off\",\"name\":\"piggy_bank_id[]\"},on:{\"submit\":function($event){$event.preventDefault();},\"change\":function($event){var $$selectedVal = Array.prototype.filter.call($event.target.options,function(o){return o.selected}).map(function(o){var val = \"_value\" in o ? o._value : o.value;return val}); _vm.value=$event.target.multiple ? $$selectedVal : $$selectedVal[0]}}},_vm._l((this.piggyList),function(piggy){return _c('option',{attrs:{\"label\":piggy.name_with_balance},domProps:{\"value\":piggy.id}},[_vm._v(_vm._s(piggy.name_with_balance))])}),0)])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionInternalReference.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionInternalReference.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./TransactionInternalReference.vue?vue&type=template&id=4acf7450&scoped=true&\"\nimport script from \"./TransactionInternalReference.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionInternalReference.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"4acf7450\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"text-xs d-none d-lg-block d-xl-block\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('firefly.internal_reference'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"input-group\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.value),expression:\"value\"}],staticClass:\"form-control\",attrs:{\"type\":\"text\",\"name\":\"internal_reference[]\",\"placeholder\":_vm.$t('firefly.internal_reference')},domProps:{\"value\":(_vm.value)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.value=$event.target.value}}}),_vm._v(\" \"),_vm._m(0)])])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"input-group-append\"},[_c('button',{staticClass:\"btn btn-outline-secondary\",attrs:{\"type\":\"button\"}},[_c('i',{staticClass:\"far fa-trash-alt\"})])])}]\n\nexport { render, staticRenderFns }","\n\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionExternalUrl.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionExternalUrl.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./TransactionExternalUrl.vue?vue&type=template&id=30ba9330&scoped=true&\"\nimport script from \"./TransactionExternalUrl.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionExternalUrl.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"30ba9330\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"text-xs d-none d-lg-block d-xl-block\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('firefly.external_url'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"input-group\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.value),expression:\"value\"}],staticClass:\"form-control\",attrs:{\"type\":\"url\",\"name\":\"external_url[]\",\"placeholder\":_vm.$t('firefly.external_url')},domProps:{\"value\":(_vm.value)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.value=$event.target.value}}}),_vm._v(\" \"),_vm._m(0)])])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"input-group-append\"},[_c('button',{staticClass:\"btn btn-outline-secondary\",attrs:{\"type\":\"button\"}},[_c('i',{staticClass:\"far fa-trash-alt\"})])])}]\n\nexport { render, staticRenderFns }","\n\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionNotes.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionNotes.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./TransactionNotes.vue?vue&type=template&id=117d7865&scoped=true&\"\nimport script from \"./TransactionNotes.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionNotes.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"117d7865\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"text-xs d-none d-lg-block d-xl-block\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('firefly.notes'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"input-group\"},[_c('textarea',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.value),expression:\"value\"}],staticClass:\"form-control\",attrs:{\"placeholder\":_vm.$t('firefly.notes')},domProps:{\"value\":(_vm.value)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.value=$event.target.value}}})])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionLinks.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionLinks.vue?vue&type=script&lang=js&\"","\n\n\n\n\n","import { render, staticRenderFns } from \"./TransactionLinks.vue?vue&type=template&id=0574343e&\"\nimport script from \"./TransactionLinks.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionLinks.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"text-xs d-none d-lg-block d-xl-block\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('firefly.journal_links'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[(_vm.value.length === 0)?_c('p',[_vm._m(0)]):_vm._e(),_vm._v(\" \"),(_vm.value.length > 0)?_c('ul',{staticClass:\"list-group\"},_vm._l((_vm.value),function(transaction){return _c('li',{staticClass:\"list-group-item\"},[_c('em',[_vm._v(_vm._s(_vm.getTextForLinkType(transaction.link_type_id)))]),_vm._v(\" \"),_c('a',{attrs:{\"href\":\"./transaction/show/\" + transaction.transaction_group_id}},[_vm._v(_vm._s(transaction.description))]),_vm._v(\" \"),(transaction.type === 'withdrawal')?_c('span',[_vm._v(\"\\n (\"),_c('span',{staticClass:\"text-danger\"},[_vm._v(_vm._s(Intl.NumberFormat(_vm.locale, {\n style: 'currency',\n currency: transaction.currency_code\n }).format(parseFloat(transaction.amount) * -1)))]),_vm._v(\")\\n \")]):_vm._e(),_vm._v(\" \"),(transaction.type === 'deposit')?_c('span',[_vm._v(\"\\n (\"),_c('span',{staticClass:\"text-success\"},[_vm._v(_vm._s(Intl.NumberFormat(_vm.locale, {\n style: 'currency',\n currency: transaction.currency_code\n }).format(parseFloat(transaction.amount))))]),_vm._v(\")\\n \")]):_vm._e(),_vm._v(\" \"),(transaction.type === 'transfer')?_c('span',[_vm._v(\"\\n (\"),_c('span',{staticClass:\"text-info\"},[_vm._v(_vm._s(Intl.NumberFormat(_vm.locale, {\n style: 'currency',\n currency: transaction.currency_code\n }).format(parseFloat(transaction.amount))))]),_vm._v(\")\\n \")]):_vm._e(),_vm._v(\" \"),_vm._m(1,true)])}),0):_vm._e(),_vm._v(\" \"),(_vm.value.length > 0)?_c('div',{staticClass:\"form-text\"},[_vm._m(2)]):_vm._e()])])]),_vm._v(\" \"),_c('div',{staticClass:\"modal\",attrs:{\"tabindex\":\"-1\",\"id\":\"linkModal\"}},[_c('div',{staticClass:\"modal-dialog modal-lg\"},[_c('div',{staticClass:\"modal-content\"},[_vm._m(3),_vm._v(\" \"),_c('div',{staticClass:\"modal-body\"},[_c('div',{staticClass:\"container-fluid\"},[_vm._m(4),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('form',{on:{\"submit\":function($event){$event.preventDefault();return _vm.search($event)}}},[_c('div',{staticClass:\"input-group\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.query),expression:\"query\"}],staticClass:\"form-control\",attrs:{\"autocomplete\":\"off\",\"maxlength\":\"255\",\"type\":\"text\",\"name\":\"search\",\"id\":\"query\",\"placeholder\":\"Search query\"},domProps:{\"value\":(_vm.query)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.query=$event.target.value}}}),_vm._v(\" \"),_vm._m(5)])])])]),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[(_vm.searching)?_c('span',[_c('i',{staticClass:\"fas fa-spinner fa-spin\"})]):_vm._e(),_vm._v(\" \"),(_vm.searchResults.length > 0)?_c('h4',[_vm._v(\"Search results\")]):_vm._e(),_vm._v(\" \"),(_vm.searchResults.length > 0)?_c('table',{staticClass:\"table table-sm\"},[_vm._m(6),_vm._v(\" \"),_c('tbody',_vm._l((_vm.searchResults),function(result){return _c('tr',[_c('td',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(result.selected),expression:\"result.selected\"}],staticClass:\"form-control\",attrs:{\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(result.selected)?_vm._i(result.selected,null)>-1:(result.selected)},on:{\"change\":[function($event){var $$a=result.selected,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.$set(result, \"selected\", $$a.concat([$$v])))}else{$$i>-1&&(_vm.$set(result, \"selected\", $$a.slice(0,$$i).concat($$a.slice($$i+1))))}}else{_vm.$set(result, \"selected\", $$c)}},function($event){return _vm.selectTransaction($event)}]}})]),_vm._v(\" \"),_c('td',[_c('select',{directives:[{name:\"model\",rawName:\"v-model\",value:(result.link_type_id),expression:\"result.link_type_id\"}],staticClass:\"form-control\",on:{\"change\":[function($event){var $$selectedVal = Array.prototype.filter.call($event.target.options,function(o){return o.selected}).map(function(o){var val = \"_value\" in o ? o._value : o.value;return val}); _vm.$set(result, \"link_type_id\", $event.target.multiple ? $$selectedVal : $$selectedVal[0])},function($event){return _vm.selectLinkType($event)}]}},_vm._l((_vm.linkTypes),function(linkType){return _c('option',{attrs:{\"label\":linkType.type},domProps:{\"value\":linkType.id + '-' + linkType.direction}},[_vm._v(_vm._s(linkType.type)+\"\\n \")])}),0)]),_vm._v(\" \"),_c('td',[_c('a',{attrs:{\"href\":'./transactions/show/' + result.transaction_group_id}},[_vm._v(_vm._s(result.description))]),_vm._v(\" \"),(result.type === 'withdrawal')?_c('span',[_vm._v(\"\\n (\"),_c('span',{staticClass:\"text-danger\"},[_vm._v(_vm._s(Intl.NumberFormat(_vm.locale, {\n style: 'currency',\n currency: result.currency_code\n }).format(parseFloat(result.amount) * -1)))]),_vm._v(\")\\n \")]):_vm._e(),_vm._v(\" \"),(result.type === 'deposit')?_c('span',[_vm._v(\"\\n (\"),_c('span',{staticClass:\"text-success\"},[_vm._v(_vm._s(Intl.NumberFormat(_vm.locale, {\n style: 'currency',\n currency: result.currency_code\n }).format(parseFloat(result.amount))))]),_vm._v(\")\\n \")]):_vm._e(),_vm._v(\" \"),(result.type === 'transfer')?_c('span',[_vm._v(\"\\n (\"),_c('span',{staticClass:\"text-info\"},[_vm._v(_vm._s(Intl.NumberFormat(_vm.locale, {\n style: 'currency',\n currency: result.currency_code\n }).format(parseFloat(result.amount))))]),_vm._v(\")\\n \")]):_vm._e(),_vm._v(\" \"),_c('br'),_vm._v(\" \"),_c('em',[_c('a',{attrs:{\"href\":'./accounts/show/' + result.source_id}},[_vm._v(_vm._s(result.source_name))]),_vm._v(\"\\n →\\n \"),_c('a',{attrs:{\"href\":'./accounts/show/' + result.destination_id}},[_vm._v(_vm._s(result.destination_name))])])])])}),0)]):_vm._e()])])])]),_vm._v(\" \"),_vm._m(7)])])])])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('button',{staticClass:\"btn btn-default btn-xs\",attrs:{\"data-toggle\":\"modal\",\"data-target\":\"#linkModal\"}},[_c('i',{staticClass:\"fas fa-plus\"}),_vm._v(\" Add transaction link\")])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"btn-group btn-group-xs float-right\"},[_c('a',{staticClass:\"btn btn-xs btn-default\",attrs:{\"href\":\"#\"}},[_c('i',{staticClass:\"far fa-edit\"})]),_vm._v(\" \"),_c('a',{staticClass:\"btn btn-xs btn-danger\",attrs:{\"href\":\"#\"}},[_c('i',{staticClass:\"far fa-trash-alt\"})])])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('button',{staticClass:\"btn btn-default\",attrs:{\"data-toggle\":\"modal\",\"data-target\":\"#linkModal\"}},[_c('i',{staticClass:\"fas fa-plus\"})])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"modal-header\"},[_c('h5',{staticClass:\"modal-title\"},[_vm._v(\"Transaction thing dialog.\")]),_vm._v(\" \"),_c('button',{staticClass:\"close\",attrs:{\"type\":\"button\",\"data-dismiss\":\"modal\",\"aria-label\":\"Close\"}},[_c('span',{attrs:{\"aria-hidden\":\"true\"}},[_vm._v(\"×\")])])])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('p',[_vm._v(\"\\n Use this form to search for transactions you wish to link to this one. When in doubt, use \"),_c('code',[_vm._v(\"id:*\")]),_vm._v(\" where the ID is the number from\\n the URL.\\n \")])])])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"input-group-append\"},[_c('button',{staticClass:\"btn btn-default\",attrs:{\"type\":\"submit\"}},[_c('i',{staticClass:\"fas fa-search\"}),_vm._v(\" Search\")])])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('thead',[_c('tr',[_c('th',{staticStyle:{\"width\":\"33%\"},attrs:{\"colspan\":\"2\"}},[_vm._v(\"Include?\")]),_vm._v(\" \"),_c('th',[_vm._v(\"Transaction\")])])])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"modal-footer\"},[_c('button',{staticClass:\"btn btn-secondary\",attrs:{\"type\":\"button\",\"data-dismiss\":\"modal\"}},[_vm._v(\"Close\")])])}]\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionAttachments.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./TransactionAttachments.vue?vue&type=script&lang=js&\"","\n\n\n\n\n\n","import { render, staticRenderFns } from \"./TransactionAttachments.vue?vue&type=template&id=922408e0&scoped=true&\"\nimport script from \"./TransactionAttachments.vue?vue&type=script&lang=js&\"\nexport * from \"./TransactionAttachments.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"922408e0\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"form-group\"},[_c('div',{staticClass:\"text-xs d-none d-lg-block d-xl-block\"},[_vm._v(\"\\n \"+_vm._s(_vm.$t('firefly.attachments'))+\"\\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"input-group\"},[_c('input',{staticClass:\"form-control\",attrs:{\"type\":\"file\",\"multiple\":\"\",\"name\":\"attachments[]\",\"placeholder\":_vm.$t('firefly.attachment')}})])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Create.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Create.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./Create.vue?vue&type=template&id=7f815cd8&scoped=true&\"\nimport script from \"./Create.vue?vue&type=script&lang=js&\"\nexport * from \"./Create.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"7f815cd8\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_vm._l((this.transactions),function(transaction,index){return _c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-header\"},[_c('h3',{staticClass:\"card-title\"},[(0 === _vm.transactions.length)?_c('span',[_vm._v(_vm._s(_vm.$t('firefly.create_new_transaction')))]):_vm._e(),_vm._v(\" \"),(_vm.transactions.length > 1)?_c('span',[_vm._v(_vm._s(_vm.$t('firefly.single_split'))+\" \"+_vm._s(index + 1)+\" / \"+_vm._s(_vm.transactions.length))]):_vm._e()]),_vm._v(\" \"),(_vm.transactions.length > 1)?_c('div',{staticClass:\"card-tools\"},[_c('button',{staticClass:\"btn btn-xs btn-danger\",attrs:{\"type\":\"button\"},on:{\"click\":function($event){return _vm.removeTransaction(index)}}},[_c('i',{staticClass:\"fa fa-trash\"})])]):_vm._e()]),_vm._v(\" \"),_c('div',{staticClass:\"card-body\"},[_c('h4',[_vm._v(_vm._s(_vm.$t('firefly.basic_journal_information')))]),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_vm._v(\"\\n Description:\\n \"),_c('TransactionDescription',{attrs:{\"index\":index},model:{value:(transaction.description),callback:function ($$v) {_vm.$set(transaction, \"description\", $$v)},expression:\"transaction.description\"}})],1)]),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col-xl-5 col-lg-5 col-md-10 col-sm-12 col-xs-12\"},[_c('TransactionAccount',{attrs:{\"direction\":\"source\",\"index\":index},model:{value:(transaction.source_account),callback:function ($$v) {_vm.$set(transaction, \"source_account\", $$v)},expression:\"transaction.source_account\"}})],1),_vm._v(\" \"),_c('div',{staticClass:\"col-xl-2 col-lg-2 col-md-2 col-sm-12 text-center d-none d-sm-block\"},[_c('SwitchAccount',{attrs:{\"index\":index}})],1),_vm._v(\" \"),_c('div',{staticClass:\"col-xl-5 col-lg-5 col-md-12 col-sm-12 col-xs-12\"},[_c('TransactionAccount',{attrs:{\"direction\":\"destination\",\"index\":index},model:{value:(transaction.destination_account),callback:function ($$v) {_vm.$set(transaction, \"destination_account\", $$v)},expression:\"transaction.destination_account\"}})],1)]),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col-xl-5 col-lg-5 col-md-10 col-sm-12 col-xs-12\"},[_c('TransactionAmount',{attrs:{\"index\":index}})],1),_vm._v(\" \"),_c('div',{staticClass:\"col-xl-2 col-lg-2 col-md-2 col-sm-12 text-center d-none d-sm-block\"},[_c('TransactionForeignCurrency',{attrs:{\"index\":index}})],1),_vm._v(\" \"),_c('div',{staticClass:\"col-xl-5 col-lg-5 col-md-12 col-sm-12 col-xs-12\"},[_c('TransactionForeignAmount',{attrs:{\"index\":index}})],1)]),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col-xl-5 col-lg-5 col-md-12 col-sm-12 col-xs-12\"},[_c('TransactionDate',{attrs:{\"index\":index}})],1),_vm._v(\" \"),_c('div',{staticClass:\"col-xl-5 col-lg-5 col-md-12 col-sm-12 col-xs-12 offset-xl-2 offset-lg-2\"},[_c('TransactionCustomDates',{attrs:{\"index\":index,\"enabled-dates\":_vm.customDateFields}})],1)]),_vm._v(\" \"),_c('h4',[_vm._v(_vm._s(_vm.$t('firefly.transaction_journal_meta')))]),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('TransactionBudget',{attrs:{\"index\":index},model:{value:(transaction.budget_id),callback:function ($$v) {_vm.$set(transaction, \"budget_id\", $$v)},expression:\"transaction.budget_id\"}}),_vm._v(\" \"),_c('TransactionCategory',{attrs:{\"index\":index},model:{value:(transaction.category),callback:function ($$v) {_vm.$set(transaction, \"category\", $$v)},expression:\"transaction.category\"}})],1),_vm._v(\" \"),_c('div',{staticClass:\"col\"},[_c('TransactionBill',{attrs:{\"index\":index},model:{value:(transaction.bill_id),callback:function ($$v) {_vm.$set(transaction, \"bill_id\", $$v)},expression:\"transaction.bill_id\"}}),_vm._v(\" \"),_c('TransactionTags',{attrs:{\"index\":index},model:{value:(transaction.tags),callback:function ($$v) {_vm.$set(transaction, \"tags\", $$v)},expression:\"transaction.tags\"}}),_vm._v(\" \"),_c('TransactionPiggyBank',{attrs:{\"index\":index},model:{value:(transaction.piggy_bank_id),callback:function ($$v) {_vm.$set(transaction, \"piggy_bank_id\", $$v)},expression:\"transaction.piggy_bank_id\"}})],1)]),_vm._v(\" \"),_c('h4',[_vm._v(_vm._s(_vm.$t('firefly.transaction_journal_extra')))]),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('TransactionInternalReference',{attrs:{\"index\":index},model:{value:(transaction.internal_reference),callback:function ($$v) {_vm.$set(transaction, \"internal_reference\", $$v)},expression:\"transaction.internal_reference\"}}),_vm._v(\" \"),_c('TransactionExternalUrl',{attrs:{\"index\":index},model:{value:(transaction.external_url),callback:function ($$v) {_vm.$set(transaction, \"external_url\", $$v)},expression:\"transaction.external_url\"}}),_vm._v(\" \"),_c('TransactionNotes',{attrs:{\"index\":index},model:{value:(transaction.notes),callback:function ($$v) {_vm.$set(transaction, \"notes\", $$v)},expression:\"transaction.notes\"}})],1),_vm._v(\" \"),_c('div',{staticClass:\"col\"},[_c('TransactionAttachments',{attrs:{\"index\":index},model:{value:(transaction.attachments),callback:function ($$v) {_vm.$set(transaction, \"attachments\", $$v)},expression:\"transaction.attachments\"}}),_vm._v(\" \"),_c('TransactionLinks',{attrs:{\"index\":index},model:{value:(transaction.links),callback:function ($$v) {_vm.$set(transaction, \"links\", $$v)},expression:\"transaction.links\"}})],1)])])])])])}),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('button',{staticClass:\"btn btn-primary\",on:{\"click\":_vm.addTransaction}},[_vm._v(_vm._s(_vm.$t('firefly.add_another_split')))])]),_vm._v(\" \"),_c('div',{staticClass:\"col\"},[_c('p',{staticClass:\"float-right\"},[_c('button',{staticClass:\"btn btn-success\",attrs:{\"disabled\":_vm.isSubmitting},on:{\"click\":_vm.submitTransaction}},[_vm._v(\"Store transaction\")]),_vm._v(\" \"),_c('br')])])]),_vm._v(\" \"),_vm._m(0)],2)}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col float-right\"},[_c('p',{staticClass:\"text-right\"},[_c('small',{staticClass:\"text-muted\"},[_vm._v(\"Create another another another \"),_c('input',{attrs:{\"type\":\"checkbox\"}})]),_c('br'),_vm._v(\" \"),_c('small',{staticClass:\"text-muted\"},[_vm._v(\"Return here \"),_c('input',{attrs:{\"type\":\"checkbox\"}})]),_c('br')])])])}]\n\nexport { render, staticRenderFns }","/*\n * create.js\n * Copyright (c) 2020 james@firefly-iii.org\n *\n * This file is part of Firefly III (https://github.com/firefly-iii).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see .\n */\n\nimport store from \"../../components/store\";\nimport Create from \"../../components/transactions/Create\";\nimport Vue from \"vue\";\n\nrequire('../../bootstrap');\n\nVue.config.productionTip = false;\n// i18n\nlet i18n = require('../../i18n');\n\nlet props = {};\nnew Vue({\n i18n,\n store,\n render(createElement) {\n return createElement(Create, {props: props});\n },\n beforeCreate() {\n this.$store.commit('initialiseStore');\n this.$store.dispatch('updateCurrencyPreference');\n },\n }).$mount('#transactions_create');\n"],"sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack:///src/components/transactions/Create.vue","webpack:///./src/components/transactions/Create.vue?0a62","webpack:///./src/components/transactions/Create.vue","webpack:///./src/components/transactions/Create.vue?a1de","webpack:///./src/pages/transactions/create.js"],"names":["name","components","SplitForm","Alert","SplitPills","TransactionGroupTitle","created","this","getAllowedOpposingTypes","getAccountToTransaction","getCustomFields","addTransaction","data","errorMessage","successMessage","customFields","enableSubmit","createAnother","resetFormAfter","submittedTransaction","submittedLinks","submittedAttachments","inError","submittedAttCount","groupTitleErrors","returnedGroupId","returnedGroupTitle","accountToTransaction","allowedOpposingTypes","defaultSourceAllowedTypes","defaultDestinationAllowedTypes","sourceAllowedTypes","destinationAllowedTypes","date","Date","time","computed","watch","finalizeSubmit","methods","removeTransaction","$store","commit","payload","window","location","href","$t","transactions","hasOwnProperty","i","test","updateField","resetTransactions","setTimeout","submitTransaction","axios","post","submitAttachments","result","uploadedAttachment","key","length","storeLocation","storeAccountValue","calculateTransactionType","index","storeField","storeDate","storeTime","storeGroupTitle","setGroupTitle","source","dest","setTransactionType","expectedDestinationTypes","transactionType","submitTransactionLinks","submitted","links","ii","total","currentLink","outward_id","received","transaction_journal_id","inward_id","promises","push","then","Promise","all","parseErrors","resetErrors","errors","message","transactionIndex","parseInt","split","fieldName","setTransactionError","convertData","groupTitle","group_title","convertSplit","description","synchronizeAccounts","source_name","destination_name","source_id","destination_id","switchAccounts","theDate","setHours","getHours","setMinutes","getMinutes","setSeconds","getSeconds","dateStr","array","type","currency_id","amount","budget_id","category_name","category","tags","interest_date","book_date","process_date","due_date","payment_date","invoice_date","internal_reference","external_url","notes","external_id","zoom_level","longitude","latitude","order","reconciled","piggy_bank_id","currentSplit","bill_id","foreign_currency_id","foreign_amount","toLowerCase","firstSource","source_account_type","firstDestination","destination_account_type","source_account_currency_id","includes","destination_account_currency_id","link_type_id","linkTypeParts","inwardId","outwardId","newLink","get","setDestinationAllowedTypes","value","setSourceAllowedTypes","_vm","_h","$createElement","_c","_self","attrs","_v","staticClass","_l","transaction","on","$event","model","callback","$$v","$set","expression","_e","_s","directives","rawName","domProps","Array","isArray","_i","$$a","$$el","target","$$c","checked","$$i","concat","slice","require","Vue","config","productionTip","i18n","props","store","render","createElement","Create","beforeCreate","dispatch","$mount"],"mappings":"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","file":"/public/js/transactions/create.js","sourcesContent":["\n\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Create.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Create.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./Create.vue?vue&type=template&id=64509782&scoped=true&\"\nimport script from \"./Create.vue?vue&type=script&lang=js&\"\nexport * from \"./Create.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"64509782\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('alert',{attrs:{\"message\":_vm.errorMessage,\"type\":\"danger\"}}),_vm._v(\" \"),_c('alert',{attrs:{\"message\":_vm.successMessage,\"type\":\"success\"}}),_vm._v(\" \"),_c('SplitPills',{attrs:{\"transactions\":_vm.transactions}}),_vm._v(\" \"),_c('div',{staticClass:\"tab-content\"},_vm._l((this.transactions),function(transaction,index){return _c('SplitForm',{key:index,attrs:{\"allowed-opposing-types\":_vm.allowedOpposingTypes,\"count\":_vm.transactions.length,\"custom-fields\":_vm.customFields,\"date\":_vm.date,\"destination-allowed-types\":_vm.destinationAllowedTypes,\"index\":index,\"source-allowed-types\":_vm.sourceAllowedTypes,\"submitted-transaction\":_vm.submittedTransaction,\"time\":_vm.time,\"transaction\":transaction,\"transaction-type\":_vm.transactionType},on:{\"uploaded-attachments\":function($event){return _vm.uploadedAttachment($event)},\"set-marker-location\":function($event){return _vm.storeLocation($event)},\"set-account\":function($event){return _vm.storeAccountValue($event)},\"switch-accounts\":function($event){return _vm.switchAccounts($event)},\"set-date\":function($event){return _vm.storeDate($event)},\"set-time\":function($event){return _vm.storeTime($event)},\"set-field\":function($event){return _vm.storeField($event)},\"remove-transaction\":function($event){return _vm.removeTransaction($event)},\"set-dest-types\":function($event){return _vm.setDestinationAllowedTypes($event)},\"set-src-types\":function($event){return _vm.setSourceAllowedTypes($event)}}})}),1),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12\"},[(_vm.transactions.length > 1)?_c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-body\"},[_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('TransactionGroupTitle',{attrs:{\"errors\":this.groupTitleErrors},on:{\"set-group-title\":function($event){return _vm.storeGroupTitle($event)}},model:{value:(this.groupTitle),callback:function ($$v) {_vm.$set(this, \"groupTitle\", $$v)},expression:\"this.groupTitle\"}})],1)])])]):_vm._e()]),_vm._v(\" \"),_c('div',{staticClass:\"col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12\"},[_c('div',{staticClass:\"card card-primary\"},[_c('div',{staticClass:\"card-body\"},[_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('div',{staticClass:\"text-xs d-none d-lg-block d-xl-block\"},[_vm._v(\"\\n  \\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-outline-primary btn-block\",on:{\"click\":_vm.addTransaction}},[_c('i',{staticClass:\"far fa-clone\"}),_vm._v(\" \"+_vm._s(_vm.$t('firefly.add_another_split'))+\"\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"col\"},[_c('div',{staticClass:\"text-xs d-none d-lg-block d-xl-block\"},[_vm._v(\"\\n  \\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-success btn-block\",attrs:{\"disabled\":!_vm.enableSubmit},on:{\"click\":_vm.submitTransaction}},[(_vm.enableSubmit)?_c('span',[_c('i',{staticClass:\"far fa-save\"}),_vm._v(\" \"+_vm._s(_vm.$t('firefly.store_transaction')))]):_vm._e(),_vm._v(\" \"),(!_vm.enableSubmit)?_c('span',[_c('i',{staticClass:\"fas fa-spinner fa-spin\"})]):_vm._e()])])]),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_vm._v(\"\\n  \\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"col\"},[_c('div',{staticClass:\"form-check\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.createAnother),expression:\"createAnother\"}],staticClass:\"form-check-input\",attrs:{\"id\":\"createAnother\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.createAnother)?_vm._i(_vm.createAnother,null)>-1:(_vm.createAnother)},on:{\"change\":function($event){var $$a=_vm.createAnother,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.createAnother=$$a.concat([$$v]))}else{$$i>-1&&(_vm.createAnother=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.createAnother=$$c}}}}),_vm._v(\" \"),_c('label',{staticClass:\"form-check-label\",attrs:{\"for\":\"createAnother\"}},[_c('span',{staticClass:\"small\"},[_vm._v(_vm._s(_vm.$t('firefly.create_another')))])])]),_vm._v(\" \"),_c('div',{staticClass:\"form-check\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.resetFormAfter),expression:\"resetFormAfter\"}],staticClass:\"form-check-input\",attrs:{\"id\":\"resetFormAfter\",\"disabled\":!_vm.createAnother,\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.resetFormAfter)?_vm._i(_vm.resetFormAfter,null)>-1:(_vm.resetFormAfter)},on:{\"change\":function($event){var $$a=_vm.resetFormAfter,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.resetFormAfter=$$a.concat([$$v]))}else{$$i>-1&&(_vm.resetFormAfter=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.resetFormAfter=$$c}}}}),_vm._v(\" \"),_c('label',{staticClass:\"form-check-label\",attrs:{\"for\":\"resetFormAfter\"}},[_c('span',{staticClass:\"small\"},[_vm._v(_vm._s(_vm.$t('firefly.reset_after')))])])])])])])])])])],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","/*\n * create.js\n * Copyright (c) 2020 james@firefly-iii.org\n *\n * This file is part of Firefly III (https://github.com/firefly-iii).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see .\n */\n\nimport store from \"../../components/store\";\nimport Create from \"../../components/transactions/Create\";\nimport Vue from \"vue\";\n\nrequire('../../bootstrap');\n\nVue.config.productionTip = false;\n// i18n\nlet i18n = require('../../i18n');\n\nlet props = {};\nnew Vue({\n i18n,\n store,\n render(createElement) {\n return createElement(Create, {props: props});\n },\n beforeCreate() {\n this.$store.commit('initialiseStore');\n this.$store.dispatch('updateCurrencyPreference');\n },\n }).$mount('#transactions_create');\n"],"sourceRoot":""} \ No newline at end of file diff --git a/public/v2/js/transactions/edit.js b/public/v2/js/transactions/edit.js new file mode 100755 index 0000000000..a6c46260f6 --- /dev/null +++ b/public/v2/js/transactions/edit.js @@ -0,0 +1,2 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[9],{422:function(t,e,s){t.exports=s(431)},431:function(t,e,s){"use strict";s.r(e);var i=s(17),a=s(39),n=s(22),r=s(38),o=s(40),c=s(7),d=s(41),l={name:"Edit",created:function(){var t=window.location.pathname.split("/");this.groupId=parseInt(t[t.length-1]),this.getTransactionGroup(),this.getAllowedOpposingTypes(),this.getCustomFields()},data:function(){return{successMessage:"",errorMessage:"",transactions:[],originalTransactions:[],groupTitle:"",originalGroupTitle:"",transactionType:"any",groudId:0,groupTitleErrors:[],customFields:{},returnedGroupId:0,returnedGroupTitle:"",date:new Date,time:new Date,originalDate:new Date,originalTime:new Date,submittedTransaction:!1,submittedLinks:!1,submittedAttachments:!1,allowedOpposingTypes:{},destinationAllowedTypes:[],sourceAllowedTypes:[],enableSubmit:!0,createAnother:!1,resetFormAfter:!1}},components:{Alert:a.a,SplitPills:n.a,SplitForm:r.a,TransactionGroupTitle:o.a},methods:{getTransactionGroup:function(){var t=this;axios.get("./api/v1/transactions/"+this.groupId).then((function(e){t.parseTransactionGroup(e.data)})).catch((function(t){}))},parseTransactionGroup:function(t){var e=t.data.attributes,s=e.transactions.reverse();for(var i in this.groupTitle=e.group_title,this.originalGroupTitle=e.group_title,s)if(s.hasOwnProperty(i)&&/^0$|^[1-9]\d*$/.test(i)&&i<=4294967294){var a=this.parseTransaction(parseInt(i),s[i]);this.transactions.push(a),this.originalTransactions.push(d(a)),this.parseLinks(parseInt(a.transaction_journal_id),parseInt(i))}},parseTransaction:function(t,e){0===t&&(this.transactionType=e.type.charAt(0).toUpperCase()+e.type.slice(1),this.sourceAllowedTypes=[e.source_type],this.destinationAllowedTypes=[e.destination_type],this.date=new Date(e.date),this.time=new Date(e.date),this.originalDate=new Date(e.date),this.originalTime=new Date(e.date));var s=Object(c.b)();return s.description=e.description,s.transaction_journal_id=parseInt(e.transaction_journal_id),s.source_account_id=e.source_id,s.source_account_name=e.source_name,s.source_account_type=e.source_type,s.destination_account_id=e.destination_id,s.destination_account_name=e.destination_name,s.destination_account_type=e.destination_type,s.amount=e.amount,s.currency_id=e.currency_id,s.foreign_amount=e.foreign_amount,s.foreign_currency_id=e.foreign_currency_id,s.category=e.category_name,s.budget_id=e.budget_id,s.bill_id=e.bill_id,s.tags=e.tags,s.interest_date=e.interest_date?e.interest_date.substr(0,10):"",s.book_date=e.book_date?e.book_date.substr(0,10):"",s.process_date=e.process_date?e.process_date.substr(0,10):"",s.due_date=e.due_date?e.due_date.substr(0,10):"",s.payment_date=e.payment_date?e.payment_date.substr(0,10):"",s.invoice_date=e.invoice_date?e.invoice_date.substr(0,10):"",s.internal_reference=e.internal_reference,s.external_url=e.external_uri,s.external_id=e.external_id,s.notes=e.notes,s.location={zoom_level:e.zoom_level,longitude:e.longitude,latitude:e.latitude},s.zoom_level=e.zoom_level,s.longitude=e.longitude,s.latitude=e.latitude,s.errors=Object(c.a)(),s},parseLinks:function(t,e){var s=this;axios.get("./api/v1/transactions/"+t+"/links").then((function(i){var a=i.data.data;for(var n in a)a.hasOwnProperty(n)&&/^0$|^[1-9]\d*$/.test(n)&&n<=4294967294&&s.parseLink(a[n],t,e)}))},parseLink:function(t,e,s){var i=this,a=[],n=parseInt(t.attributes.inward_id),r="inward";n===e&&(n=parseInt(t.attributes.outward_id),r="outward"),a.push(new Promise((function(i){i({link:t,journalId:e,opposingId:n,index:s,direction:r})}))),a.push(axios.get("./api/v1/transaction-journals/"+n)),a.push(axios.get("./api/v1/transaction_links/"+t.attributes.link_type_id)),Promise.all(a).then((function(t){var e=t[1].data.data.attributes.transactions,s=t[0].opposingId,a={};for(var n in e)e.hasOwnProperty(n)&&/^0$|^[1-9]\d*$/.test(n)&&n<=4294967294&&e[n].transaction_journal_id===s&&(a=e[n]);var r=t[0].index,o=t[0].direction,c={link_type_id:t[2].data.data.id+"-"+o,transaction_group_id:t[1].data.data.id,transaction_journal_id:a.transaction_journal_id,description:a.description,type:a.type,currency_code:a.currency_code,amount:a.amount};i.transactions[r].links.push(c),i.originalTransactions[r].links.push(c)}))},getAllowedOpposingTypes:function(){var t=this;axios.get("./api/v1/configuration/static/firefly.allowed_opposing_types").then((function(e){t.allowedOpposingTypes=e.data["firefly.allowed_opposing_types"]}))},getCustomFields:function(){var t=this;axios.get("./api/v1/preferences/transaction_journal_optional_fields").then((function(e){t.customFields=e.data.data.attributes.data}))},uploadedAttachment:function(t){console.log("event: uploadedAttachment"),console.log(t)},storeLocation:function(t){this.transactions[t.index].zoom_level=t.zoomLevel,this.transactions[t.index].longitude=t.lng,this.transactions[t.index].latitude=t.lat},storeAccountValue:function(t){var e=t.direction,s=t.index;this.transactions[s][e+"_account_id"]=t.id,this.transactions[s][e+"_account_type"]=t.type,this.transactions[s][e+"_account_name"]=t.name},storeDate:function(t){this.date=t.date},storeTime:function(t){this.time=t.time},storeField:function(t){var e=t.field;"category"===e&&(e="category_name"),this.transactions[t.index][e]=t.value},removeTransaction:function(t){this.transactions.splice(t.index,1),this.originalTransactions=[]},storeGroupTitle:function(t){this.groupTitle=t},selectedAttachments:function(t){for(var e in this.transactions)this.transactions.hasOwnProperty(e)&&/^0$|^[1-9]\d*$/.test(e)&&e<=4294967294&&parseInt(this.transactions[e].transaction_journal_id)===parseInt(t)&&(this.transactions[e].selectedAttachments=!0)},addTransaction:function(){var t=Object(c.b)();t.errors=Object(c.a)(),this.transactions.push(t)},submitTransaction:function(){var t={transactions:[]},e=!1,s=!1,i=!1;for(var a in this.groupTitle!==this.originalGroupTitle&&(t.group_title=this.groupTitle,e=!0),this.transactions)if(this.transactions.hasOwnProperty(a)&&/^0$|^[1-9]\d*$/.test(a)&&a<=4294967294){var n=this.transactions[a],r=this.originalTransactions.hasOwnProperty(a)?this.originalTransactions[a]:{},o={},d=["description","source_account_id","source_account_name","destination_account_id","destination_account_name","amount","foreign_amount","foreign_currency_id","category_name","budget_id","bill_id","interest_date","book_date","due_date","payment_date","invoice_date","external_url","internal_reference","external_id","notes","zoom_level","longitude","latitude"];for(var l in d)if(d.hasOwnProperty(l)&&/^0$|^[1-9]\d*$/.test(l)&&l<=4294967294){var u=d[l];n[u]!==r[u]&&(o[u]=n[u])}0!==n.piggy_bank_id&&(o.piggy_bank_id=n.piggy_bank_id),JSON.stringify(n.tags)!==JSON.stringify(r.tags)&&(o.tags=n.tags),this.compareLinks(n.links)!==this.compareLinks(r.links)&&(s=!0),void 0!==n.selectedAttachments&&!0===n.selectedAttachments&&(i=!0);var p="invalid";if(this.date.toISOString()!==this.originalDate.toISOString()||this.time.toISOString()!==this.originalTime.toISOString()){e=!0;var _=this.date;_.setHours(this.time.getHours()),_.setMinutes(this.time.getMinutes()),_.setSeconds(this.time.getSeconds()),p=Object(c.c)(_),t.date=p}0!==Object.keys(o).length&&(o.transaction_journal_id=r.transaction_journal_id,t.transactions.push(o),e=!0)}console.log("submitTransaction"),console.log(i),console.log(s),console.log(e),e&&this.submitUpdate(t)},compareLinks:function(t){var e=[];for(var s in t)t.hasOwnProperty(s)&&/^0$|^[1-9]\d*$/.test(s)&&s<=4294967294&&e.push({amount:t[s].amount,currency_code:t[s].currency_code,description:t[s].description,link_type_id:t[s].link_type_id,transaction_group_id:t[s].transaction_group_id,type:t[s].type});return JSON.stringify(e)},submitUpdate:function(t){var e=this;console.log("submitUpdate");var s="./api/v1/transactions/"+this.groupId;axios.put(s,t).then((function(t){e.submittedTransaction=!0})).catch((function(t){console.log("error :("),console.log(t.response.data),e.enableSubmit=!0,e.submittedTransaction=!0,e.submittedAttachments=!0,e.submittedLinks=!0,e.inError=!0,e.parseErrors(t.response.data)}))},parseErrors:function(t){for(var e in this.transactions)this.transactions.hasOwnProperty(e)&&/^0$|^[1-9]\d*$/.test(e)&&e<=4294967294&&this.resetErrors({index:e});var s,i,a;for(var n in this.successMessage="",this.errorMessage=this.$t("firefly.errors_submission"),void 0===t.errors&&(this.successMessage="",this.errorMessage=t.message),t.errors)if(t.errors.hasOwnProperty(n)){if("group_title"===n){this.groupTitleErrors=t.errors[n];continue}if("group_title"!==n)switch(i=parseInt(n.split(".")[1]),a=n.split(".")[2]){case"amount":case"description":case"date":case"tags":s={index:i,field:a,errors:t.errors[n]},this.setTransactionError(s);break;case"budget_id":s={index:i,field:"budget",errors:t.errors[n]},this.setTransactionError(s);break;case"bill_id":s={index:i,field:"bill",errors:t.errors[n]},this.setTransactionError(s);break;case"piggy_bank_id":s={index:i,field:"piggy_bank",errors:t.errors[n]},this.setTransactionError(s);break;case"category_name":s={index:i,field:"category",errors:t.errors[n]},this.setTransactionError(s);break;case"source_name":case"source_id":s={index:i,field:"source",errors:t.errors[n]},this.setTransactionError(s);break;case"destination_name":case"destination_id":s={index:i,field:"destination",errors:t.errors[n]},this.setTransactionError(s);break;case"foreign_amount":case"foreign_currency":s={index:i,field:"foreign_amount",errors:t.errors[n]},this.setTransactionError(s)}this.transactions[i]}},setTransactionError:function(t){this.transactions[t.index].errors[t.field]=t.errors},resetErrors:function(t){this.transactions[t.index].errors=d(Object(c.a)())}}},u=s(1),p=Object(u.a)(l,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",[s("Alert",{attrs:{message:t.errorMessage,type:"danger"}}),t._v(" "),s("Alert",{attrs:{message:t.successMessage,type:"success"}}),t._v(" "),s("SplitPills",{attrs:{transactions:t.transactions}}),t._v(" "),s("div",{staticClass:"tab-content"},t._l(this.transactions,(function(e,i){return s("SplitForm",{key:i,attrs:{count:t.transactions.length,transaction:e,"allowed-opposing-types":t.allowedOpposingTypes,"custom-fields":t.customFields,date:t.date,time:t.time,index:i,"transaction-type":t.transactionType,"destination-allowed-types":t.destinationAllowedTypes,"source-allowed-types":t.sourceAllowedTypes,"allow-switch":!1,"submitted-transaction":t.submittedTransaction},on:{"uploaded-attachments":function(e){return t.uploadedAttachment(e)},"set-marker-location":function(e){return t.storeLocation(e)},"set-account":function(e){return t.storeAccountValue(e)},"set-date":function(e){return t.storeDate(e)},"set-time":function(e){return t.storeTime(e)},"set-field":function(e){return t.storeField(e)},"remove-transaction":function(e){return t.removeTransaction(e)},"selected-attachments":function(e){return t.selectedAttachments(e)}}})})),1),t._v(" "),s("div",{staticClass:"row"},[s("div",{staticClass:"col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12"},[t.transactions.length>1?s("div",{staticClass:"card"},[s("div",{staticClass:"card-body"},[s("div",{staticClass:"row"},[s("div",{staticClass:"col"},[s("TransactionGroupTitle",{attrs:{errors:this.groupTitleErrors},on:{"set-group-title":function(e){return t.storeGroupTitle(e)}},model:{value:this.groupTitle,callback:function(e){t.$set(this,"groupTitle",e)},expression:"this.groupTitle"}})],1)])])]):t._e()]),t._v(" "),s("div",{staticClass:"col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12"},[s("div",{staticClass:"card"},[s("div",{staticClass:"card-body"},[s("div",{staticClass:"row"},[s("div",{staticClass:"col"},[s("div",{staticClass:"text-xs d-none d-lg-block d-xl-block"},[t._v("\n  \n ")]),t._v(" "),s("button",{staticClass:"btn btn-outline-primary btn-block",on:{click:t.addTransaction}},[s("i",{staticClass:"far fa-clone"}),t._v(" "+t._s(t.$t("firefly.add_another_split"))+"\n ")])]),t._v(" "),s("div",{staticClass:"col"},[s("div",{staticClass:"text-xs d-none d-lg-block d-xl-block"},[t._v("\n  \n ")]),t._v(" "),s("button",{staticClass:"btn btn-info btn-block",attrs:{disabled:!t.enableSubmit},on:{click:t.submitTransaction}},[t.enableSubmit?s("span",[s("i",{staticClass:"far fa-save"}),t._v(" "+t._s(t.$t("firefly.update_transaction")))]):t._e(),t._v(" "),t.enableSubmit?t._e():s("span",[s("i",{staticClass:"fas fa-spinner fa-spin"})])])])]),t._v(" "),s("div",{staticClass:"row"},[s("div",{staticClass:"col"},[t._v("\n  \n ")]),t._v(" "),s("div",{staticClass:"col"},[s("div",{staticClass:"form-check"},[s("input",{directives:[{name:"model",rawName:"v-model",value:t.createAnother,expression:"createAnother"}],staticClass:"form-check-input",attrs:{id:"createAnother",type:"checkbox"},domProps:{checked:Array.isArray(t.createAnother)?t._i(t.createAnother,null)>-1:t.createAnother},on:{change:function(e){var s=t.createAnother,i=e.target,a=!!i.checked;if(Array.isArray(s)){var n=t._i(s,null);i.checked?n<0&&(t.createAnother=s.concat([null])):n>-1&&(t.createAnother=s.slice(0,n).concat(s.slice(n+1)))}else t.createAnother=a}}}),t._v(" "),s("label",{staticClass:"form-check-label",attrs:{for:"createAnother"}},[s("span",{staticClass:"small"},[t._v(t._s(t.$t("firefly.after_update_create_another")))])])])])])])])])])],1)}),[],!1,null,"749f0a97",null).exports,_=s(2),h=s.n(_);s(16),h.a.config.productionTip=!1;var g=s(19),m={};new h.a({i18n:g,store:i.a,render:function(t){return t(p,{props:m})},beforeCreate:function(){this.$store.commit("initialiseStore"),this.$store.dispatch("updateCurrencyPreference")}}).$mount("#transactions_edit")}},[[422,0,1]]]); +//# sourceMappingURL=edit.js.map \ No newline at end of file diff --git a/public/v2/js/transactions/edit.js.map b/public/v2/js/transactions/edit.js.map new file mode 100755 index 0000000000..a401fbf149 --- /dev/null +++ b/public/v2/js/transactions/edit.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///src/components/transactions/Edit.vue","webpack:///./src/components/transactions/Edit.vue?f2c6","webpack:///./src/components/transactions/Edit.vue","webpack:///./src/components/transactions/Edit.vue?5a30","webpack:///./src/pages/transactions/edit.js"],"names":["name","created","this","groupId","parseInt","parts","length","getTransactionGroup","getAllowedOpposingTypes","getCustomFields","data","successMessage","errorMessage","transactions","originalTransactions","groupTitle","originalGroupTitle","transactionType","groudId","groupTitleErrors","customFields","returnedGroupId","returnedGroupTitle","date","Date","time","originalDate","originalTime","submittedTransaction","submittedLinks","submittedAttachments","allowedOpposingTypes","destinationAllowedTypes","sourceAllowedTypes","enableSubmit","createAnother","resetFormAfter","components","Alert","SplitPills","SplitForm","TransactionGroupTitle","methods","axios","get","parseTransactionGroup","attributes","group_title","hasOwnProperty","i","test","push","result","lodashClonedeep","parseLinks","transaction_journal_id","parseTransaction","index","array","type","charAt","toUpperCase","slice","source_type","destination_type","description","source_account_id","source_id","source_account_name","source_name","source_account_type","destination_account_id","destination_id","destination_account_name","destination_name","destination_account_type","amount","currency_id","foreign_amount","foreign_currency_id","category","category_name","budget_id","bill_id","tags","interest_date","substr","book_date","process_date","due_date","payment_date","invoice_date","internal_reference","external_url","external_uri","external_id","notes","location","zoom_level","longitude","latitude","errors","journalId","parseLink","opposingId","link","outward_id","linkDirection","promises","Promise","resolve","link_type_id","all","then","journals","journal","direction","transaction_group_id","responses","id","currency_code","uploadedAttachment","console","log","payload","storeLocation","zoomLevel","lng","lat","storeAccountValue","storeDate","storeTime","storeField","field","value","removeTransaction","splice","storeGroupTitle","selectedAttachments","addTransaction","newTransaction","submitTransaction","submission","shouldSubmit","basicFields","ii","currentTransaction","fieldName","originalTransaction","diff","piggy_bank_id","JSON","stringify","shouldLinks","shouldUpload","theDate","setHours","getHours","setMinutes","getMinutes","setSeconds","getSeconds","dateStr","Object","keys","submitUpdate","compareLinks","compare","put","url","parseErrors","resetErrors","$t","message","key","transactionIndex","split","setTransactionError","_vm","_h","$createElement","_c","_self","attrs","_v","staticClass","_l","transaction","on","$event","model","callback","$$v","$set","expression","_e","_s","directives","rawName","domProps","Array","isArray","_i","$$a","$$el","target","$$c","checked","$$i","concat","require","Vue","config","productionTip","i18n","props","store","render","createElement","Edit","beforeCreate","$store","commit","dispatch","$mount"],"mappings":"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","file":"/public/js/transactions/edit.js","sourcesContent":["\n\n\n\n\n\n","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Edit.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--4-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Edit.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./Edit.vue?vue&type=template&id=749f0a97&scoped=true&\"\nimport script from \"./Edit.vue?vue&type=script&lang=js&\"\nexport * from \"./Edit.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"749f0a97\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('Alert',{attrs:{\"message\":_vm.errorMessage,\"type\":\"danger\"}}),_vm._v(\" \"),_c('Alert',{attrs:{\"message\":_vm.successMessage,\"type\":\"success\"}}),_vm._v(\" \"),_c('SplitPills',{attrs:{\"transactions\":_vm.transactions}}),_vm._v(\" \"),_c('div',{staticClass:\"tab-content\"},_vm._l((this.transactions),function(transaction,index){return _c('SplitForm',{key:index,attrs:{\"count\":_vm.transactions.length,\"transaction\":transaction,\"allowed-opposing-types\":_vm.allowedOpposingTypes,\"custom-fields\":_vm.customFields,\"date\":_vm.date,\"time\":_vm.time,\"index\":index,\"transaction-type\":_vm.transactionType,\"destination-allowed-types\":_vm.destinationAllowedTypes,\"source-allowed-types\":_vm.sourceAllowedTypes,\"allow-switch\":false,\"submitted-transaction\":_vm.submittedTransaction},on:{\"uploaded-attachments\":function($event){return _vm.uploadedAttachment($event)},\"set-marker-location\":function($event){return _vm.storeLocation($event)},\"set-account\":function($event){return _vm.storeAccountValue($event)},\"set-date\":function($event){return _vm.storeDate($event)},\"set-time\":function($event){return _vm.storeTime($event)},\"set-field\":function($event){return _vm.storeField($event)},\"remove-transaction\":function($event){return _vm.removeTransaction($event)},\"selected-attachments\":function($event){return _vm.selectedAttachments($event)}}})}),1),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12\"},[(_vm.transactions.length > 1)?_c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-body\"},[_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('TransactionGroupTitle',{attrs:{\"errors\":this.groupTitleErrors},on:{\"set-group-title\":function($event){return _vm.storeGroupTitle($event)}},model:{value:(this.groupTitle),callback:function ($$v) {_vm.$set(this, \"groupTitle\", $$v)},expression:\"this.groupTitle\"}})],1)])])]):_vm._e()]),_vm._v(\" \"),_c('div',{staticClass:\"col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12\"},[_c('div',{staticClass:\"card\"},[_c('div',{staticClass:\"card-body\"},[_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_c('div',{staticClass:\"text-xs d-none d-lg-block d-xl-block\"},[_vm._v(\"\\n  \\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-outline-primary btn-block\",on:{\"click\":_vm.addTransaction}},[_c('i',{staticClass:\"far fa-clone\"}),_vm._v(\" \"+_vm._s(_vm.$t('firefly.add_another_split'))+\"\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"col\"},[_c('div',{staticClass:\"text-xs d-none d-lg-block d-xl-block\"},[_vm._v(\"\\n  \\n \")]),_vm._v(\" \"),_c('button',{staticClass:\"btn btn-info btn-block\",attrs:{\"disabled\":!_vm.enableSubmit},on:{\"click\":_vm.submitTransaction}},[(_vm.enableSubmit)?_c('span',[_c('i',{staticClass:\"far fa-save\"}),_vm._v(\" \"+_vm._s(_vm.$t('firefly.update_transaction')))]):_vm._e(),_vm._v(\" \"),(!_vm.enableSubmit)?_c('span',[_c('i',{staticClass:\"fas fa-spinner fa-spin\"})]):_vm._e()])])]),_vm._v(\" \"),_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"col\"},[_vm._v(\"\\n  \\n \")]),_vm._v(\" \"),_c('div',{staticClass:\"col\"},[_c('div',{staticClass:\"form-check\"},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.createAnother),expression:\"createAnother\"}],staticClass:\"form-check-input\",attrs:{\"id\":\"createAnother\",\"type\":\"checkbox\"},domProps:{\"checked\":Array.isArray(_vm.createAnother)?_vm._i(_vm.createAnother,null)>-1:(_vm.createAnother)},on:{\"change\":function($event){var $$a=_vm.createAnother,$$el=$event.target,$$c=$$el.checked?(true):(false);if(Array.isArray($$a)){var $$v=null,$$i=_vm._i($$a,$$v);if($$el.checked){$$i<0&&(_vm.createAnother=$$a.concat([$$v]))}else{$$i>-1&&(_vm.createAnother=$$a.slice(0,$$i).concat($$a.slice($$i+1)))}}else{_vm.createAnother=$$c}}}}),_vm._v(\" \"),_c('label',{staticClass:\"form-check-label\",attrs:{\"for\":\"createAnother\"}},[_c('span',{staticClass:\"small\"},[_vm._v(_vm._s(_vm.$t('firefly.after_update_create_another')))])])])])])])])])])],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","/*\n * edit.js\n * Copyright (c) 2020 james@firefly-iii.org\n *\n * This file is part of Firefly III (https://github.com/firefly-iii).\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see .\n */\n\nimport store from \"../../components/store\";\nimport Edit from \"../../components/transactions/Edit\";\nimport Vue from \"vue\";\n\nrequire('../../bootstrap');\n\nVue.config.productionTip = false;\n// i18n\nlet i18n = require('../../i18n');\n\nlet props = {};\nnew Vue({\n i18n,\n store,\n render(createElement) {\n return createElement(Edit, {props: props});\n },\n beforeCreate() {\n this.$store.commit('initialiseStore');\n this.$store.dispatch('updateCurrencyPreference');\n },\n }).$mount('#transactions_edit');\n"],"sourceRoot":""} \ No newline at end of file diff --git a/public/v2/js/vendor.js b/public/v2/js/vendor.js index e7c708ebf7..c43bbd3601 100755 --- a/public/v2/js/vendor.js +++ b/public/v2/js/vendor.js @@ -1,3 +1,3 @@ /*! For license information please see vendor.js.LICENSE.txt */ -(window.webpackJsonp=window.webpackJsonp||[]).push([[1],[function(e,t,n){(function(e){e.exports=function(){"use strict";var t,r;function i(){return t.apply(null,arguments)}function a(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function o(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function s(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function u(e){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(e).length;var t;for(t in e)if(s(e,t))return!1;return!0}function l(e){return void 0===e}function c(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function d(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function f(e,t){var n,r=[];for(n=0;n>>0;for(t=0;t0)for(n=0;n=0?n?"+":"":"-")+Math.pow(10,Math.max(0,i)).toString().substr(1)+r}i.suppressDeprecationWarnings=!1,i.deprecationHandler=null,k=Object.keys?Object.keys:function(e){var t,n=[];for(t in e)s(e,t)&&n.push(t);return n};var E=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|N{1,5}|YYYYYY|YYYYY|YYYY|YY|y{2,4}|yo?|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,j=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,O={},P={};function I(e,t,n,r){var i=r;"string"==typeof r&&(i=function(){return this[r]()}),e&&(P[e]=i),t&&(P[t[0]]=function(){return C(i.apply(this,arguments),t[1],t[2])}),n&&(P[n]=function(){return this.localeData().ordinal(i.apply(this,arguments),e)})}function H(e,t){return e.isValid()?(t=N(t,e.localeData()),O[t]=O[t]||function(e){var t,n,r,i=e.match(E);for(t=0,n=i.length;t=0&&j.test(e);)e=e.replace(j,r),j.lastIndex=0,n-=1;return e}var F={};function B(e,t){var n=e.toLowerCase();F[n]=F[n+"s"]=F[t]=e}function z(e){return"string"==typeof e?F[e]||F[e.toLowerCase()]:void 0}function $(e){var t,n,r={};for(n in e)s(e,n)&&(t=z(n))&&(r[t]=e[n]);return r}var R={};function W(e,t){R[e]=t}function V(e){return e%4==0&&e%100!=0||e%400==0}function U(e){return e<0?Math.ceil(e)||0:Math.floor(e)}function q(e){var t=+e,n=0;return 0!==t&&isFinite(t)&&(n=U(t)),n}function G(e,t){return function(n){return null!=n?(Q(this,e,n),i.updateOffset(this,t),this):J(this,e)}}function J(e,t){return e.isValid()?e._d["get"+(e._isUTC?"UTC":"")+t]():NaN}function Q(e,t,n){e.isValid()&&!isNaN(n)&&("FullYear"===t&&V(e.year())&&1===e.month()&&29===e.date()?(n=q(n),e._d["set"+(e._isUTC?"UTC":"")+t](n,e.month(),Ae(n,e.month()))):e._d["set"+(e._isUTC?"UTC":"")+t](n))}var K,Z=/\d/,X=/\d\d/,ee=/\d{3}/,te=/\d{4}/,ne=/[+-]?\d{6}/,re=/\d\d?/,ie=/\d\d\d\d?/,ae=/\d\d\d\d\d\d?/,oe=/\d{1,3}/,se=/\d{1,4}/,ue=/[+-]?\d{1,6}/,le=/\d+/,ce=/[+-]?\d+/,de=/Z|[+-]\d\d:?\d\d/gi,fe=/Z|[+-]\d\d(?::?\d\d)?/gi,he=/[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i;function pe(e,t,n){K[e]=T(t)?t:function(e,r){return e&&n?n:t}}function me(e,t){return s(K,e)?K[e](t._strict,t._locale):new RegExp(_e(e.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,(function(e,t,n,r,i){return t||n||r||i}))))}function _e(e){return e.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}K={};var ge,ve={};function ye(e,t){var n,r=t;for("string"==typeof e&&(e=[e]),c(t)&&(r=function(e,n){n[t]=q(e)}),n=0;n68?1900:2e3)};var je=G("FullYear",!0);function Oe(e,t,n,r,i,a,o){var s;return e<100&&e>=0?(s=new Date(e+400,t,n,r,i,a,o),isFinite(s.getFullYear())&&s.setFullYear(e)):s=new Date(e,t,n,r,i,a,o),s}function Pe(e){var t,n;return e<100&&e>=0?((n=Array.prototype.slice.call(arguments))[0]=e+400,t=new Date(Date.UTC.apply(null,n)),isFinite(t.getUTCFullYear())&&t.setUTCFullYear(e)):t=new Date(Date.UTC.apply(null,arguments)),t}function Ie(e,t,n){var r=7+t-n;return-(7+Pe(e,0,r).getUTCDay()-t)%7+r-1}function He(e,t,n,r,i){var a,o,s=1+7*(t-1)+(7+n-r)%7+Ie(e,r,i);return s<=0?o=Ee(a=e-1)+s:s>Ee(e)?(a=e+1,o=s-Ee(e)):(a=e,o=s),{year:a,dayOfYear:o}}function Ne(e,t,n){var r,i,a=Ie(e.year(),t,n),o=Math.floor((e.dayOfYear()-a-1)/7)+1;return o<1?r=o+Fe(i=e.year()-1,t,n):o>Fe(e.year(),t,n)?(r=o-Fe(e.year(),t,n),i=e.year()+1):(i=e.year(),r=o),{week:r,year:i}}function Fe(e,t,n){var r=Ie(e,t,n),i=Ie(e+1,t,n);return(Ee(e)-r+i)/7}function Be(e,t){return e.slice(t,7).concat(e.slice(0,t))}I("w",["ww",2],"wo","week"),I("W",["WW",2],"Wo","isoWeek"),B("week","w"),B("isoWeek","W"),W("week",5),W("isoWeek",5),pe("w",re),pe("ww",re,X),pe("W",re),pe("WW",re,X),be(["w","ww","W","WW"],(function(e,t,n,r){t[r.substr(0,1)]=q(e)})),I("d",0,"do","day"),I("dd",0,0,(function(e){return this.localeData().weekdaysMin(this,e)})),I("ddd",0,0,(function(e){return this.localeData().weekdaysShort(this,e)})),I("dddd",0,0,(function(e){return this.localeData().weekdays(this,e)})),I("e",0,0,"weekday"),I("E",0,0,"isoWeekday"),B("day","d"),B("weekday","e"),B("isoWeekday","E"),W("day",11),W("weekday",11),W("isoWeekday",11),pe("d",re),pe("e",re),pe("E",re),pe("dd",(function(e,t){return t.weekdaysMinRegex(e)})),pe("ddd",(function(e,t){return t.weekdaysShortRegex(e)})),pe("dddd",(function(e,t){return t.weekdaysRegex(e)})),be(["dd","ddd","dddd"],(function(e,t,n,r){var i=n._locale.weekdaysParse(e,r,n._strict);null!=i?t.d=i:m(n).invalidWeekday=e})),be(["d","e","E"],(function(e,t,n,r){t[r]=q(e)}));var ze="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),$e="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Re="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),We=he,Ve=he,Ue=he;function qe(e,t,n){var r,i,a,o=e.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],r=0;r<7;++r)a=p([2e3,1]).day(r),this._minWeekdaysParse[r]=this.weekdaysMin(a,"").toLocaleLowerCase(),this._shortWeekdaysParse[r]=this.weekdaysShort(a,"").toLocaleLowerCase(),this._weekdaysParse[r]=this.weekdays(a,"").toLocaleLowerCase();return n?"dddd"===t?-1!==(i=ge.call(this._weekdaysParse,o))?i:null:"ddd"===t?-1!==(i=ge.call(this._shortWeekdaysParse,o))?i:null:-1!==(i=ge.call(this._minWeekdaysParse,o))?i:null:"dddd"===t?-1!==(i=ge.call(this._weekdaysParse,o))||-1!==(i=ge.call(this._shortWeekdaysParse,o))||-1!==(i=ge.call(this._minWeekdaysParse,o))?i:null:"ddd"===t?-1!==(i=ge.call(this._shortWeekdaysParse,o))||-1!==(i=ge.call(this._weekdaysParse,o))||-1!==(i=ge.call(this._minWeekdaysParse,o))?i:null:-1!==(i=ge.call(this._minWeekdaysParse,o))||-1!==(i=ge.call(this._weekdaysParse,o))||-1!==(i=ge.call(this._shortWeekdaysParse,o))?i:null}function Ge(){function e(e,t){return t.length-e.length}var t,n,r,i,a,o=[],s=[],u=[],l=[];for(t=0;t<7;t++)n=p([2e3,1]).day(t),r=_e(this.weekdaysMin(n,"")),i=_e(this.weekdaysShort(n,"")),a=_e(this.weekdays(n,"")),o.push(r),s.push(i),u.push(a),l.push(r),l.push(i),l.push(a);o.sort(e),s.sort(e),u.sort(e),l.sort(e),this._weekdaysRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+s.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+o.join("|")+")","i")}function Je(){return this.hours()%12||12}function Qe(e,t){I(e,0,0,(function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)}))}function Ke(e,t){return t._meridiemParse}I("H",["HH",2],0,"hour"),I("h",["hh",2],0,Je),I("k",["kk",2],0,(function(){return this.hours()||24})),I("hmm",0,0,(function(){return""+Je.apply(this)+C(this.minutes(),2)})),I("hmmss",0,0,(function(){return""+Je.apply(this)+C(this.minutes(),2)+C(this.seconds(),2)})),I("Hmm",0,0,(function(){return""+this.hours()+C(this.minutes(),2)})),I("Hmmss",0,0,(function(){return""+this.hours()+C(this.minutes(),2)+C(this.seconds(),2)})),Qe("a",!0),Qe("A",!1),B("hour","h"),W("hour",13),pe("a",Ke),pe("A",Ke),pe("H",re),pe("h",re),pe("k",re),pe("HH",re,X),pe("hh",re,X),pe("kk",re,X),pe("hmm",ie),pe("hmmss",ae),pe("Hmm",ie),pe("Hmmss",ae),ye(["H","HH"],3),ye(["k","kk"],(function(e,t,n){var r=q(e);t[3]=24===r?0:r})),ye(["a","A"],(function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e})),ye(["h","hh"],(function(e,t,n){t[3]=q(e),m(n).bigHour=!0})),ye("hmm",(function(e,t,n){var r=e.length-2;t[3]=q(e.substr(0,r)),t[4]=q(e.substr(r)),m(n).bigHour=!0})),ye("hmmss",(function(e,t,n){var r=e.length-4,i=e.length-2;t[3]=q(e.substr(0,r)),t[4]=q(e.substr(r,2)),t[5]=q(e.substr(i)),m(n).bigHour=!0})),ye("Hmm",(function(e,t,n){var r=e.length-2;t[3]=q(e.substr(0,r)),t[4]=q(e.substr(r))})),ye("Hmmss",(function(e,t,n){var r=e.length-4,i=e.length-2;t[3]=q(e.substr(0,r)),t[4]=q(e.substr(r,2)),t[5]=q(e.substr(i))}));var Ze,Xe=G("Hours",!0),et={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",w:"a week",ww:"%d weeks",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:xe,monthsShort:Me,week:{dow:0,doy:6},weekdays:ze,weekdaysMin:Re,weekdaysShort:$e,meridiemParse:/[ap]\.?m?\.?/i},tt={},nt={};function rt(e,t){var n,r=Math.min(e.length,t.length);for(n=0;n0;){if(r=at(i.slice(0,t).join("-")))return r;if(n&&n.length>=t&&rt(i,n)>=t-1)break;t--}a++}return Ze}(e)}function lt(e){var t,n=e._a;return n&&-2===m(e).overflow&&(t=n[1]<0||n[1]>11?1:n[2]<1||n[2]>Ae(n[0],n[1])?2:n[3]<0||n[3]>24||24===n[3]&&(0!==n[4]||0!==n[5]||0!==n[6])?3:n[4]<0||n[4]>59?4:n[5]<0||n[5]>59?5:n[6]<0||n[6]>999?6:-1,m(e)._overflowDayOfYear&&(t<0||t>2)&&(t=2),m(e)._overflowWeeks&&-1===t&&(t=7),m(e)._overflowWeekday&&-1===t&&(t=8),m(e).overflow=t),e}var ct=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/,dt=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d|))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/,ft=/Z|[+-]\d\d(?::?\d\d)?/,ht=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/],["YYYYMM",/\d{6}/,!1],["YYYY",/\d{4}/,!1]],pt=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],mt=/^\/?Date\((-?\d+)/i,_t=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/,gt={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function vt(e){var t,n,r,i,a,o,s=e._i,u=ct.exec(s)||dt.exec(s);if(u){for(m(e).iso=!0,t=0,n=ht.length;t7)&&(u=!0)):(a=e._locale._week.dow,o=e._locale._week.doy,l=Ne(Dt(),a,o),n=wt(t.gg,e._a[0],l.year),r=wt(t.w,l.week),null!=t.d?((i=t.d)<0||i>6)&&(u=!0):null!=t.e?(i=t.e+a,(t.e<0||t.e>6)&&(u=!0)):i=a),r<1||r>Fe(n,a,o)?m(e)._overflowWeeks=!0:null!=u?m(e)._overflowWeekday=!0:(s=He(n,r,i,a,o),e._a[0]=s.year,e._dayOfYear=s.dayOfYear)}(e),null!=e._dayOfYear&&(o=wt(e._a[0],r[0]),(e._dayOfYear>Ee(o)||0===e._dayOfYear)&&(m(e)._overflowDayOfYear=!0),n=Pe(o,0,e._dayOfYear),e._a[1]=n.getUTCMonth(),e._a[2]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=s[t]=r[t];for(;t<7;t++)e._a[t]=s[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[3]&&0===e._a[4]&&0===e._a[5]&&0===e._a[6]&&(e._nextDay=!0,e._a[3]=0),e._d=(e._useUTC?Pe:Oe).apply(null,s),a=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[3]=24),e._w&&void 0!==e._w.d&&e._w.d!==a&&(m(e).weekdayMismatch=!0)}}function xt(e){if(e._f!==i.ISO_8601)if(e._f!==i.RFC_2822){e._a=[],m(e).empty=!0;var t,n,r,a,o,s,u=""+e._i,l=u.length,c=0;for(r=N(e._f,e._locale).match(E)||[],t=0;t0&&m(e).unusedInput.push(o),u=u.slice(u.indexOf(n)+n.length),c+=n.length),P[a]?(n?m(e).empty=!1:m(e).unusedTokens.push(a),we(a,n,e)):e._strict&&!n&&m(e).unusedTokens.push(a);m(e).charsLeftOver=l-c,u.length>0&&m(e).unusedInput.push(u),e._a[3]<=12&&!0===m(e).bigHour&&e._a[3]>0&&(m(e).bigHour=void 0),m(e).parsedDateParts=e._a.slice(0),m(e).meridiem=e._meridiem,e._a[3]=function(e,t,n){var r;return null==n?t:null!=e.meridiemHour?e.meridiemHour(t,n):null!=e.isPM?((r=e.isPM(n))&&t<12&&(t+=12),r||12!==t||(t=0),t):t}(e._locale,e._a[3],e._meridiem),null!==(s=m(e).era)&&(e._a[0]=e._locale.erasConvertYear(s,e._a[0])),At(e),lt(e)}else bt(e);else vt(e)}function Mt(e){var t=e._i,n=e._f;return e._locale=e._locale||ut(e._l),null===t||void 0===n&&""===t?g({nullInput:!0}):("string"==typeof t&&(e._i=t=e._locale.preparse(t)),A(t)?new w(lt(t)):(d(t)?e._d=t:a(n)?function(e){var t,n,r,i,a,o,s=!1;if(0===e._f.length)return m(e).invalidFormat=!0,void(e._d=new Date(NaN));for(i=0;ithis?this:e:g()}));function St(e,t){var n,r;if(1===t.length&&a(t[0])&&(t=t[0]),!t.length)return Dt();for(n=t[0],r=1;r=0?new Date(e+400,t,n)-126227808e5:new Date(e,t,n).valueOf()}function an(e,t,n){return e<100&&e>=0?Date.UTC(e+400,t,n)-126227808e5:Date.UTC(e,t,n)}function on(e,t){return t.erasAbbrRegex(e)}function sn(){var e,t,n=[],r=[],i=[],a=[],o=this.eras();for(e=0,t=o.length;e(a=Fe(e,r,i))&&(t=a),cn.call(this,e,t,n,r,i))}function cn(e,t,n,r,i){var a=He(e,t,n,r,i),o=Pe(a.year,0,a.dayOfYear);return this.year(o.getUTCFullYear()),this.month(o.getUTCMonth()),this.date(o.getUTCDate()),this}I("N",0,0,"eraAbbr"),I("NN",0,0,"eraAbbr"),I("NNN",0,0,"eraAbbr"),I("NNNN",0,0,"eraName"),I("NNNNN",0,0,"eraNarrow"),I("y",["y",1],"yo","eraYear"),I("y",["yy",2],0,"eraYear"),I("y",["yyy",3],0,"eraYear"),I("y",["yyyy",4],0,"eraYear"),pe("N",on),pe("NN",on),pe("NNN",on),pe("NNNN",(function(e,t){return t.erasNameRegex(e)})),pe("NNNNN",(function(e,t){return t.erasNarrowRegex(e)})),ye(["N","NN","NNN","NNNN","NNNNN"],(function(e,t,n,r){var i=n._locale.erasParse(e,r,n._strict);i?m(n).era=i:m(n).invalidEra=e})),pe("y",le),pe("yy",le),pe("yyy",le),pe("yyyy",le),pe("yo",(function(e,t){return t._eraYearOrdinalRegex||le})),ye(["y","yy","yyy","yyyy"],0),ye(["yo"],(function(e,t,n,r){var i;n._locale._eraYearOrdinalRegex&&(i=e.match(n._locale._eraYearOrdinalRegex)),n._locale.eraYearOrdinalParse?t[0]=n._locale.eraYearOrdinalParse(e,i):t[0]=parseInt(e,10)})),I(0,["gg",2],0,(function(){return this.weekYear()%100})),I(0,["GG",2],0,(function(){return this.isoWeekYear()%100})),un("gggg","weekYear"),un("ggggg","weekYear"),un("GGGG","isoWeekYear"),un("GGGGG","isoWeekYear"),B("weekYear","gg"),B("isoWeekYear","GG"),W("weekYear",1),W("isoWeekYear",1),pe("G",ce),pe("g",ce),pe("GG",re,X),pe("gg",re,X),pe("GGGG",se,te),pe("gggg",se,te),pe("GGGGG",ue,ne),pe("ggggg",ue,ne),be(["gggg","ggggg","GGGG","GGGGG"],(function(e,t,n,r){t[r.substr(0,2)]=q(e)})),be(["gg","GG"],(function(e,t,n,r){t[r]=i.parseTwoDigitYear(e)})),I("Q",0,"Qo","quarter"),B("quarter","Q"),W("quarter",7),pe("Q",Z),ye("Q",(function(e,t){t[1]=3*(q(e)-1)})),I("D",["DD",2],"Do","date"),B("date","D"),W("date",9),pe("D",re),pe("DD",re,X),pe("Do",(function(e,t){return e?t._dayOfMonthOrdinalParse||t._ordinalParse:t._dayOfMonthOrdinalParseLenient})),ye(["D","DD"],2),ye("Do",(function(e,t){t[2]=q(e.match(re)[0])}));var dn=G("Date",!0);I("DDD",["DDDD",3],"DDDo","dayOfYear"),B("dayOfYear","DDD"),W("dayOfYear",4),pe("DDD",oe),pe("DDDD",ee),ye(["DDD","DDDD"],(function(e,t,n){n._dayOfYear=q(e)})),I("m",["mm",2],0,"minute"),B("minute","m"),W("minute",14),pe("m",re),pe("mm",re,X),ye(["m","mm"],4);var fn=G("Minutes",!1);I("s",["ss",2],0,"second"),B("second","s"),W("second",15),pe("s",re),pe("ss",re,X),ye(["s","ss"],5);var hn,pn,mn=G("Seconds",!1);for(I("S",0,0,(function(){return~~(this.millisecond()/100)})),I(0,["SS",2],0,(function(){return~~(this.millisecond()/10)})),I(0,["SSS",3],0,"millisecond"),I(0,["SSSS",4],0,(function(){return 10*this.millisecond()})),I(0,["SSSSS",5],0,(function(){return 100*this.millisecond()})),I(0,["SSSSSS",6],0,(function(){return 1e3*this.millisecond()})),I(0,["SSSSSSS",7],0,(function(){return 1e4*this.millisecond()})),I(0,["SSSSSSSS",8],0,(function(){return 1e5*this.millisecond()})),I(0,["SSSSSSSSS",9],0,(function(){return 1e6*this.millisecond()})),B("millisecond","ms"),W("millisecond",16),pe("S",oe,Z),pe("SS",oe,X),pe("SSS",oe,ee),hn="SSSS";hn.length<=9;hn+="S")pe(hn,le);function _n(e,t){t[6]=q(1e3*("0."+e))}for(hn="S";hn.length<=9;hn+="S")ye(hn,_n);pn=G("Milliseconds",!1),I("z",0,0,"zoneAbbr"),I("zz",0,0,"zoneName");var gn=w.prototype;function vn(e){return e}gn.add=qt,gn.calendar=function(e,t){1===arguments.length&&(arguments[0]?Qt(arguments[0])?(e=arguments[0],t=void 0):Kt(arguments[0])&&(t=arguments[0],e=void 0):(e=void 0,t=void 0));var n=e||Dt(),r=Ht(n,this).startOf("day"),a=i.calendarFormat(this,r)||"sameElse",o=t&&(T(t[a])?t[a].call(this,n):t[a]);return this.format(o||this.localeData().calendar(a,this,Dt(n)))},gn.clone=function(){return new w(this)},gn.diff=function(e,t,n){var r,i,a;if(!this.isValid())return NaN;if(!(r=Ht(e,this)).isValid())return NaN;switch(i=6e4*(r.utcOffset()-this.utcOffset()),t=z(t)){case"year":a=Zt(this,r)/12;break;case"month":a=Zt(this,r);break;case"quarter":a=Zt(this,r)/3;break;case"second":a=(this-r)/1e3;break;case"minute":a=(this-r)/6e4;break;case"hour":a=(this-r)/36e5;break;case"day":a=(this-r-i)/864e5;break;case"week":a=(this-r-i)/6048e5;break;default:a=this-r}return n?a:U(a)},gn.endOf=function(e){var t,n;if(void 0===(e=z(e))||"millisecond"===e||!this.isValid())return this;switch(n=this._isUTC?an:rn,e){case"year":t=n(this.year()+1,0,1)-1;break;case"quarter":t=n(this.year(),this.month()-this.month()%3+3,1)-1;break;case"month":t=n(this.year(),this.month()+1,1)-1;break;case"week":t=n(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case"isoWeek":t=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case"day":case"date":t=n(this.year(),this.month(),this.date()+1)-1;break;case"hour":t=this._d.valueOf(),t+=36e5-nn(t+(this._isUTC?0:6e4*this.utcOffset()),36e5)-1;break;case"minute":t=this._d.valueOf(),t+=6e4-nn(t,6e4)-1;break;case"second":t=this._d.valueOf(),t+=1e3-nn(t,1e3)-1}return this._d.setTime(t),i.updateOffset(this,!0),this},gn.format=function(e){e||(e=this.isUtc()?i.defaultFormatUtc:i.defaultFormat);var t=H(this,e);return this.localeData().postformat(t)},gn.from=function(e,t){return this.isValid()&&(A(e)&&e.isValid()||Dt(e).isValid())?$t({to:this,from:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},gn.fromNow=function(e){return this.from(Dt(),e)},gn.to=function(e,t){return this.isValid()&&(A(e)&&e.isValid()||Dt(e).isValid())?$t({from:this,to:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},gn.toNow=function(e){return this.to(Dt(),e)},gn.get=function(e){return T(this[e=z(e)])?this[e]():this},gn.invalidAt=function(){return m(this).overflow},gn.isAfter=function(e,t){var n=A(e)?e:Dt(e);return!(!this.isValid()||!n.isValid())&&("millisecond"===(t=z(t)||"millisecond")?this.valueOf()>n.valueOf():n.valueOf()9999?H(n,t?"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ"):T(Date.prototype.toISOString)?t?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace("Z",H(n,"Z")):H(n,t?"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYY-MM-DD[T]HH:mm:ss.SSSZ")},gn.inspect=function(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var e,t,n,r="moment",i="";return this.isLocal()||(r=0===this.utcOffset()?"moment.utc":"moment.parseZone",i="Z"),e="["+r+'("]',t=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",n=i+'[")]',this.format(e+t+"-MM-DD[T]HH:mm:ss.SSS"+n)},"undefined"!=typeof Symbol&&null!=Symbol.for&&(gn[Symbol.for("nodejs.util.inspect.custom")]=function(){return"Moment<"+this.format()+">"}),gn.toJSON=function(){return this.isValid()?this.toISOString():null},gn.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},gn.unix=function(){return Math.floor(this.valueOf()/1e3)},gn.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},gn.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},gn.eraName=function(){var e,t,n,r=this.localeData().eras();for(e=0,t=r.length;ethis.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},gn.isLocal=function(){return!!this.isValid()&&!this._isUTC},gn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},gn.isUtc=Ft,gn.isUTC=Ft,gn.zoneAbbr=function(){return this._isUTC?"UTC":""},gn.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},gn.dates=M("dates accessor is deprecated. Use date instead.",dn),gn.months=M("months accessor is deprecated. Use month instead",Ye),gn.years=M("years accessor is deprecated. Use year instead",je),gn.zone=M("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",(function(e,t){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()})),gn.isDSTShifted=M("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",(function(){if(!l(this._isDSTShifted))return this._isDSTShifted;var e,t={};return b(t,this),(t=Mt(t))._a?(e=t._isUTC?p(t._a):Dt(t._a),this._isDSTShifted=this.isValid()&&function(e,t,n){var r,i=Math.min(e.length,t.length),a=Math.abs(e.length-t.length),o=0;for(r=0;r0):this._isDSTShifted=!1,this._isDSTShifted}));var yn=Y.prototype;function bn(e,t,n,r){var i=ut(),a=p().set(r,t);return i[n](a,e)}function wn(e,t,n){if(c(e)&&(t=e,e=void 0),e=e||"",null!=t)return bn(e,t,n,"month");var r,i=[];for(r=0;r<12;r++)i[r]=bn(e,r,n,"month");return i}function An(e,t,n,r){"boolean"==typeof e?(c(t)&&(n=t,t=void 0),t=t||""):(n=t=e,e=!1,c(t)&&(n=t,t=void 0),t=t||"");var i,a=ut(),o=e?a._week.dow:0,s=[];if(null!=n)return bn(t,(n+o)%7,r,"day");for(i=0;i<7;i++)s[i]=bn(t,(i+o)%7,r,"day");return s}yn.calendar=function(e,t,n){var r=this._calendar[e]||this._calendar.sameElse;return T(r)?r.call(t,n):r},yn.longDateFormat=function(e){var t=this._longDateFormat[e],n=this._longDateFormat[e.toUpperCase()];return t||!n?t:(this._longDateFormat[e]=n.match(E).map((function(e){return"MMMM"===e||"MM"===e||"DD"===e||"dddd"===e?e.slice(1):e})).join(""),this._longDateFormat[e])},yn.invalidDate=function(){return this._invalidDate},yn.ordinal=function(e){return this._ordinal.replace("%d",e)},yn.preparse=vn,yn.postformat=vn,yn.relativeTime=function(e,t,n,r){var i=this._relativeTime[n];return T(i)?i(e,t,n,r):i.replace(/%d/i,e)},yn.pastFuture=function(e,t){var n=this._relativeTime[e>0?"future":"past"];return T(n)?n(t):n.replace(/%s/i,t)},yn.set=function(e){var t,n;for(n in e)s(e,n)&&(T(t=e[n])?this[n]=t:this["_"+n]=t);this._config=e,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+"|"+/\d{1,2}/.source)},yn.eras=function(e,t){var n,r,a,o=this._eras||ut("en")._eras;for(n=0,r=o.length;n=0)return u[r]},yn.erasConvertYear=function(e,t){var n=e.since<=e.until?1:-1;return void 0===t?i(e.since).year():i(e.since).year()+(t-e.offset)*n},yn.erasAbbrRegex=function(e){return s(this,"_erasAbbrRegex")||sn.call(this),e?this._erasAbbrRegex:this._erasRegex},yn.erasNameRegex=function(e){return s(this,"_erasNameRegex")||sn.call(this),e?this._erasNameRegex:this._erasRegex},yn.erasNarrowRegex=function(e){return s(this,"_erasNarrowRegex")||sn.call(this),e?this._erasNarrowRegex:this._erasRegex},yn.months=function(e,t){return e?a(this._months)?this._months[e.month()]:this._months[(this._months.isFormat||ke).test(t)?"format":"standalone"][e.month()]:a(this._months)?this._months:this._months.standalone},yn.monthsShort=function(e,t){return e?a(this._monthsShort)?this._monthsShort[e.month()]:this._monthsShort[ke.test(t)?"format":"standalone"][e.month()]:a(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},yn.monthsParse=function(e,t,n){var r,i,a;if(this._monthsParseExact)return Te.call(this,e,t,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),r=0;r<12;r++){if(i=p([2e3,r]),n&&!this._longMonthsParse[r]&&(this._longMonthsParse[r]=new RegExp("^"+this.months(i,"").replace(".","")+"$","i"),this._shortMonthsParse[r]=new RegExp("^"+this.monthsShort(i,"").replace(".","")+"$","i")),n||this._monthsParse[r]||(a="^"+this.months(i,"")+"|^"+this.monthsShort(i,""),this._monthsParse[r]=new RegExp(a.replace(".",""),"i")),n&&"MMMM"===t&&this._longMonthsParse[r].test(e))return r;if(n&&"MMM"===t&&this._shortMonthsParse[r].test(e))return r;if(!n&&this._monthsParse[r].test(e))return r}},yn.monthsRegex=function(e){return this._monthsParseExact?(s(this,"_monthsRegex")||Ce.call(this),e?this._monthsStrictRegex:this._monthsRegex):(s(this,"_monthsRegex")||(this._monthsRegex=Le),this._monthsStrictRegex&&e?this._monthsStrictRegex:this._monthsRegex)},yn.monthsShortRegex=function(e){return this._monthsParseExact?(s(this,"_monthsRegex")||Ce.call(this),e?this._monthsShortStrictRegex:this._monthsShortRegex):(s(this,"_monthsShortRegex")||(this._monthsShortRegex=De),this._monthsShortStrictRegex&&e?this._monthsShortStrictRegex:this._monthsShortRegex)},yn.week=function(e){return Ne(e,this._week.dow,this._week.doy).week},yn.firstDayOfYear=function(){return this._week.doy},yn.firstDayOfWeek=function(){return this._week.dow},yn.weekdays=function(e,t){var n=a(this._weekdays)?this._weekdays:this._weekdays[e&&!0!==e&&this._weekdays.isFormat.test(t)?"format":"standalone"];return!0===e?Be(n,this._week.dow):e?n[e.day()]:n},yn.weekdaysMin=function(e){return!0===e?Be(this._weekdaysMin,this._week.dow):e?this._weekdaysMin[e.day()]:this._weekdaysMin},yn.weekdaysShort=function(e){return!0===e?Be(this._weekdaysShort,this._week.dow):e?this._weekdaysShort[e.day()]:this._weekdaysShort},yn.weekdaysParse=function(e,t,n){var r,i,a;if(this._weekdaysParseExact)return qe.call(this,e,t,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),r=0;r<7;r++){if(i=p([2e3,1]).day(r),n&&!this._fullWeekdaysParse[r]&&(this._fullWeekdaysParse[r]=new RegExp("^"+this.weekdays(i,"").replace(".","\\.?")+"$","i"),this._shortWeekdaysParse[r]=new RegExp("^"+this.weekdaysShort(i,"").replace(".","\\.?")+"$","i"),this._minWeekdaysParse[r]=new RegExp("^"+this.weekdaysMin(i,"").replace(".","\\.?")+"$","i")),this._weekdaysParse[r]||(a="^"+this.weekdays(i,"")+"|^"+this.weekdaysShort(i,"")+"|^"+this.weekdaysMin(i,""),this._weekdaysParse[r]=new RegExp(a.replace(".",""),"i")),n&&"dddd"===t&&this._fullWeekdaysParse[r].test(e))return r;if(n&&"ddd"===t&&this._shortWeekdaysParse[r].test(e))return r;if(n&&"dd"===t&&this._minWeekdaysParse[r].test(e))return r;if(!n&&this._weekdaysParse[r].test(e))return r}},yn.weekdaysRegex=function(e){return this._weekdaysParseExact?(s(this,"_weekdaysRegex")||Ge.call(this),e?this._weekdaysStrictRegex:this._weekdaysRegex):(s(this,"_weekdaysRegex")||(this._weekdaysRegex=We),this._weekdaysStrictRegex&&e?this._weekdaysStrictRegex:this._weekdaysRegex)},yn.weekdaysShortRegex=function(e){return this._weekdaysParseExact?(s(this,"_weekdaysRegex")||Ge.call(this),e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(s(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=Ve),this._weekdaysShortStrictRegex&&e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},yn.weekdaysMinRegex=function(e){return this._weekdaysParseExact?(s(this,"_weekdaysRegex")||Ge.call(this),e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(s(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=Ue),this._weekdaysMinStrictRegex&&e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},yn.isPM=function(e){return"p"===(e+"").toLowerCase().charAt(0)},yn.meridiem=function(e,t,n){return e>11?n?"pm":"PM":n?"am":"AM"},ot("en",{eras:[{since:"0001-01-01",until:1/0,offset:1,name:"Anno Domini",narrow:"AD",abbr:"AD"},{since:"0000-12-31",until:-1/0,offset:1,name:"Before Christ",narrow:"BC",abbr:"BC"}],dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10;return e+(1===q(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th")}}),i.lang=M("moment.lang is deprecated. Use moment.locale instead.",ot),i.langData=M("moment.langData is deprecated. Use moment.localeData instead.",ut);var xn=Math.abs;function Mn(e,t,n,r){var i=$t(t,n);return e._milliseconds+=r*i._milliseconds,e._days+=r*i._days,e._months+=r*i._months,e._bubble()}function kn(e){return e<0?Math.floor(e):Math.ceil(e)}function Dn(e){return 4800*e/146097}function Ln(e){return 146097*e/4800}function Tn(e){return function(){return this.as(e)}}var Sn=Tn("ms"),Yn=Tn("s"),Cn=Tn("m"),En=Tn("h"),jn=Tn("d"),On=Tn("w"),Pn=Tn("M"),In=Tn("Q"),Hn=Tn("y");function Nn(e){return function(){return this.isValid()?this._data[e]:NaN}}var Fn=Nn("milliseconds"),Bn=Nn("seconds"),zn=Nn("minutes"),$n=Nn("hours"),Rn=Nn("days"),Wn=Nn("months"),Vn=Nn("years"),Un=Math.round,qn={ss:44,s:45,m:45,h:22,d:26,w:null,M:11};function Gn(e,t,n,r,i){return i.relativeTime(t||1,!!n,e,r)}var Jn=Math.abs;function Qn(e){return(e>0)-(e<0)||+e}function Kn(){if(!this.isValid())return this.localeData().invalidDate();var e,t,n,r,i,a,o,s,u=Jn(this._milliseconds)/1e3,l=Jn(this._days),c=Jn(this._months),d=this.asSeconds();return d?(e=U(u/60),t=U(e/60),u%=60,e%=60,n=U(c/12),c%=12,r=u?u.toFixed(3).replace(/\.?0+$/,""):"",i=d<0?"-":"",a=Qn(this._months)!==Qn(d)?"-":"",o=Qn(this._days)!==Qn(d)?"-":"",s=Qn(this._milliseconds)!==Qn(d)?"-":"",i+"P"+(n?a+n+"Y":"")+(c?a+c+"M":"")+(l?o+l+"D":"")+(t||e||u?"T":"")+(t?s+t+"H":"")+(e?s+e+"M":"")+(u?s+r+"S":"")):"P0D"}var Zn=Ct.prototype;return Zn.isValid=function(){return this._isValid},Zn.abs=function(){var e=this._data;return this._milliseconds=xn(this._milliseconds),this._days=xn(this._days),this._months=xn(this._months),e.milliseconds=xn(e.milliseconds),e.seconds=xn(e.seconds),e.minutes=xn(e.minutes),e.hours=xn(e.hours),e.months=xn(e.months),e.years=xn(e.years),this},Zn.add=function(e,t){return Mn(this,e,t,1)},Zn.subtract=function(e,t){return Mn(this,e,t,-1)},Zn.as=function(e){if(!this.isValid())return NaN;var t,n,r=this._milliseconds;if("month"===(e=z(e))||"quarter"===e||"year"===e)switch(t=this._days+r/864e5,n=this._months+Dn(t),e){case"month":return n;case"quarter":return n/3;case"year":return n/12}else switch(t=this._days+Math.round(Ln(this._months)),e){case"week":return t/7+r/6048e5;case"day":return t+r/864e5;case"hour":return 24*t+r/36e5;case"minute":return 1440*t+r/6e4;case"second":return 86400*t+r/1e3;case"millisecond":return Math.floor(864e5*t)+r;default:throw new Error("Unknown unit "+e)}},Zn.asMilliseconds=Sn,Zn.asSeconds=Yn,Zn.asMinutes=Cn,Zn.asHours=En,Zn.asDays=jn,Zn.asWeeks=On,Zn.asMonths=Pn,Zn.asQuarters=In,Zn.asYears=Hn,Zn.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*q(this._months/12):NaN},Zn._bubble=function(){var e,t,n,r,i,a=this._milliseconds,o=this._days,s=this._months,u=this._data;return a>=0&&o>=0&&s>=0||a<=0&&o<=0&&s<=0||(a+=864e5*kn(Ln(s)+o),o=0,s=0),u.milliseconds=a%1e3,e=U(a/1e3),u.seconds=e%60,t=U(e/60),u.minutes=t%60,n=U(t/60),u.hours=n%24,o+=U(n/24),i=U(Dn(o)),s+=i,o-=kn(Ln(i)),r=U(s/12),s%=12,u.days=o,u.months=s,u.years=r,this},Zn.clone=function(){return $t(this)},Zn.get=function(e){return e=z(e),this.isValid()?this[e+"s"]():NaN},Zn.milliseconds=Fn,Zn.seconds=Bn,Zn.minutes=zn,Zn.hours=$n,Zn.days=Rn,Zn.weeks=function(){return U(this.days()/7)},Zn.months=Wn,Zn.years=Vn,Zn.humanize=function(e,t){if(!this.isValid())return this.localeData().invalidDate();var n,r,i=!1,a=qn;return"object"==typeof e&&(t=e,e=!1),"boolean"==typeof e&&(i=e),"object"==typeof t&&(a=Object.assign({},qn,t),null!=t.s&&null==t.ss&&(a.ss=t.s-1)),n=this.localeData(),r=function(e,t,n,r){var i=$t(e).abs(),a=Un(i.as("s")),o=Un(i.as("m")),s=Un(i.as("h")),u=Un(i.as("d")),l=Un(i.as("M")),c=Un(i.as("w")),d=Un(i.as("y")),f=a<=n.ss&&["s",a]||a0,f[4]=r,Gn.apply(null,f)}(this,!i,a,n),i&&(r=n.pastFuture(+this,r)),n.postformat(r)},Zn.toISOString=Kn,Zn.toString=Kn,Zn.toJSON=Kn,Zn.locale=Xt,Zn.localeData=tn,Zn.toIsoString=M("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",Kn),Zn.lang=en,I("X",0,0,"unix"),I("x",0,0,"valueOf"),pe("x",ce),pe("X",/[+-]?\d+(\.\d{1,3})?/),ye("X",(function(e,t,n){n._d=new Date(1e3*parseFloat(e))})),ye("x",(function(e,t,n){n._d=new Date(q(e))})),i.version="2.29.1",t=Dt,i.fn=gn,i.min=function(){var e=[].slice.call(arguments,0);return St("isBefore",e)},i.max=function(){var e=[].slice.call(arguments,0);return St("isAfter",e)},i.now=function(){return Date.now?Date.now():+new Date},i.utc=p,i.unix=function(e){return Dt(1e3*e)},i.months=function(e,t){return wn(e,t,"months")},i.isDate=d,i.locale=ot,i.invalid=g,i.duration=$t,i.isMoment=A,i.weekdays=function(e,t,n){return An(e,t,n,"weekdays")},i.parseZone=function(){return Dt.apply(null,arguments).parseZone()},i.localeData=ut,i.isDuration=Et,i.monthsShort=function(e,t){return wn(e,t,"monthsShort")},i.weekdaysMin=function(e,t,n){return An(e,t,n,"weekdaysMin")},i.defineLocale=st,i.updateLocale=function(e,t){if(null!=t){var n,r,i=et;null!=tt[e]&&null!=tt[e].parentLocale?tt[e].set(S(tt[e]._config,t)):(null!=(r=at(e))&&(i=r._config),t=S(i,t),null==r&&(t.abbr=e),(n=new Y(t)).parentLocale=tt[e],tt[e]=n),ot(e)}else null!=tt[e]&&(null!=tt[e].parentLocale?(tt[e]=tt[e].parentLocale,e===ot()&&ot(e)):null!=tt[e]&&delete tt[e]);return tt[e]},i.locales=function(){return k(tt)},i.weekdaysShort=function(e,t,n){return An(e,t,n,"weekdaysShort")},i.normalizeUnits=z,i.relativeTimeRounding=function(e){return void 0===e?Un:"function"==typeof e&&(Un=e,!0)},i.relativeTimeThreshold=function(e,t){return void 0!==qn[e]&&(void 0===t?qn[e]:(qn[e]=t,"s"===e&&(qn.ss=t-1),!0))},i.calendarFormat=function(e,t){var n=e.diff(t,"days",!0);return n<-6?"sameElse":n<-1?"lastWeek":n<0?"lastDay":n<1?"sameDay":n<2?"nextDay":n<7?"nextWeek":"sameElse"},i.prototype=gn,i.HTML5_FMT={DATETIME_LOCAL:"YYYY-MM-DDTHH:mm",DATETIME_LOCAL_SECONDS:"YYYY-MM-DDTHH:mm:ss",DATETIME_LOCAL_MS:"YYYY-MM-DDTHH:mm:ss.SSS",DATE:"YYYY-MM-DD",TIME:"HH:mm",TIME_SECONDS:"HH:mm:ss",TIME_MS:"HH:mm:ss.SSS",WEEK:"GGGG-[W]WW",MONTH:"YYYY-MM"},i}()}).call(this,n(12)(e))},function(e,t,n){"use strict";function r(e,t,n,r,i,a,o,s){var u,l="function"==typeof e?e.options:e;if(t&&(l.render=t,l.staticRenderFns=n,l._compiled=!0),r&&(l.functional=!0),a&&(l._scopeId="data-v-"+a),o?(u=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),i&&i.call(this,e),e&&e._registeredComponents&&e._registeredComponents.add(o)},l._ssrRegister=u):i&&(u=s?function(){i.call(this,(l.functional?this.parent:this).$root.$options.shadowRoot)}:i),u)if(l.functional){l._injectStyles=u;var c=l.render;l.render=function(e,t){return u.call(t),c(e,t)}}else{var d=l.beforeCreate;l.beforeCreate=d?[].concat(d,u):[u]}return{exports:e,options:l}}n.d(t,"a",(function(){return r}))},function(e,t,n){"use strict";(function(e){n.d(t,"a",(function(){return T})),n.d(t,"b",(function(){return M}));var r=("undefined"!=typeof window?window:void 0!==e?e:{}).__VUE_DEVTOOLS_GLOBAL_HOOK__;function i(e,t){if(void 0===t&&(t=[]),null===e||"object"!=typeof e)return e;var n,r=(n=function(t){return t.original===e},t.filter(n)[0]);if(r)return r.copy;var a=Array.isArray(e)?[]:{};return t.push({original:e,copy:a}),Object.keys(e).forEach((function(n){a[n]=i(e[n],t)})),a}function a(e,t){Object.keys(e).forEach((function(n){return t(e[n],n)}))}function o(e){return null!==e&&"object"==typeof e}var s=function(e,t){this.runtime=t,this._children=Object.create(null),this._rawModule=e;var n=e.state;this.state=("function"==typeof n?n():n)||{}},u={namespaced:{configurable:!0}};u.namespaced.get=function(){return!!this._rawModule.namespaced},s.prototype.addChild=function(e,t){this._children[e]=t},s.prototype.removeChild=function(e){delete this._children[e]},s.prototype.getChild=function(e){return this._children[e]},s.prototype.hasChild=function(e){return e in this._children},s.prototype.update=function(e){this._rawModule.namespaced=e.namespaced,e.actions&&(this._rawModule.actions=e.actions),e.mutations&&(this._rawModule.mutations=e.mutations),e.getters&&(this._rawModule.getters=e.getters)},s.prototype.forEachChild=function(e){a(this._children,e)},s.prototype.forEachGetter=function(e){this._rawModule.getters&&a(this._rawModule.getters,e)},s.prototype.forEachAction=function(e){this._rawModule.actions&&a(this._rawModule.actions,e)},s.prototype.forEachMutation=function(e){this._rawModule.mutations&&a(this._rawModule.mutations,e)},Object.defineProperties(s.prototype,u);var l=function(e){this.register([],e,!1)};l.prototype.get=function(e){return e.reduce((function(e,t){return e.getChild(t)}),this.root)},l.prototype.getNamespace=function(e){var t=this.root;return e.reduce((function(e,n){return e+((t=t.getChild(n)).namespaced?n+"/":"")}),"")},l.prototype.update=function(e){!function e(t,n,r){0;if(n.update(r),r.modules)for(var i in r.modules){if(!n.getChild(i))return void 0;e(t.concat(i),n.getChild(i),r.modules[i])}}([],this.root,e)},l.prototype.register=function(e,t,n){var r=this;void 0===n&&(n=!0);var i=new s(t,n);0===e.length?this.root=i:this.get(e.slice(0,-1)).addChild(e[e.length-1],i);t.modules&&a(t.modules,(function(t,i){r.register(e.concat(i),t,n)}))},l.prototype.unregister=function(e){var t=this.get(e.slice(0,-1)),n=e[e.length-1],r=t.getChild(n);r&&r.runtime&&t.removeChild(n)},l.prototype.isRegistered=function(e){var t=this.get(e.slice(0,-1)),n=e[e.length-1];return!!t&&t.hasChild(n)};var c;var d=function(e){var t=this;void 0===e&&(e={}),!c&&"undefined"!=typeof window&&window.Vue&&y(window.Vue);var n=e.plugins;void 0===n&&(n=[]);var i=e.strict;void 0===i&&(i=!1),this._committing=!1,this._actions=Object.create(null),this._actionSubscribers=[],this._mutations=Object.create(null),this._wrappedGetters=Object.create(null),this._modules=new l(e),this._modulesNamespaceMap=Object.create(null),this._subscribers=[],this._watcherVM=new c,this._makeLocalGettersCache=Object.create(null);var a=this,o=this.dispatch,s=this.commit;this.dispatch=function(e,t){return o.call(a,e,t)},this.commit=function(e,t,n){return s.call(a,e,t,n)},this.strict=i;var u=this._modules.root.state;_(this,u,[],this._modules.root),m(this,u),n.forEach((function(e){return e(t)})),(void 0!==e.devtools?e.devtools:c.config.devtools)&&function(e){r&&(e._devtoolHook=r,r.emit("vuex:init",e),r.on("vuex:travel-to-state",(function(t){e.replaceState(t)})),e.subscribe((function(e,t){r.emit("vuex:mutation",e,t)}),{prepend:!0}),e.subscribeAction((function(e,t){r.emit("vuex:action",e,t)}),{prepend:!0}))}(this)},f={state:{configurable:!0}};function h(e,t,n){return t.indexOf(e)<0&&(n&&n.prepend?t.unshift(e):t.push(e)),function(){var n=t.indexOf(e);n>-1&&t.splice(n,1)}}function p(e,t){e._actions=Object.create(null),e._mutations=Object.create(null),e._wrappedGetters=Object.create(null),e._modulesNamespaceMap=Object.create(null);var n=e.state;_(e,n,[],e._modules.root,!0),m(e,n,t)}function m(e,t,n){var r=e._vm;e.getters={},e._makeLocalGettersCache=Object.create(null);var i=e._wrappedGetters,o={};a(i,(function(t,n){o[n]=function(e,t){return function(){return e(t)}}(t,e),Object.defineProperty(e.getters,n,{get:function(){return e._vm[n]},enumerable:!0})}));var s=c.config.silent;c.config.silent=!0,e._vm=new c({data:{$$state:t},computed:o}),c.config.silent=s,e.strict&&function(e){e._vm.$watch((function(){return this._data.$$state}),(function(){0}),{deep:!0,sync:!0})}(e),r&&(n&&e._withCommit((function(){r._data.$$state=null})),c.nextTick((function(){return r.$destroy()})))}function _(e,t,n,r,i){var a=!n.length,o=e._modules.getNamespace(n);if(r.namespaced&&(e._modulesNamespaceMap[o],e._modulesNamespaceMap[o]=r),!a&&!i){var s=g(t,n.slice(0,-1)),u=n[n.length-1];e._withCommit((function(){c.set(s,u,r.state)}))}var l=r.context=function(e,t,n){var r=""===t,i={dispatch:r?e.dispatch:function(n,r,i){var a=v(n,r,i),o=a.payload,s=a.options,u=a.type;return s&&s.root||(u=t+u),e.dispatch(u,o)},commit:r?e.commit:function(n,r,i){var a=v(n,r,i),o=a.payload,s=a.options,u=a.type;s&&s.root||(u=t+u),e.commit(u,o,s)}};return Object.defineProperties(i,{getters:{get:r?function(){return e.getters}:function(){return function(e,t){if(!e._makeLocalGettersCache[t]){var n={},r=t.length;Object.keys(e.getters).forEach((function(i){if(i.slice(0,r)===t){var a=i.slice(r);Object.defineProperty(n,a,{get:function(){return e.getters[i]},enumerable:!0})}})),e._makeLocalGettersCache[t]=n}return e._makeLocalGettersCache[t]}(e,t)}},state:{get:function(){return g(e.state,n)}}}),i}(e,o,n);r.forEachMutation((function(t,n){!function(e,t,n,r){(e._mutations[t]||(e._mutations[t]=[])).push((function(t){n.call(e,r.state,t)}))}(e,o+n,t,l)})),r.forEachAction((function(t,n){var r=t.root?n:o+n,i=t.handler||t;!function(e,t,n,r){(e._actions[t]||(e._actions[t]=[])).push((function(t){var i,a=n.call(e,{dispatch:r.dispatch,commit:r.commit,getters:r.getters,state:r.state,rootGetters:e.getters,rootState:e.state},t);return(i=a)&&"function"==typeof i.then||(a=Promise.resolve(a)),e._devtoolHook?a.catch((function(t){throw e._devtoolHook.emit("vuex:error",t),t})):a}))}(e,r,i,l)})),r.forEachGetter((function(t,n){!function(e,t,n,r){if(e._wrappedGetters[t])return void 0;e._wrappedGetters[t]=function(e){return n(r.state,r.getters,e.state,e.getters)}}(e,o+n,t,l)})),r.forEachChild((function(r,a){_(e,t,n.concat(a),r,i)}))}function g(e,t){return t.reduce((function(e,t){return e[t]}),e)}function v(e,t,n){return o(e)&&e.type&&(n=t,t=e,e=e.type),{type:e,payload:t,options:n}}function y(e){c&&e===c||function(e){if(Number(e.version.split(".")[0])>=2)e.mixin({beforeCreate:n});else{var t=e.prototype._init;e.prototype._init=function(e){void 0===e&&(e={}),e.init=e.init?[n].concat(e.init):n,t.call(this,e)}}function n(){var e=this.$options;e.store?this.$store="function"==typeof e.store?e.store():e.store:e.parent&&e.parent.$store&&(this.$store=e.parent.$store)}}(c=e)}f.state.get=function(){return this._vm._data.$$state},f.state.set=function(e){0},d.prototype.commit=function(e,t,n){var r=this,i=v(e,t,n),a=i.type,o=i.payload,s=(i.options,{type:a,payload:o}),u=this._mutations[a];u&&(this._withCommit((function(){u.forEach((function(e){e(o)}))})),this._subscribers.slice().forEach((function(e){return e(s,r.state)})))},d.prototype.dispatch=function(e,t){var n=this,r=v(e,t),i=r.type,a=r.payload,o={type:i,payload:a},s=this._actions[i];if(s){try{this._actionSubscribers.slice().filter((function(e){return e.before})).forEach((function(e){return e.before(o,n.state)}))}catch(e){0}var u=s.length>1?Promise.all(s.map((function(e){return e(a)}))):s[0](a);return new Promise((function(e,t){u.then((function(t){try{n._actionSubscribers.filter((function(e){return e.after})).forEach((function(e){return e.after(o,n.state)}))}catch(e){0}e(t)}),(function(e){try{n._actionSubscribers.filter((function(e){return e.error})).forEach((function(t){return t.error(o,n.state,e)}))}catch(e){0}t(e)}))}))}},d.prototype.subscribe=function(e,t){return h(e,this._subscribers,t)},d.prototype.subscribeAction=function(e,t){return h("function"==typeof e?{before:e}:e,this._actionSubscribers,t)},d.prototype.watch=function(e,t,n){var r=this;return this._watcherVM.$watch((function(){return e(r.state,r.getters)}),t,n)},d.prototype.replaceState=function(e){var t=this;this._withCommit((function(){t._vm._data.$$state=e}))},d.prototype.registerModule=function(e,t,n){void 0===n&&(n={}),"string"==typeof e&&(e=[e]),this._modules.register(e,t),_(this,this.state,e,this._modules.get(e),n.preserveState),m(this,this.state)},d.prototype.unregisterModule=function(e){var t=this;"string"==typeof e&&(e=[e]),this._modules.unregister(e),this._withCommit((function(){var n=g(t.state,e.slice(0,-1));c.delete(n,e[e.length-1])})),p(this)},d.prototype.hasModule=function(e){return"string"==typeof e&&(e=[e]),this._modules.isRegistered(e)},d.prototype.hotUpdate=function(e){this._modules.update(e),p(this,!0)},d.prototype._withCommit=function(e){var t=this._committing;this._committing=!0,e(),this._committing=t},Object.defineProperties(d.prototype,f);var b=D((function(e,t){var n={};return k(t).forEach((function(t){var r=t.key,i=t.val;n[r]=function(){var t=this.$store.state,n=this.$store.getters;if(e){var r=L(this.$store,"mapState",e);if(!r)return;t=r.context.state,n=r.context.getters}return"function"==typeof i?i.call(this,t,n):t[i]},n[r].vuex=!0})),n})),w=D((function(e,t){var n={};return k(t).forEach((function(t){var r=t.key,i=t.val;n[r]=function(){for(var t=[],n=arguments.length;n--;)t[n]=arguments[n];var r=this.$store.commit;if(e){var a=L(this.$store,"mapMutations",e);if(!a)return;r=a.context.commit}return"function"==typeof i?i.apply(this,[r].concat(t)):r.apply(this.$store,[i].concat(t))}})),n})),A=D((function(e,t){var n={};return k(t).forEach((function(t){var r=t.key,i=t.val;i=e+i,n[r]=function(){if(!e||L(this.$store,"mapGetters",e))return this.$store.getters[i]},n[r].vuex=!0})),n})),x=D((function(e,t){var n={};return k(t).forEach((function(t){var r=t.key,i=t.val;n[r]=function(){for(var t=[],n=arguments.length;n--;)t[n]=arguments[n];var r=this.$store.dispatch;if(e){var a=L(this.$store,"mapActions",e);if(!a)return;r=a.context.dispatch}return"function"==typeof i?i.apply(this,[r].concat(t)):r.apply(this.$store,[i].concat(t))}})),n})),M=function(e){return{mapState:b.bind(null,e),mapGetters:A.bind(null,e),mapMutations:w.bind(null,e),mapActions:x.bind(null,e)}};function k(e){return function(e){return Array.isArray(e)||o(e)}(e)?Array.isArray(e)?e.map((function(e){return{key:e,val:e}})):Object.keys(e).map((function(t){return{key:t,val:e[t]}})):[]}function D(e){return function(t,n){return"string"!=typeof t?(n=t,t=""):"/"!==t.charAt(t.length-1)&&(t+="/"),e(t,n)}}function L(e,t,n){return e._modulesNamespaceMap[n]}function T(e){void 0===e&&(e={});var t=e.collapsed;void 0===t&&(t=!0);var n=e.filter;void 0===n&&(n=function(e,t,n){return!0});var r=e.transformer;void 0===r&&(r=function(e){return e});var a=e.mutationTransformer;void 0===a&&(a=function(e){return e});var o=e.actionFilter;void 0===o&&(o=function(e,t){return!0});var s=e.actionTransformer;void 0===s&&(s=function(e){return e});var u=e.logMutations;void 0===u&&(u=!0);var l=e.logActions;void 0===l&&(l=!0);var c=e.logger;return void 0===c&&(c=console),function(e){var d=i(e.state);void 0!==c&&(u&&e.subscribe((function(e,o){var s=i(o);if(n(e,d,s)){var u=C(),l=a(e),f="mutation "+e.type+u;S(c,f,t),c.log("%c prev state","color: #9E9E9E; font-weight: bold",r(d)),c.log("%c mutation","color: #03A9F4; font-weight: bold",l),c.log("%c next state","color: #4CAF50; font-weight: bold",r(s)),Y(c)}d=s})),l&&e.subscribeAction((function(e,n){if(o(e,n)){var r=C(),i=s(e),a="action "+e.type+r;S(c,a,t),c.log("%c action","color: #03A9F4; font-weight: bold",i),Y(c)}})))}}function S(e,t,n){var r=n?e.groupCollapsed:e.group;try{r.call(e,t)}catch(n){e.log(t)}}function Y(e){try{e.groupEnd()}catch(t){e.log("—— log end ——")}}function C(){var e=new Date;return" @ "+E(e.getHours(),2)+":"+E(e.getMinutes(),2)+":"+E(e.getSeconds(),2)+"."+E(e.getMilliseconds(),3)}function E(e,t){return n="0",r=t-e.toString().length,new Array(r+1).join(n)+e;var n,r}var j={Store:d,install:y,version:"3.6.2",mapState:b,mapMutations:w,mapGetters:A,mapActions:x,createNamespacedHelpers:M,createLogger:T};t.c=j}).call(this,n(6))},function(e,t,n){e.exports=n(236)},function(e,t,n){"use strict";var r=n(53),i=Object.prototype.toString;function a(e){return"[object Array]"===i.call(e)}function o(e){return void 0===e}function s(e){return null!==e&&"object"==typeof e}function u(e){if("[object Object]"!==i.call(e))return!1;var t=Object.getPrototypeOf(e);return null===t||t===Object.prototype}function l(e){return"[object Function]"===i.call(e)}function c(e,t){if(null!=e)if("object"!=typeof e&&(e=[e]),a(e))for(var n=0,r=e.length;n/g,">").replace(/"/g,""").replace(/'/g,"'"))})),e}var x={beforeCreate:function(){var e=this.$options;if(e.i18n=e.i18n||(e.__i18n?{}:null),e.i18n)if(e.i18n instanceof Z){if(e.__i18n)try{var t=e.i18n&&e.i18n.messages?e.i18n.messages:{};e.__i18n.forEach((function(e){t=b(t,JSON.parse(e))})),Object.keys(t).forEach((function(n){e.i18n.mergeLocaleMessage(n,t[n])}))}catch(e){0}this._i18n=e.i18n,this._i18nWatcher=this._i18n.watchI18nData()}else if(f(e.i18n)){var n=this.$root&&this.$root.$i18n&&this.$root.$i18n instanceof Z?this.$root.$i18n:null;if(n&&(e.i18n.root=this.$root,e.i18n.formatter=n.formatter,e.i18n.fallbackLocale=n.fallbackLocale,e.i18n.formatFallbackMessages=n.formatFallbackMessages,e.i18n.silentTranslationWarn=n.silentTranslationWarn,e.i18n.silentFallbackWarn=n.silentFallbackWarn,e.i18n.pluralizationRules=n.pluralizationRules,e.i18n.preserveDirectiveContent=n.preserveDirectiveContent),e.__i18n)try{var r=e.i18n&&e.i18n.messages?e.i18n.messages:{};e.__i18n.forEach((function(e){r=b(r,JSON.parse(e))})),e.i18n.messages=r}catch(e){0}var i=e.i18n.sharedMessages;i&&f(i)&&(e.i18n.messages=b(e.i18n.messages,i)),this._i18n=new Z(e.i18n),this._i18nWatcher=this._i18n.watchI18nData(),(void 0===e.i18n.sync||e.i18n.sync)&&(this._localeWatcher=this.$i18n.watchLocale()),n&&n.onComponentInstanceCreated(this._i18n)}else 0;else this.$root&&this.$root.$i18n&&this.$root.$i18n instanceof Z?this._i18n=this.$root.$i18n:e.parent&&e.parent.$i18n&&e.parent.$i18n instanceof Z&&(this._i18n=e.parent.$i18n)},beforeMount:function(){var e=this.$options;e.i18n=e.i18n||(e.__i18n?{}:null),e.i18n?(e.i18n instanceof Z||f(e.i18n))&&(this._i18n.subscribeDataChanging(this),this._subscribing=!0):(this.$root&&this.$root.$i18n&&this.$root.$i18n instanceof Z||e.parent&&e.parent.$i18n&&e.parent.$i18n instanceof Z)&&(this._i18n.subscribeDataChanging(this),this._subscribing=!0)},beforeDestroy:function(){if(this._i18n){var e=this;this.$nextTick((function(){e._subscribing&&(e._i18n.unsubscribeDataChanging(e),delete e._subscribing),e._i18nWatcher&&(e._i18nWatcher(),e._i18n.destroyVM(),delete e._i18nWatcher),e._localeWatcher&&(e._localeWatcher(),delete e._localeWatcher)}))}}},M={name:"i18n",functional:!0,props:{tag:{type:[String,Boolean,Object],default:"span"},path:{type:String,required:!0},locale:{type:String},places:{type:[Array,Object]}},render:function(e,t){var n=t.data,r=t.parent,i=t.props,a=t.slots,o=r.$i18n;if(o){var s=i.path,u=i.locale,l=i.places,c=a(),d=o.i(s,u,function(e){var t;for(t in e)if("default"!==t)return!1;return Boolean(t)}(c)||l?function(e,t){var n=t?function(e){0;return Array.isArray(e)?e.reduce(D,{}):Object.assign({},e)}(t):{};if(!e)return n;var r=(e=e.filter((function(e){return e.tag||""!==e.text.trim()}))).every(L);0;return e.reduce(r?k:D,n)}(c.default,l):c),f=i.tag&&!0!==i.tag||!1===i.tag?i.tag:"span";return f?e(f,n,d):d}}};function k(e,t){return t.data&&t.data.attrs&&t.data.attrs.place&&(e[t.data.attrs.place]=t),e}function D(e,t,n){return e[n]=t,e}function L(e){return Boolean(e.data&&e.data.attrs&&e.data.attrs.place)}var T,S={name:"i18n-n",functional:!0,props:{tag:{type:[String,Boolean,Object],default:"span"},value:{type:Number,required:!0},format:{type:[String,Object]},locale:{type:String}},render:function(e,t){var n=t.props,r=t.parent,i=t.data,a=r.$i18n;if(!a)return null;var s=null,u=null;c(n.format)?s=n.format:l(n.format)&&(n.format.key&&(s=n.format.key),u=Object.keys(n.format).reduce((function(e,t){var r;return g(o,t)?Object.assign({},e,((r={})[t]=n.format[t],r)):e}),null));var d=n.locale||a.locale,f=a._ntp(n.value,d,s,u),h=f.map((function(e,t){var n,r=i.scopedSlots&&i.scopedSlots[e.type];return r?r(((n={})[e.type]=e.value,n.index=t,n.parts=f,n)):e.value})),p=n.tag&&!0!==n.tag||!1===n.tag?n.tag:"span";return p?e(p,{attrs:i.attrs,class:i.class,staticClass:i.staticClass},h):h}};function Y(e,t,n){j(e,n)&&O(e,t,n)}function C(e,t,n,r){if(j(e,n)){var i=n.context.$i18n;(function(e,t){var n=t.context;return e._locale===n.$i18n.locale})(e,n)&&w(t.value,t.oldValue)&&w(e._localeMessage,i.getLocaleMessage(i.locale))||O(e,t,n)}}function E(e,t,n,r){if(n.context){var i=n.context.$i18n||{};t.modifiers.preserve||i.preserveDirectiveContent||(e.textContent=""),e._vt=void 0,delete e._vt,e._locale=void 0,delete e._locale,e._localeMessage=void 0,delete e._localeMessage}else s("Vue instance does not exists in VNode context")}function j(e,t){var n=t.context;return n?!!n.$i18n||(s("VueI18n instance does not exists in Vue instance"),!1):(s("Vue instance does not exists in VNode context"),!1)}function O(e,t,n){var r,i,a=function(e){var t,n,r,i;c(e)?t=e:f(e)&&(t=e.path,n=e.locale,r=e.args,i=e.choice);return{path:t,locale:n,args:r,choice:i}}(t.value),o=a.path,u=a.locale,l=a.args,d=a.choice;if(o||u||l)if(o){var h=n.context;e._vt=e.textContent=null!=d?(r=h.$i18n).tc.apply(r,[o,d].concat(P(u,l))):(i=h.$i18n).t.apply(i,[o].concat(P(u,l))),e._locale=h.$i18n.locale,e._localeMessage=h.$i18n.getLocaleMessage(h.$i18n.locale)}else s("`path` is required in v-t directive");else s("value type not supported")}function P(e,t){var n=[];return e&&n.push(e),t&&(Array.isArray(t)||f(t))&&n.push(t),n}function I(e){I.installed=!0;(T=e).version&&Number(T.version.split(".")[0]);(function(e){e.prototype.hasOwnProperty("$i18n")||Object.defineProperty(e.prototype,"$i18n",{get:function(){return this._i18n}}),e.prototype.$t=function(e){for(var t=[],n=arguments.length-1;n-- >0;)t[n]=arguments[n+1];var r=this.$i18n;return r._t.apply(r,[e,r.locale,r._getMessages(),this].concat(t))},e.prototype.$tc=function(e,t){for(var n=[],r=arguments.length-2;r-- >0;)n[r]=arguments[r+2];var i=this.$i18n;return i._tc.apply(i,[e,i.locale,i._getMessages(),this,t].concat(n))},e.prototype.$te=function(e,t){var n=this.$i18n;return n._te(e,n.locale,n._getMessages(),t)},e.prototype.$d=function(e){for(var t,n=[],r=arguments.length-1;r-- >0;)n[r]=arguments[r+1];return(t=this.$i18n).d.apply(t,[e].concat(n))},e.prototype.$n=function(e){for(var t,n=[],r=arguments.length-1;r-- >0;)n[r]=arguments[r+1];return(t=this.$i18n).n.apply(t,[e].concat(n))}})(T),T.mixin(x),T.directive("t",{bind:Y,update:C,unbind:E}),T.component(M.name,M),T.component(S.name,S),T.config.optionMergeStrategies.i18n=function(e,t){return void 0===t?e:t}}var H=function(){this._caches=Object.create(null)};H.prototype.interpolate=function(e,t){if(!t)return[e];var n=this._caches[e];return n||(n=function(e){var t=[],n=0,r="";for(;n0)d--,c=4,f[0]();else{if(d=0,void 0===n)return!1;if(!1===(n=R(n)))return!1;f[1]()}};null!==c;)if(l++,"\\"!==(t=e[l])||!h()){if(i=$(t),8===(a=(s=B[c])[i]||s.else||8))return;if(c=a[0],(o=f[a[1]])&&(r=void 0===(r=a[2])?t:r,!1===o()))return;if(7===c)return u}}(e))&&(this._cache[e]=t),t||[]},W.prototype.getPathValue=function(e,t){if(!l(e))return null;var n=this.parsePath(t);if(0===n.length)return null;for(var r=n.length,i=e,a=0;a/,q=/(?:@(?:\.[a-z]+)?:(?:[\w\-_|.]+|\([\w\-_|.]+\)))/g,G=/^@(?:\.([a-z]+))?:/,J=/[()]/g,Q={upper:function(e){return e.toLocaleUpperCase()},lower:function(e){return e.toLocaleLowerCase()},capitalize:function(e){return""+e.charAt(0).toLocaleUpperCase()+e.substr(1)}},K=new H,Z=function(e){var t=this;void 0===e&&(e={}),!T&&"undefined"!=typeof window&&window.Vue&&I(window.Vue);var n=e.locale||"en-US",r=!1!==e.fallbackLocale&&(e.fallbackLocale||"en-US"),i=e.messages||{},a=e.dateTimeFormats||{},o=e.numberFormats||{};this._vm=null,this._formatter=e.formatter||K,this._modifiers=e.modifiers||{},this._missing=e.missing||null,this._root=e.root||null,this._sync=void 0===e.sync||!!e.sync,this._fallbackRoot=void 0===e.fallbackRoot||!!e.fallbackRoot,this._formatFallbackMessages=void 0!==e.formatFallbackMessages&&!!e.formatFallbackMessages,this._silentTranslationWarn=void 0!==e.silentTranslationWarn&&e.silentTranslationWarn,this._silentFallbackWarn=void 0!==e.silentFallbackWarn&&!!e.silentFallbackWarn,this._dateTimeFormatters={},this._numberFormatters={},this._path=new W,this._dataListeners=[],this._componentInstanceCreatedListener=e.componentInstanceCreatedListener||null,this._preserveDirectiveContent=void 0!==e.preserveDirectiveContent&&!!e.preserveDirectiveContent,this.pluralizationRules=e.pluralizationRules||{},this._warnHtmlInMessage=e.warnHtmlInMessage||"off",this._postTranslation=e.postTranslation||null,this._escapeParameterHtml=e.escapeParameterHtml||!1,this.getChoiceIndex=function(e,n){var r=Object.getPrototypeOf(t);if(r&&r.getChoiceIndex)return r.getChoiceIndex.call(t,e,n);var i,a;return t.locale in t.pluralizationRules?t.pluralizationRules[t.locale].apply(t,[e,n]):(i=e,a=n,i=Math.abs(i),2===a?i?i>1?1:0:1:i?Math.min(i,2):0)},this._exist=function(e,n){return!(!e||!n)&&(!h(t._path.getPathValue(e,n))||!!e[n])},"warn"!==this._warnHtmlInMessage&&"error"!==this._warnHtmlInMessage||Object.keys(i).forEach((function(e){t._checkLocaleMessage(e,t._warnHtmlInMessage,i[e])})),this._initVM({locale:n,fallbackLocale:r,messages:i,dateTimeFormats:a,numberFormats:o})},X={vm:{configurable:!0},messages:{configurable:!0},dateTimeFormats:{configurable:!0},numberFormats:{configurable:!0},availableLocales:{configurable:!0},locale:{configurable:!0},fallbackLocale:{configurable:!0},formatFallbackMessages:{configurable:!0},missing:{configurable:!0},formatter:{configurable:!0},silentTranslationWarn:{configurable:!0},silentFallbackWarn:{configurable:!0},preserveDirectiveContent:{configurable:!0},warnHtmlInMessage:{configurable:!0},postTranslation:{configurable:!0}};Z.prototype._checkLocaleMessage=function(e,t,n){var r=function(e,t,n,i){if(f(n))Object.keys(n).forEach((function(a){var o=n[a];f(o)?(i.push(a),i.push("."),r(e,t,o,i),i.pop(),i.pop()):(i.push(a),r(e,t,o,i),i.pop())}));else if(u(n))n.forEach((function(n,a){f(n)?(i.push("["+a+"]"),i.push("."),r(e,t,n,i),i.pop(),i.pop()):(i.push("["+a+"]"),r(e,t,n,i),i.pop())}));else if(c(n)){if(U.test(n)){var a="Detected HTML in message '"+n+"' of keypath '"+i.join("")+"' at '"+t+"'. Consider component interpolation with '' to avoid XSS. See https://bit.ly/2ZqJzkp";"warn"===e?s(a):"error"===e&&function(e,t){"undefined"!=typeof console&&(console.error("[vue-i18n] "+e),t&&console.error(t.stack))}(a)}}};r(t,e,n,[])},Z.prototype._initVM=function(e){var t=T.config.silent;T.config.silent=!0,this._vm=new T({data:e}),T.config.silent=t},Z.prototype.destroyVM=function(){this._vm.$destroy()},Z.prototype.subscribeDataChanging=function(e){this._dataListeners.push(e)},Z.prototype.unsubscribeDataChanging=function(e){!function(e,t){if(e.length){var n=e.indexOf(t);if(n>-1)e.splice(n,1)}}(this._dataListeners,e)},Z.prototype.watchI18nData=function(){var e=this;return this._vm.$watch("$data",(function(){for(var t=e._dataListeners.length;t--;)T.nextTick((function(){e._dataListeners[t]&&e._dataListeners[t].$forceUpdate()}))}),{deep:!0})},Z.prototype.watchLocale=function(){if(!this._sync||!this._root)return null;var e=this._vm;return this._root.$i18n.vm.$watch("locale",(function(t){e.$set(e,"locale",t),e.$forceUpdate()}),{immediate:!0})},Z.prototype.onComponentInstanceCreated=function(e){this._componentInstanceCreatedListener&&this._componentInstanceCreatedListener(e,this)},X.vm.get=function(){return this._vm},X.messages.get=function(){return _(this._getMessages())},X.dateTimeFormats.get=function(){return _(this._getDateTimeFormats())},X.numberFormats.get=function(){return _(this._getNumberFormats())},X.availableLocales.get=function(){return Object.keys(this.messages).sort()},X.locale.get=function(){return this._vm.locale},X.locale.set=function(e){this._vm.$set(this._vm,"locale",e)},X.fallbackLocale.get=function(){return this._vm.fallbackLocale},X.fallbackLocale.set=function(e){this._localeChainCache={},this._vm.$set(this._vm,"fallbackLocale",e)},X.formatFallbackMessages.get=function(){return this._formatFallbackMessages},X.formatFallbackMessages.set=function(e){this._formatFallbackMessages=e},X.missing.get=function(){return this._missing},X.missing.set=function(e){this._missing=e},X.formatter.get=function(){return this._formatter},X.formatter.set=function(e){this._formatter=e},X.silentTranslationWarn.get=function(){return this._silentTranslationWarn},X.silentTranslationWarn.set=function(e){this._silentTranslationWarn=e},X.silentFallbackWarn.get=function(){return this._silentFallbackWarn},X.silentFallbackWarn.set=function(e){this._silentFallbackWarn=e},X.preserveDirectiveContent.get=function(){return this._preserveDirectiveContent},X.preserveDirectiveContent.set=function(e){this._preserveDirectiveContent=e},X.warnHtmlInMessage.get=function(){return this._warnHtmlInMessage},X.warnHtmlInMessage.set=function(e){var t=this,n=this._warnHtmlInMessage;if(this._warnHtmlInMessage=e,n!==e&&("warn"===e||"error"===e)){var r=this._getMessages();Object.keys(r).forEach((function(e){t._checkLocaleMessage(e,t._warnHtmlInMessage,r[e])}))}},X.postTranslation.get=function(){return this._postTranslation},X.postTranslation.set=function(e){this._postTranslation=e},Z.prototype._getMessages=function(){return this._vm.messages},Z.prototype._getDateTimeFormats=function(){return this._vm.dateTimeFormats},Z.prototype._getNumberFormats=function(){return this._vm.numberFormats},Z.prototype._warnDefault=function(e,t,n,r,i,a){if(!h(n))return n;if(this._missing){var o=this._missing.apply(null,[e,t,r,i]);if(c(o))return o}else 0;if(this._formatFallbackMessages){var s=m.apply(void 0,i);return this._render(t,a,s.params,t)}return t},Z.prototype._isFallbackRoot=function(e){return!e&&!h(this._root)&&this._fallbackRoot},Z.prototype._isSilentFallbackWarn=function(e){return this._silentFallbackWarn instanceof RegExp?this._silentFallbackWarn.test(e):this._silentFallbackWarn},Z.prototype._isSilentFallback=function(e,t){return this._isSilentFallbackWarn(t)&&(this._isFallbackRoot()||e!==this.fallbackLocale)},Z.prototype._isSilentTranslationWarn=function(e){return this._silentTranslationWarn instanceof RegExp?this._silentTranslationWarn.test(e):this._silentTranslationWarn},Z.prototype._interpolate=function(e,t,n,r,i,a,o){if(!t)return null;var s,l=this._path.getPathValue(t,n);if(u(l)||f(l))return l;if(h(l)){if(!f(t))return null;if(!c(s=t[n])&&!p(s))return null}else{if(!c(l)&&!p(l))return null;s=l}return c(s)&&(s.indexOf("@:")>=0||s.indexOf("@.")>=0)&&(s=this._link(e,t,s,r,"raw",a,o)),this._render(s,i,a,n)},Z.prototype._link=function(e,t,n,r,i,a,o){var s=n,l=s.match(q);for(var c in l)if(l.hasOwnProperty(c)){var d=l[c],f=d.match(G),h=f[0],p=f[1],m=d.replace(h,"").replace(J,"");if(g(o,m))return s;o.push(m);var _=this._interpolate(e,t,m,r,"raw"===i?"string":i,"raw"===i?void 0:a,o);if(this._isFallbackRoot(_)){if(!this._root)throw Error("unexpected error");var v=this._root.$i18n;_=v._translate(v._getMessages(),v.locale,v.fallbackLocale,m,r,i,a)}_=this._warnDefault(e,m,_,r,u(a)?a:[a],i),this._modifiers.hasOwnProperty(p)?_=this._modifiers[p](_):Q.hasOwnProperty(p)&&(_=Q[p](_)),o.pop(),s=_?s.replace(d,_):s}return s},Z.prototype._createMessageContext=function(e){var t=u(e)?e:[],n=l(e)?e:{};return{list:function(e){return t[e]},named:function(e){return n[e]}}},Z.prototype._render=function(e,t,n,r){if(p(e))return e(this._createMessageContext(n));var i=this._formatter.interpolate(e,n,r);return i||(i=K.interpolate(e,n,r)),"string"!==t||c(i)?i:i.join("")},Z.prototype._appendItemToChain=function(e,t,n){var r=!1;return g(e,t)||(r=!0,t&&(r="!"!==t[t.length-1],t=t.replace(/!/g,""),e.push(t),n&&n[t]&&(r=n[t]))),r},Z.prototype._appendLocaleToChain=function(e,t,n){var r,i=t.split("-");do{var a=i.join("-");r=this._appendItemToChain(e,a,n),i.splice(-1,1)}while(i.length&&!0===r);return r},Z.prototype._appendBlockToChain=function(e,t,n){for(var r=!0,i=0;i0;)a[o]=arguments[o+4];if(!e)return"";var s=m.apply(void 0,a);this._escapeParameterHtml&&(s.params=A(s.params));var u=s.locale||t,l=this._translate(n,u,this.fallbackLocale,e,r,"string",s.params);if(this._isFallbackRoot(l)){if(!this._root)throw Error("unexpected error");return(i=this._root).$t.apply(i,[e].concat(a))}return l=this._warnDefault(u,e,l,r,a,"string"),this._postTranslation&&null!=l&&(l=this._postTranslation(l,e)),l},Z.prototype.t=function(e){for(var t,n=[],r=arguments.length-1;r-- >0;)n[r]=arguments[r+1];return(t=this)._t.apply(t,[e,this.locale,this._getMessages(),null].concat(n))},Z.prototype._i=function(e,t,n,r,i){var a=this._translate(n,t,this.fallbackLocale,e,r,"raw",i);if(this._isFallbackRoot(a)){if(!this._root)throw Error("unexpected error");return this._root.$i18n.i(e,t,i)}return this._warnDefault(t,e,a,r,[i],"raw")},Z.prototype.i=function(e,t,n){return e?(c(t)||(t=this.locale),this._i(e,t,this._getMessages(),null,n)):""},Z.prototype._tc=function(e,t,n,r,i){for(var a,o=[],s=arguments.length-5;s-- >0;)o[s]=arguments[s+5];if(!e)return"";void 0===i&&(i=1);var u={count:i,n:i},l=m.apply(void 0,o);return l.params=Object.assign(u,l.params),o=null===l.locale?[l.params]:[l.locale,l.params],this.fetchChoice((a=this)._t.apply(a,[e,t,n,r].concat(o)),i)},Z.prototype.fetchChoice=function(e,t){if(!e||!c(e))return null;var n=e.split("|");return n[t=this.getChoiceIndex(t,n.length)]?n[t].trim():e},Z.prototype.tc=function(e,t){for(var n,r=[],i=arguments.length-2;i-- >0;)r[i]=arguments[i+2];return(n=this)._tc.apply(n,[e,this.locale,this._getMessages(),null,t].concat(r))},Z.prototype._te=function(e,t,n){for(var r=[],i=arguments.length-3;i-- >0;)r[i]=arguments[i+3];var a=m.apply(void 0,r).locale||t;return this._exist(n[a],e)},Z.prototype.te=function(e,t){return this._te(e,this.locale,this._getMessages(),t)},Z.prototype.getLocaleMessage=function(e){return _(this._vm.messages[e]||{})},Z.prototype.setLocaleMessage=function(e,t){"warn"!==this._warnHtmlInMessage&&"error"!==this._warnHtmlInMessage||this._checkLocaleMessage(e,this._warnHtmlInMessage,t),this._vm.$set(this._vm.messages,e,t)},Z.prototype.mergeLocaleMessage=function(e,t){"warn"!==this._warnHtmlInMessage&&"error"!==this._warnHtmlInMessage||this._checkLocaleMessage(e,this._warnHtmlInMessage,t),this._vm.$set(this._vm.messages,e,b(void 0!==this._vm.messages[e]&&Object.keys(this._vm.messages[e]).length?this._vm.messages[e]:{},t))},Z.prototype.getDateTimeFormat=function(e){return _(this._vm.dateTimeFormats[e]||{})},Z.prototype.setDateTimeFormat=function(e,t){this._vm.$set(this._vm.dateTimeFormats,e,t),this._clearDateTimeFormat(e,t)},Z.prototype.mergeDateTimeFormat=function(e,t){this._vm.$set(this._vm.dateTimeFormats,e,b(this._vm.dateTimeFormats[e]||{},t)),this._clearDateTimeFormat(e,t)},Z.prototype._clearDateTimeFormat=function(e,t){for(var n in t){var r=e+"__"+n;this._dateTimeFormatters.hasOwnProperty(r)&&delete this._dateTimeFormatters[r]}},Z.prototype._localizeDateTime=function(e,t,n,r,i){for(var a=t,o=r[a],s=this._getLocaleChain(t,n),u=0;u0;)t[n]=arguments[n+1];var r=this.locale,i=null;return 1===t.length?c(t[0])?i=t[0]:l(t[0])&&(t[0].locale&&(r=t[0].locale),t[0].key&&(i=t[0].key)):2===t.length&&(c(t[0])&&(i=t[0]),c(t[1])&&(r=t[1])),this._d(e,r,i)},Z.prototype.getNumberFormat=function(e){return _(this._vm.numberFormats[e]||{})},Z.prototype.setNumberFormat=function(e,t){this._vm.$set(this._vm.numberFormats,e,t),this._clearNumberFormat(e,t)},Z.prototype.mergeNumberFormat=function(e,t){this._vm.$set(this._vm.numberFormats,e,b(this._vm.numberFormats[e]||{},t)),this._clearNumberFormat(e,t)},Z.prototype._clearNumberFormat=function(e,t){for(var n in t){var r=e+"__"+n;this._numberFormatters.hasOwnProperty(r)&&delete this._numberFormatters[r]}},Z.prototype._getNumberFormatter=function(e,t,n,r,i,a){for(var o=t,s=r[o],u=this._getLocaleChain(t,n),l=0;l0;)t[n]=arguments[n+1];var r=this.locale,i=null,a=null;return 1===t.length?c(t[0])?i=t[0]:l(t[0])&&(t[0].locale&&(r=t[0].locale),t[0].key&&(i=t[0].key),a=Object.keys(t[0]).reduce((function(e,n){var r;return g(o,n)?Object.assign({},e,((r={})[n]=t[0][n],r)):e}),null)):2===t.length&&(c(t[0])&&(i=t[0]),c(t[1])&&(r=t[1])),this._n(e,r,i,a)},Z.prototype._ntp=function(e,t,n,r){if(!Z.availabilities.numberFormat)return[];if(!n)return(r?new Intl.NumberFormat(t,r):new Intl.NumberFormat(t)).formatToParts(e);var i=this._getNumberFormatter(e,t,this.fallbackLocale,this._getNumberFormats(),n,r),a=i&&i.formatToParts(e);if(this._isFallbackRoot(a)){if(!this._root)throw Error("unexpected error");return this._root.$i18n._ntp(e,t,n,r)}return a||[]},Object.defineProperties(Z.prototype,X),Object.defineProperty(Z,"availabilities",{get:function(){if(!V){var e="undefined"!=typeof Intl;V={dateTimeFormat:e&&void 0!==Intl.DateTimeFormat,numberFormat:e&&void 0!==Intl.NumberFormat}}return V}}),Z.install=I,Z.version="8.22.4";var ee=Z,te=n(8),ne=n.n(te);function re(e,t){var n=arguments;if(null==e)throw new TypeError("Cannot convert undefined or null to object");for(var r=Object(e),i=1;i0&&this.$select(this.activeIndex),this.startInterval()},beforeDestroy:function(){this.stopInterval()},methods:{run:function(e,t){var n,r=this,i=t||0;n=e>i?["next","left"]:["prev","right"],this.slides[e].slideClass[n[0]]=!0,this.$nextTick((function(){r.slides[e].$el.offsetHeight,r.slides.forEach((function(t,r){r===i?(t.slideClass.active=!0,t.slideClass[n[1]]=!0):r===e&&(t.slideClass[n[1]]=!0)})),r.timeoutId=setTimeout((function(){r.$select(e),r.$emit("change",e),r.timeoutId=0}),600)}))},startInterval:function(){var e=this;this.stopInterval(),this.interval>0&&(this.intervalId=setInterval((function(){e.next()}),this.interval))},stopInterval:function(){clearInterval(this.intervalId),this.intervalId=0},resetAllSlideClass:function(){this.slides.forEach((function(e){e.slideClass.active=!1,e.slideClass.left=!1,e.slideClass.right=!1,e.slideClass.next=!1,e.slideClass.prev=!1}))},$select:function(e){this.resetAllSlideClass(),this.slides[e].slideClass.active=!0},select:function(e){0===this.timeoutId&&e!==this.activeIndex&&(ie(this.value)?this.$emit("input",e):(this.run(e,this.activeIndex),this.activeIndex=e))},prev:function(){this.select(0===this.activeIndex?this.slides.length-1:this.activeIndex-1)},next:function(){this.select(this.activeIndex===this.slides.length-1?0:this.activeIndex+1)}}},fe=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"carousel slide",attrs:{"data-ride":"carousel"},on:{mouseenter:e.stopInterval,mouseleave:e.startInterval}},[e.indicators?e._t("indicators",[n("ol",{staticClass:"carousel-indicators"},e._l(e.slides,(function(t,r){return n("li",{class:{active:r===e.activeIndex},on:{click:function(t){return e.select(r)}}})})),0)],{select:e.select,activeIndex:e.activeIndex}):e._e(),e._v(" "),n("div",{staticClass:"carousel-inner",attrs:{role:"listbox"}},[e._t("default")],2),e._v(" "),e.controls?n("a",{staticClass:"left carousel-control",attrs:{href:"#",role:"button"},on:{click:function(t){return t.preventDefault(),e.prev()}}},[n("span",{class:e.iconControlLeft,attrs:{"aria-hidden":"true"}}),e._v(" "),n("span",{staticClass:"sr-only"},[e._v("Previous")])]):e._e(),e._v(" "),e.controls?n("a",{staticClass:"right carousel-control",attrs:{href:"#",role:"button"},on:{click:function(t){return t.preventDefault(),e.next()}}},[n("span",{class:e.iconControlRight,attrs:{"aria-hidden":"true"}}),e._v(" "),n("span",{staticClass:"sr-only"},[e._v("Next")])]):e._e()],2)};fe._withStripped=!0;var he=ce({render:fe,staticRenderFns:[]},void 0,de,void 0,!1,void 0,!1,void 0,void 0,void 0);function pe(e,t){if(Array.isArray(e)){var n=e.indexOf(t);n>=0&&e.splice(n,1)}}function me(e){return Array.prototype.slice.call(e||[])}function _e(e,t,n){return n.indexOf(e)===t}var ge={data:function(){return{slideClass:{active:!1,prev:!1,next:!1,left:!1,right:!1}}},created:function(){try{this.$parent.slides.push(this)}catch(e){throw new Error("Slide parent must be Carousel.")}},beforeDestroy:function(){pe(this.$parent&&this.$parent.slides,this)}},ve=function(){var e=this.$createElement;return(this._self._c||e)("div",{staticClass:"item",class:this.slideClass},[this._t("default")],2)};ve._withStripped=!0;var ye=ce({render:ve,staticRenderFns:[]},void 0,ge,void 0,!1,void 0,!1,void 0,void 0,void 0),be="mouseenter",we="mouseleave",Ae="mousedown",xe="mouseup",Me="focus",ke="blur",De="click",Le="input",Te="keydown",Se="keyup",Ye="resize",Ce="scroll",Ee="touchend",je="click",Oe="hover",Pe="focus",Ie="hover-focus",He="outside-click",Ne="top",Fe="right",Be="bottom",ze="left";function $e(e){return window.getComputedStyle(e)}function Re(){return{width:Math.max(document.documentElement.clientWidth,window.innerWidth)||0,height:Math.max(document.documentElement.clientHeight,window.innerHeight)||0}}var We=null,Ve=null;function Ue(e){void 0===e&&(e=!1);var t=Re();if(null!==We&&!e&&t.height===Ve.height&&t.width===Ve.width)return We;if("loading"===document.readyState)return null;var n=document.createElement("div"),r=document.createElement("div");return n.style.width=r.style.width=n.style.height=r.style.height="100px",n.style.overflow="scroll",r.style.overflow="hidden",document.body.appendChild(n),document.body.appendChild(r),We=Math.abs(n.scrollHeight-r.scrollHeight),document.body.removeChild(n),document.body.removeChild(r),Ve=t,We}function qe(e,t,n){e.addEventListener(t,n)}function Ge(e,t,n){e.removeEventListener(t,n)}function Je(e){return e&&e.nodeType===Node.ELEMENT_NODE}function Qe(e){Je(e)&&Je(e.parentNode)&&e.parentNode.removeChild(e)}function Ke(){Element.prototype.matches||(Element.prototype.matches=Element.prototype.matchesSelector||Element.prototype.mozMatchesSelector||Element.prototype.msMatchesSelector||Element.prototype.oMatchesSelector||Element.prototype.webkitMatchesSelector||function(e){for(var t=(this.document||this.ownerDocument).querySelectorAll(e),n=t.length;--n>=0&&t.item(n)!==this;);return n>-1})}function Ze(e,t){if(Je(e))if(e.className){var n=e.className.split(" ");n.indexOf(t)<0&&(n.push(t),e.className=n.join(" "))}else e.className=t}function Xe(e,t){if(Je(e)&&e.className){for(var n=e.className.split(" "),r=[],i=0,a=n.length;i=i.height,l=r.left+r.width/2>=i.width/2,s=r.right-r.width/2+i.width/2<=a.width;break;case Be:u=r.bottom+i.height<=a.height,l=r.left+r.width/2>=i.width/2,s=r.right-r.width/2+i.width/2<=a.width;break;case Fe:s=r.right+i.width<=a.width,o=r.top+r.height/2>=i.height/2,u=r.bottom-r.height/2+i.height/2<=a.height;break;case ze:l=r.left>=i.width,o=r.top+r.height/2>=i.height/2,u=r.bottom-r.height/2+i.height/2<=a.height}return o&&s&&u&&l}function tt(e){var t=e.scrollHeight>e.clientHeight,n=$e(e);return t||"scroll"===n.overflow||"scroll"===n.overflowY}function nt(e){var t=document.body;if(e)Xe(t,"modal-open"),t.style.paddingRight=null,me(document.querySelectorAll(".navbar-fixed-top, .navbar-fixed-bottom")).forEach((function(e){e.style.paddingRight=null}));else{var n=-1!==window.navigator.appVersion.indexOf("MSIE 10")||!!window.MSInputMethodContext&&!!document.documentMode;if((tt(document.documentElement)||tt(document.body))&&!n){var r=Ue();t.style.paddingRight=r+"px",me(document.querySelectorAll(".navbar-fixed-top, .navbar-fixed-bottom")).forEach((function(e){e.style.paddingRight=r+"px"}))}Ze(t,"modal-open")}}function rt(e,t,n){void 0===n&&(n=null),Ke();for(var r=[],i=e.parentElement;i;){if(i.matches(t))r.push(i);else if(n&&(n===i||i.matches(n)))break;i=i.parentElement}return r}function it(e){Je(e)&&(!e.getAttribute("tabindex")&&e.setAttribute("tabindex","-1"),e.focus())}function at(){return document.querySelectorAll(".modal-backdrop")}function ot(){return at().length}function st(e){return se(e)?document.querySelector(e):Je(e)?e:Je(e.$el)?e.$el:null}var ut={render:function(e){return e(this.tag,{},this.$slots.default)},props:{tag:{type:String,default:"div"},value:{type:Boolean,default:!1},transition:{type:Number,default:350}},data:function(){return{timeoutId:0}},watch:{value:function(e){this.toggle(e)}},mounted:function(){var e=this.$el;Ze(e,"collapse"),this.value&&Ze(e,"in")},methods:{toggle:function(e){var t=this;clearTimeout(this.timeoutId);var n=this.$el;if(e){this.$emit("show"),Xe(n,"collapse"),n.style.height="auto";var r=window.getComputedStyle(n).height;n.style.height=null,Ze(n,"collapsing"),n.offsetHeight,n.style.height=r,this.timeoutId=setTimeout((function(){Xe(n,"collapsing"),Ze(n,"collapse"),Ze(n,"in"),n.style.height=null,t.timeoutId=0,t.$emit("shown")}),this.transition)}else this.$emit("hide"),n.style.height=window.getComputedStyle(n).height,Xe(n,"in"),Xe(n,"collapse"),n.offsetHeight,n.style.height=null,Ze(n,"collapsing"),this.timeoutId=setTimeout((function(){Ze(n,"collapse"),Xe(n,"collapsing"),n.style.height=null,t.timeoutId=0,t.$emit("hidden")}),this.transition)}}},lt={render:function(e){return e(this.tag,{class:{"btn-group":"div"===this.tag,dropdown:!this.dropup,dropup:this.dropup,open:this.show}},[this.$slots.default,e("ul",{class:{"dropdown-menu":!0,"dropdown-menu-right":this.menuRight},ref:"dropdown"},[this.$slots.dropdown])])},props:{tag:{type:String,default:"div"},appendToBody:{type:Boolean,default:!1},value:Boolean,dropup:{type:Boolean,default:!1},menuRight:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},notCloseElements:Array,positionElement:null},data:function(){return{show:!1,triggerEl:void 0}},watch:{value:function(e){this.toggle(e)}},mounted:function(){this.initTrigger(),this.triggerEl&&(qe(this.triggerEl,De,this.toggle),qe(this.triggerEl,Te,this.onKeyPress)),qe(this.$refs.dropdown,Te,this.onKeyPress),qe(window,De,this.windowClicked),qe(window,Ee,this.windowClicked),this.value&&this.toggle(!0)},beforeDestroy:function(){this.removeDropdownFromBody(),this.triggerEl&&(Ge(this.triggerEl,De,this.toggle),Ge(this.triggerEl,Te,this.onKeyPress)),Ge(this.$refs.dropdown,Te,this.onKeyPress),Ge(window,De,this.windowClicked),Ge(window,Ee,this.windowClicked)},methods:{getFocusItem:function(){return this.$refs.dropdown.querySelector("li > a:focus")},onKeyPress:function(e){if(this.show){var t=this.$refs.dropdown,n=e.keyCode;if(27===n)this.toggle(!1),this.triggerEl&&this.triggerEl.focus();else if(13===n){var r=this.getFocusItem();r&&r.click()}else if(38===n||40===n){e.preventDefault(),e.stopPropagation();var i=this.getFocusItem(),a=t.querySelectorAll("li:not(.disabled) > a");if(i){for(var o=0;o0?it(a[o-1]):40===n&&o=0;o=a||s&&u}if(o){n=!0;break}}var l=this.$refs.dropdown.contains(t),c=this.$el.contains(t)&&!l,d=l&&"touchend"===e.type;c||n||d||this.toggle(!1)}},appendDropdownToBody:function(){try{var e=this.$refs.dropdown;e.style.display="block",document.body.appendChild(e),function(e,t,n){void 0===n&&(n={});var r=document.documentElement,i=(window.pageXOffset||r.scrollLeft)-(r.clientLeft||0),a=(window.pageYOffset||r.scrollTop)-(r.clientTop||0),o=t.getBoundingClientRect(),s=e.getBoundingClientRect();e.style.right="auto",e.style.bottom="auto",n.menuRight?e.style.left=i+o.left+o.width-s.width+"px":e.style.left=i+o.left+"px",n.dropup?e.style.top=a+o.top-s.height-4+"px":e.style.top=a+o.top+o.height+"px"}(e,this.positionElement||this.$el,this)}catch(e){}},removeDropdownFromBody:function(){try{var e=this.$refs.dropdown;e.removeAttribute("style"),this.$el.appendChild(e)}catch(e){}}}},ct={uiv:{datePicker:{clear:"Clear",today:"Today",month:"Month",month1:"January",month2:"February",month3:"March",month4:"April",month5:"May",month6:"June",month7:"July",month8:"August",month9:"September",month10:"October",month11:"November",month12:"December",year:"Year",week1:"Mon",week2:"Tue",week3:"Wed",week4:"Thu",week5:"Fri",week6:"Sat",week7:"Sun"},timePicker:{am:"AM",pm:"PM"},modal:{cancel:"Cancel",ok:"OK"},multiSelect:{placeholder:"Select...",filterPlaceholder:"Search..."}}},dt=function(){var e=Object.getPrototypeOf(this).$t;if(ae(e))try{return e.apply(this,arguments)}catch(e){return this.$t.apply(this,arguments)}},ft=function(e,t){var n;t=t||{};try{if(ie(n=dt.apply(this,arguments))&&!t.$$locale)return n}catch(e){}for(var r=e.split("."),i=t.$$locale||ct,a=0,o=r.length;a=0:r.value===r.inputValue,s={btn:!0,active:r.inputType?o:r.active,disabled:r.disabled,"btn-block":r.block};s["btn-"+r.type]=Boolean(r.type),s["btn-"+r.size]=Boolean(r.size);var u,l,c,d={click:function(e){r.disabled&&e instanceof Event&&(e.preventDefault(),e.stopPropagation())}};return r.href?(u="a",c=n,l=At(i,{on:d,class:s,attrs:{role:"button",href:r.href,target:r.target}})):r.to?(u="router-link",c=n,l=At(i,{nativeOn:d,class:s,props:{event:r.disabled?"":"click",to:r.to,replace:r.replace,append:r.append,exact:r.exact},attrs:{role:"button"}})):r.inputType?(u="label",l=At(i,{on:d,class:s}),c=[e("input",{attrs:{autocomplete:"off",type:r.inputType,checked:o?"checked":null,disabled:r.disabled},domProps:{checked:o},on:{input:function(e){e.stopPropagation()},change:function(){if("checkbox"===r.inputType){var e=r.value.slice();o?e.splice(e.indexOf(r.inputValue),1):e.push(r.inputValue),a.input(e)}else a.input(r.inputValue)}}}),n]):r.justified?(u=Mt,l={},c=[e("button",At(i,{on:d,class:s,attrs:{type:r.nativeType,disabled:r.disabled}}),n)]):(u="button",c=n,l=At(i,{on:d,class:s,attrs:{type:r.nativeType,disabled:r.disabled}})),e(u,l,c)},props:{justified:{type:Boolean,default:!1},type:{type:String,default:"default"},nativeType:{type:String,default:"button"},size:String,block:{type:Boolean,default:!1},active:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},value:null,inputValue:null,inputType:{type:String,validator:function(e){return"checkbox"===e||"radio"===e}}}},Dt={mixins:[mt],components:{Btn:kt},props:{value:{type:Boolean,default:!1},title:String,size:String,backdrop:{type:Boolean,default:!0},footer:{type:Boolean,default:!0},header:{type:Boolean,default:!0},cancelText:String,cancelType:{type:String,default:"default"},okText:String,okType:{type:String,default:"primary"},dismissBtn:{type:Boolean,default:!0},transition:{type:Number,default:150},autoFocus:{type:Boolean,default:!1},keyboard:{type:Boolean,default:!0},beforeClose:Function,zOffset:{type:Number,default:20},appendToBody:{type:Boolean,default:!1},displayStyle:{type:String,default:"block"}},data:function(){return{msg:""}},computed:{modalSizeClass:function(){var e;return(e={})["modal-"+this.size]=Boolean(this.size),e}},watch:{value:function(e){this.$toggle(e)}},mounted:function(){Qe(this.$refs.backdrop),qe(window,Ae,this.suppressBackgroundClose),qe(window,Se,this.onKeyPress),this.value&&this.$toggle(!0)},beforeDestroy:function(){clearTimeout(this.timeoutId),Qe(this.$refs.backdrop),Qe(this.$el),0===ot()&&nt(!0),Ge(window,Ae,this.suppressBackgroundClose),Ge(window,xe,this.unsuppressBackgroundClose),Ge(window,Se,this.onKeyPress)},methods:{onKeyPress:function(e){if(this.keyboard&&this.value&&27===e.keyCode){var t=this.$refs.backdrop,n=t.style.zIndex;n=n&&"auto"!==n?parseInt(n):0;for(var r=at(),i=r.length,a=0;an)return}this.toggle(!1)}},toggle:function(e,t){var n=this,r=!0;if(ae(this.beforeClose)&&(r=this.beforeClose(t)),ue())Promise.resolve(r).then((function(r){!e&&r&&(n.msg=t,n.$emit("input",e))}));else{if(!e&&!r)return;this.msg=t,this.$emit("input",e)}},$toggle:function(e){var t=this,n=this.$el,r=this.$refs.backdrop;clearTimeout(this.timeoutId),e?this.$nextTick((function(){var e=ot();if(document.body.appendChild(r),t.appendToBody&&document.body.appendChild(n),n.style.display=t.displayStyle,n.scrollTop=0,r.offsetHeight,nt(!1),Ze(r,"in"),Ze(n,"in"),e>0){var i=parseInt($e(n).zIndex)||1050,a=parseInt($e(r).zIndex)||1040,o=e*t.zOffset;n.style.zIndex=""+(i+o),r.style.zIndex=""+(a+o)}t.timeoutId=setTimeout((function(){if(t.autoFocus){var e=t.$el.querySelector('[data-action="auto-focus"]');e&&e.focus()}t.$emit("show"),t.timeoutId=0}),t.transition)})):(Xe(r,"in"),Xe(n,"in"),this.timeoutId=setTimeout((function(){n.style.display="none",Qe(r),t.appendToBody&&Qe(n),0===ot()&&nt(!0),t.$emit("hide",t.msg||"dismiss"),t.msg="",t.timeoutId=0,n.style.zIndex="",r.style.zIndex=""}),this.transition))},suppressBackgroundClose:function(e){e&&e.target===this.$el||(this.isCloseSuppressed=!0,qe(window,"mouseup",this.unsuppressBackgroundClose))},unsuppressBackgroundClose:function(){var e=this;this.isCloseSuppressed&&(Ge(window,"mouseup",this.unsuppressBackgroundClose),setTimeout((function(){e.isCloseSuppressed=!1}),1))},backdropClicked:function(e){this.backdrop&&!this.isCloseSuppressed&&this.toggle(!1)}}},Lt=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"modal",class:{fade:e.transition>0},attrs:{tabindex:"-1",role:"dialog"},on:{click:function(t){return t.target!==t.currentTarget?null:e.backdropClicked(t)}}},[n("div",{ref:"dialog",staticClass:"modal-dialog",class:e.modalSizeClass,attrs:{role:"document"}},[n("div",{staticClass:"modal-content"},[e.header?n("div",{staticClass:"modal-header"},[e._t("header",[e.dismissBtn?n("button",{staticClass:"close",staticStyle:{position:"relative","z-index":"1060"},attrs:{type:"button","aria-label":"Close"},on:{click:function(t){return e.toggle(!1)}}},[n("span",{attrs:{"aria-hidden":"true"}},[e._v("×")])]):e._e(),e._v(" "),n("h4",{staticClass:"modal-title"},[e._t("title",[e._v(e._s(e.title))])],2)])],2):e._e(),e._v(" "),n("div",{staticClass:"modal-body"},[e._t("default")],2),e._v(" "),e.footer?n("div",{staticClass:"modal-footer"},[e._t("footer",[n("btn",{attrs:{type:e.cancelType},on:{click:function(t){return e.toggle(!1,"cancel")}}},[n("span",[e._v(e._s(e.cancelText||e.t("uiv.modal.cancel")))])]),e._v(" "),n("btn",{attrs:{type:e.okType,"data-action":"auto-focus"},on:{click:function(t){return e.toggle(!1,"ok")}}},[n("span",[e._v(e._s(e.okText||e.t("uiv.modal.ok")))])])])],2):e._e()])]),e._v(" "),n("div",{ref:"backdrop",staticClass:"modal-backdrop",class:{fade:e.transition>0}})])};Lt._withStripped=!0;var Tt=ce({render:Lt,staticRenderFns:[]},void 0,Dt,void 0,!1,void 0,!1,void 0,void 0,void 0);function St(e){return(St="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function Yt(e){return function(e){if(Array.isArray(e)){for(var t=0,n=new Array(e.length);t1&&void 0!==arguments[1]&&arguments[1],n=e.to,r=e.from;if(n&&(r||!1!==t)&&this.transports[n])if(t)this.transports[n]=[];else{var i=this.$_getTransportIndex(e);if(i>=0){var a=this.transports[n].slice(0);a.splice(i,1),this.transports[n]=a}}},registerTarget:function(e,t,n){Ct&&(this.trackInstances&&!n&&this.targets[e]&&console.warn("[portal-vue]: Target ".concat(e," already exists")),this.$set(this.targets,e,Object.freeze([t])))},unregisterTarget:function(e){this.$delete(this.targets,e)},registerSource:function(e,t,n){Ct&&(this.trackInstances&&!n&&this.sources[e]&&console.warn("[portal-vue]: source ".concat(e," already exists")),this.$set(this.sources,e,Object.freeze([t])))},unregisterSource:function(e){this.$delete(this.sources,e)},hasTarget:function(e){return!(!this.targets[e]||!this.targets[e][0])},hasSource:function(e){return!(!this.sources[e]||!this.sources[e][0])},hasContentFor:function(e){return!!this.transports[e]&&!!this.transports[e].length},$_getTransportIndex:function(e){var t=e.to,n=e.from;for(var r in this.transports[t])if(this.transports[t][r].from===n)return+r;return-1}}}))(jt),Ht=1,Nt=ne.a.extend({name:"portal",props:{disabled:{type:Boolean},name:{type:String,default:function(){return String(Ht++)}},order:{type:Number,default:0},slim:{type:Boolean},slotProps:{type:Object,default:function(){return{}}},tag:{type:String,default:"DIV"},to:{type:String,default:function(){return String(Math.round(1e7*Math.random()))}}},created:function(){var e=this;this.$nextTick((function(){It.registerSource(e.name,e)}))},mounted:function(){this.disabled||this.sendUpdate()},updated:function(){this.disabled?this.clear():this.sendUpdate()},beforeDestroy:function(){It.unregisterSource(this.name),this.clear()},watch:{to:function(e,t){t&&t!==e&&this.clear(t),this.sendUpdate()}},methods:{clear:function(e){var t={from:this.name,to:e||this.to};It.close(t)},normalizeSlots:function(){return this.$scopedSlots.default?[this.$scopedSlots.default]:this.$slots.default},normalizeOwnChildren:function(e){return"function"==typeof e?e(this.slotProps):e},sendUpdate:function(){var e=this.normalizeSlots();if(e){var t={from:this.name,to:this.to,passengers:Yt(e),order:this.order};It.open(t)}else this.clear()}},render:function(e){var t=this.$slots.default||this.$scopedSlots.default||[],n=this.tag;return t&&this.disabled?t.length<=1&&this.slim?this.normalizeOwnChildren(t)[0]:e(n,[this.normalizeOwnChildren(t)]):this.slim?e():e(n,{class:{"v-portal":!0},style:{display:"none"},key:"v-portal-placeholder"})}}),Ft=ne.a.extend({name:"portalTarget",props:{multiple:{type:Boolean,default:!1},name:{type:String,required:!0},slim:{type:Boolean,default:!1},slotProps:{type:Object,default:function(){return{}}},tag:{type:String,default:"div"},transition:{type:[String,Object,Function]}},data:function(){return{transports:It.transports,firstRender:!0}},created:function(){var e=this;this.$nextTick((function(){It.registerTarget(e.name,e)}))},watch:{ownTransports:function(){this.$emit("change",this.children().length>0)},name:function(e,t){It.unregisterTarget(t),It.registerTarget(e,this)}},mounted:function(){var e=this;this.transition&&this.$nextTick((function(){e.firstRender=!1}))},beforeDestroy:function(){It.unregisterTarget(this.name)},computed:{ownTransports:function(){var e=this.transports[this.name]||[];return this.multiple?e:0===e.length?[]:[e[e.length-1]]},passengers:function(){return function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return e.reduce((function(e,n){var r=n.passengers[0],i="function"==typeof r?r(t):n.passengers;return e.concat(i)}),[])}(this.ownTransports,this.slotProps)}},methods:{children:function(){return 0!==this.passengers.length?this.passengers:this.$scopedSlots.default?this.$scopedSlots.default(this.slotProps):this.$slots.default||[]},noWrapper:function(){var e=this.slim&&!this.transition;return e&&this.children().length>1&&console.warn("[portal-vue]: PortalTarget with `slim` option received more than one child element."),e}},render:function(e){var t=this.noWrapper(),n=this.children(),r=this.transition||this.tag;return t?n[0]:this.slim&&!r?e():e(r,{props:{tag:this.transition&&this.tag?this.tag:void 0},class:{"vue-portal-target":!0}},n)}}),Bt=0,zt=["disabled","name","order","slim","slotProps","tag","to"],$t=["multiple","transition"];ne.a.extend({name:"MountingPortal",inheritAttrs:!1,props:{append:{type:[Boolean,String]},bail:{type:Boolean},mountTo:{type:String,required:!0},disabled:{type:Boolean},name:{type:String,default:function(){return"mounted_"+String(Bt++)}},order:{type:Number,default:0},slim:{type:Boolean},slotProps:{type:Object,default:function(){return{}}},tag:{type:String,default:"DIV"},to:{type:String,default:function(){return String(Math.round(1e7*Math.random()))}},multiple:{type:Boolean,default:!1},targetSlim:{type:Boolean},targetSlotProps:{type:Object,default:function(){return{}}},targetTag:{type:String,default:"div"},transition:{type:[String,Object,Function]}},created:function(){if("undefined"!=typeof document){var e=document.querySelector(this.mountTo);if(e){var t=this.$props;if(It.targets[t.name])t.bail?console.warn("[portal-vue]: Target ".concat(t.name," is already mounted.\n Aborting because 'bail: true' is set")):this.portalTarget=It.targets[t.name];else{var n=t.append;if(n){var r="string"==typeof n?n:"DIV",i=document.createElement(r);e.appendChild(i),e=i}var a=Et(this.$props,$t);a.slim=this.targetSlim,a.tag=this.targetTag,a.slotProps=this.targetSlotProps,a.name=this.to,this.portalTarget=new Ft({el:e,parent:this.$parent||this,propsData:a})}}else console.error("[portal-vue]: Mount Point '".concat(this.mountTo,"' not found in document"))}},beforeDestroy:function(){var e=this.portalTarget;if(this.append){var t=e.$el;t.parentNode.removeChild(t)}e.$destroy()},render:function(e){if(!this.portalTarget)return console.warn("[portal-vue] Target wasn't mounted"),e();if(!this.$scopedSlots.manual){var t=Et(this.$props,zt);return e(Nt,{props:t,attrs:this.$attrs,on:this.$listeners,scopedSlots:this.$scopedSlots},this.$slots.default)}var n=this.$scopedSlots.manual({to:this.to});return Array.isArray(n)&&(n=n[0]),n||e()}});var Rt={components:{Portal:Nt},props:{title:{type:String,default:"Tab Title"},disabled:{type:Boolean,default:!1},tabClasses:{type:Object,default:function(){return{}}},group:String,pullRight:{type:Boolean,default:!1},hidden:{type:Boolean,default:!1}},data:function(){return{active:!0,transition:150}},watch:{active:function(e){var t=this;e?setTimeout((function(){Ze(t.$el,"active"),t.$el.offsetHeight,Ze(t.$el,"in");try{t.$parent.$emit("changed",t.$parent.activeIndex)}catch(e){throw new Error(" parent must be .")}}),this.transition):(Xe(this.$el,"in"),setTimeout((function(){Xe(t.$el,"active")}),this.transition))}},created:function(){try{this.$parent.tabs.push(this)}catch(e){throw new Error(" parent must be .")}},beforeDestroy:function(){pe(this.$parent&&this.$parent.tabs,this)},methods:{show:function(){var e=this;this.$nextTick((function(){Ze(e.$el,"active"),Ze(e.$el,"in")}))}}},Wt=function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"tab-pane",class:{fade:this.transition>0},attrs:{role:"tabpanel"}},[this._t("default"),this._v(" "),t("portal",{attrs:{to:this._uid.toString()}},[this._t("title")],2)],2)};Wt._withStripped=!0;var Vt=ce({render:Wt,staticRenderFns:[]},void 0,Rt,void 0,!1,void 0,!1,void 0,void 0,void 0),Ut={components:{Dropdown:lt,PortalTarget:Ft},props:{value:{type:Number,validator:function(e){return e>=0}},transition:{type:Number,default:150},justified:Boolean,pills:Boolean,stacked:Boolean,customNavClass:null,customContentClass:null},data:function(){return{tabs:[],activeIndex:0}},watch:{value:{immediate:!0,handler:function(e){oe(e)&&(this.activeIndex=e,this.selectCurrent())}},tabs:function(e){var t=this;e.forEach((function(e,n){e.transition=t.transition,n===t.activeIndex&&e.show()})),this.selectCurrent()}},computed:{navClasses:function(){var e,t={nav:!0,"nav-justified":this.justified,"nav-tabs":!this.pills,"nav-pills":this.pills,"nav-stacked":this.stacked&&this.pills},n=this.customNavClass;return ie(n)?se(n)?re({},t,((e={})[n]=!0,e)):re({},t,n):t},contentClasses:function(){var e,t={"tab-content":!0},n=this.customContentClass;return ie(n)?se(n)?re({},t,((e={})[n]=!0,e)):re({},t,n):t},groupedTabs:function(){var e=[],t={};return this.tabs.forEach((function(n){n.group?(le(t,n.group)?e[t[n.group]].tabs.push(n):(e.push({tabs:[n],group:n.group}),t[n.group]=e.length-1),n.active&&(e[t[n.group]].active=!0),n.pullRight&&(e[t[n.group]].pullRight=!0)):e.push(n)})),e=e.map((function(e){return Array.isArray(e.tabs)&&(e.hidden=e.tabs.filter((function(e){return e.hidden})).length===e.tabs.length),e}))}},methods:{getTabClasses:function(e,t){return void 0===t&&(t=!1),re({active:e.active,disabled:e.disabled,"pull-right":e.pullRight&&!t},e.tabClasses)},selectCurrent:function(){var e=this,t=!1;this.tabs.forEach((function(n,r){r===e.activeIndex?(t=!n.active,n.active=!0):n.active=!1})),t&&this.$emit("change",this.activeIndex)},selectValidate:function(e){var t=this;ae(this.$listeners["before-change"])?this.$emit("before-change",this.activeIndex,e,(function(n){ie(n)||t.$select(e)})):this.$select(e)},select:function(e){this.tabs[e].disabled||e===this.activeIndex||this.selectValidate(e)},$select:function(e){oe(this.value)?this.$emit("input",e):(this.activeIndex=e,this.selectCurrent())}}},qt=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("section",[n("ul",{class:e.navClasses,attrs:{role:"tablist"}},[e._l(e.groupedTabs,(function(t,r){return[t.tabs?n("dropdown",{directives:[{name:"show",rawName:"v-show",value:!t.hidden,expression:"!tab.hidden"}],class:e.getTabClasses(t),attrs:{role:"presentation",tag:"li"}},[n("a",{staticClass:"dropdown-toggle",attrs:{role:"tab",href:"#"},on:{click:function(e){e.preventDefault()}}},[e._v(e._s(t.group)+" "),n("span",{staticClass:"caret"})]),e._v(" "),n("template",{slot:"dropdown"},e._l(t.tabs,(function(t){return n("li",{directives:[{name:"show",rawName:"v-show",value:!t.hidden,expression:"!subTab.hidden"}],class:e.getTabClasses(t,!0)},[n("a",{attrs:{href:"#"},on:{click:function(n){n.preventDefault(),e.select(e.tabs.indexOf(t))}}},[e._v(e._s(t.title))])])})),0)],2):n("li",{directives:[{name:"show",rawName:"v-show",value:!t.hidden,expression:"!tab.hidden"}],class:e.getTabClasses(t),attrs:{role:"presentation"}},[t.$slots.title?n("a",{attrs:{role:"tab",href:"#"},on:{click:function(n){n.preventDefault(),e.select(e.tabs.indexOf(t))}}},[n("portal-target",{attrs:{name:t._uid.toString()}})],1):n("a",{attrs:{role:"tab",href:"#"},domProps:{textContent:e._s(t.title)},on:{click:function(n){n.preventDefault(),e.select(e.tabs.indexOf(t))}}})])]})),e._v(" "),!e.justified&&e.$slots["nav-right"]?n("li",{staticClass:"pull-right"},[e._t("nav-right")],2):e._e()],2),e._v(" "),n("div",{class:e.contentClasses},[e._t("default")],2)])};qt._withStripped=!0;var Gt=ce({render:qt,staticRenderFns:[]},void 0,Ut,void 0,!1,void 0,!1,void 0,void 0,void 0);function Jt(e,t){for(var n=t-(e+="").length;n>0;n--)e="0"+e;return e}var Qt=["January","February","March","April","May","June","July","August","September","October","November","December"];function Kt(e){return new Date(e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds())}var Zt={mixins:[mt],props:{month:Number,year:Number,date:Date,today:Date,limit:Object,weekStartsWith:Number,iconControlLeft:String,iconControlRight:String,dateClass:Function,yearMonthFormatter:Function,weekNumbers:Boolean},components:{Btn:kt},computed:{weekDays:function(){for(var e=[],t=this.weekStartsWith;e.length<7;)e.push(t++),t>6&&(t=0);return e},yearMonthStr:function(){return this.yearMonthFormatter?this.yearMonthFormatter(this.year,this.month):ie(this.month)?this.year+" "+this.t("uiv.datePicker.month"+(this.month+1)):this.year},monthDayRows:function(){var e,t,n=[],r=new Date(this.year,this.month,1),i=new Date(this.year,this.month,0).getDate(),a=r.getDay(),o=(e=this.month,t=this.year,new Date(t,e+1,0).getDate()),s=0;s=this.weekStartsWith>a?7-this.weekStartsWith:0-this.weekStartsWith;for(var u=0;u<6;u++){n.push([]);for(var l=0-s;l<7-s;l++){var c=7*u+l,d={year:this.year,disabled:!1};c0?d.month=this.month-1:(d.month=11,d.year--)):c=this.limit.from),this.limit&&this.limit.to&&(p=f0?e--:(e=11,t--,this.$emit("year-change",t)),this.$emit("month-change",e)},goNextMonth:function(){var e=this.month,t=this.year;this.month<11?e++:(e=0,t++,this.$emit("year-change",t)),this.$emit("month-change",e)},changeView:function(){this.$emit("view-change","m")}}},Xt=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("table",{staticStyle:{width:"100%"},attrs:{role:"grid"}},[n("thead",[n("tr",[n("td",[n("btn",{staticClass:"uiv-datepicker-pager-prev",staticStyle:{border:"none"},attrs:{block:"",size:"sm"},on:{click:e.goPrevMonth}},[n("i",{class:e.iconControlLeft})])],1),e._v(" "),n("td",{attrs:{colspan:e.weekNumbers?6:5}},[n("btn",{staticClass:"uiv-datepicker-title",staticStyle:{border:"none"},attrs:{block:"",size:"sm"},on:{click:e.changeView}},[n("b",[e._v(e._s(e.yearMonthStr))])])],1),e._v(" "),n("td",[n("btn",{staticClass:"uiv-datepicker-pager-next",staticStyle:{border:"none"},attrs:{block:"",size:"sm"},on:{click:e.goNextMonth}},[n("i",{class:e.iconControlRight})])],1)]),e._v(" "),n("tr",{attrs:{align:"center"}},[e.weekNumbers?n("td"):e._e(),e._v(" "),e._l(e.weekDays,(function(t){return n("td",{attrs:{width:"14.2857142857%"}},[n("small",{staticClass:"uiv-datepicker-week"},[e._v(e._s(e.tWeekName(0===t?7:t)))])])}))],2)]),e._v(" "),n("tbody",e._l(e.monthDayRows,(function(t){return n("tr",[e.weekNumbers?n("td",{staticClass:"text-center",staticStyle:{"border-right":"1px solid #eee"}},[n("small",{staticClass:"text-muted"},[e._v(e._s(e.getWeekNumber(t[e.weekStartsWith])))])]):e._e(),e._v(" "),e._l(t,(function(t){return n("td",[n("btn",{class:t.classes,staticStyle:{border:"none"},attrs:{block:"",size:"sm","data-action":"select",type:e.getBtnType(t),disabled:t.disabled},on:{click:function(n){return e.select(t)}}},[n("span",{class:{"text-muted":e.month!==t.month},attrs:{"data-action":"select"}},[e._v(e._s(t.date))])])],1)}))],2)})),0)])};Xt._withStripped=!0;var en=ce({render:Xt,staticRenderFns:[]},void 0,Zt,void 0,!1,void 0,!1,void 0,void 0,void 0),tn={components:{Btn:kt},mixins:[mt],props:{month:Number,year:Number,iconControlLeft:String,iconControlRight:String},data:function(){return{rows:[]}},mounted:function(){for(var e=0;e<4;e++){this.rows.push([]);for(var t=0;t<3;t++)this.rows[e].push(3*e+t+1)}},methods:{tCell:function(e){return this.t("uiv.datePicker.month"+e)},getBtnClass:function(e){return e===this.month?"primary":"default"},goPrevYear:function(){this.$emit("year-change",this.year-1)},goNextYear:function(){this.$emit("year-change",this.year+1)},changeView:function(e){ie(e)?(this.$emit("month-change",e),this.$emit("view-change","d")):this.$emit("view-change","y")}}},nn=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("table",{staticStyle:{width:"100%"},attrs:{role:"grid"}},[n("thead",[n("tr",[n("td",[n("btn",{staticClass:"uiv-datepicker-pager-prev",staticStyle:{border:"none"},attrs:{block:"",size:"sm"},on:{click:e.goPrevYear}},[n("i",{class:e.iconControlLeft})])],1),e._v(" "),n("td",{attrs:{colspan:"4"}},[n("btn",{staticClass:"uiv-datepicker-title",staticStyle:{border:"none"},attrs:{block:"",size:"sm"},on:{click:function(t){return e.changeView()}}},[n("b",[e._v(e._s(e.year))])])],1),e._v(" "),n("td",[n("btn",{staticClass:"uiv-datepicker-pager-next",staticStyle:{border:"none"},attrs:{block:"",size:"sm"},on:{click:e.goNextYear}},[n("i",{class:e.iconControlRight})])],1)])]),e._v(" "),n("tbody",e._l(e.rows,(function(t,r){return n("tr",e._l(t,(function(t,i){return n("td",{attrs:{colspan:"2",width:"33.333333%"}},[n("btn",{staticStyle:{border:"none"},attrs:{block:"",size:"sm",type:e.getBtnClass(3*r+i)},on:{click:function(t){return e.changeView(3*r+i)}}},[n("span",[e._v(e._s(e.tCell(t)))])])],1)})),0)})),0)])};nn._withStripped=!0;var rn=ce({render:nn,staticRenderFns:[]},void 0,tn,void 0,!1,void 0,!1,void 0,void 0,void 0),an={components:{Btn:kt},props:{year:Number,iconControlLeft:String,iconControlRight:String},computed:{rows:function(){for(var e=[],t=this.year-this.year%20,n=0;n<4;n++){e.push([]);for(var r=0;r<5;r++)e[n].push(t+5*n+r)}return e},yearStr:function(){var e=this.year-this.year%20;return e+" ~ "+(e+19)}},methods:{getBtnClass:function(e){return e===this.year?"primary":"default"},goPrevYear:function(){this.$emit("year-change",this.year-20)},goNextYear:function(){this.$emit("year-change",this.year+20)},changeView:function(e){this.$emit("year-change",e),this.$emit("view-change","m")}}},on=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("table",{staticStyle:{width:"100%"},attrs:{role:"grid"}},[n("thead",[n("tr",[n("td",[n("btn",{staticClass:"uiv-datepicker-pager-prev",staticStyle:{border:"none"},attrs:{block:"",size:"sm"},on:{click:e.goPrevYear}},[n("i",{class:e.iconControlLeft})])],1),e._v(" "),n("td",{attrs:{colspan:"3"}},[n("btn",{staticClass:"uiv-datepicker-title",staticStyle:{border:"none"},attrs:{block:"",size:"sm"}},[n("b",[e._v(e._s(e.yearStr))])])],1),e._v(" "),n("td",[n("btn",{staticClass:"uiv-datepicker-pager-next",staticStyle:{border:"none"},attrs:{block:"",size:"sm"},on:{click:e.goNextYear}},[n("i",{class:e.iconControlRight})])],1)])]),e._v(" "),n("tbody",e._l(e.rows,(function(t){return n("tr",e._l(t,(function(t){return n("td",{attrs:{width:"20%"}},[n("btn",{staticStyle:{border:"none"},attrs:{block:"",size:"sm",type:e.getBtnClass(t)},on:{click:function(n){return e.changeView(t)}}},[n("span",[e._v(e._s(t))])])],1)})),0)})),0)])};on._withStripped=!0;var sn={mixins:[mt],components:{DateView:en,MonthView:rn,YearView:ce({render:on,staticRenderFns:[]},void 0,an,void 0,!1,void 0,!1,void 0,void 0,void 0),Btn:kt},props:{value:null,width:{type:Number,default:270},todayBtn:{type:Boolean,default:!0},clearBtn:{type:Boolean,default:!0},closeOnSelected:{type:Boolean,default:!0},limitFrom:null,limitTo:null,format:{type:String,default:"yyyy-MM-dd"},initialView:{type:String,default:"d"},dateParser:{type:Function,default:Date.parse},dateClass:Function,yearMonthFormatter:Function,weekStartsWith:{type:Number,default:0,validator:function(e){return e>=0&&e<=6}},weekNumbers:Boolean,iconControlLeft:{type:String,default:"glyphicon glyphicon-chevron-left"},iconControlRight:{type:String,default:"glyphicon glyphicon-chevron-right"}},data:function(){return{show:!1,now:new Date,currentMonth:0,currentYear:0,view:"d"}},computed:{valueDateObj:function(){var e=this.dateParser(this.value);if(isNaN(e))return null;var t=new Date(e);return 0!==t.getHours()&&(t=new Date(e+60*t.getTimezoneOffset()*1e3)),t},pickerStyle:function(){return{width:this.width+"px"}},pickerClass:function(){return{"uiv-datepicker":!0,"uiv-datepicker-date":"d"===this.view,"uiv-datepicker-month":"m"===this.view,"uiv-datepicker-year":"y"===this.view}},limit:function(){var e={};if(this.limitFrom){var t=this.dateParser(this.limitFrom);isNaN(t)||((t=Kt(new Date(t))).setHours(0,0,0,0),e.from=t)}if(this.limitTo){var n=this.dateParser(this.limitTo);isNaN(n)||((n=Kt(new Date(n))).setHours(0,0,0,0),e.to=n)}return e}},mounted:function(){this.value?this.setMonthAndYearByValue(this.value):(this.currentMonth=this.now.getMonth(),this.currentYear=this.now.getFullYear(),this.view=this.initialView)},watch:{value:function(e,t){this.setMonthAndYearByValue(e,t)}},methods:{setMonthAndYearByValue:function(e,t){var n=this.dateParser(e);if(!isNaN(n)){var r=new Date(n);0!==r.getHours()&&(r=new Date(n+60*r.getTimezoneOffset()*1e3)),this.limit&&(this.limit.from&&r=this.limit.to)?this.$emit("input",t||""):(this.currentMonth=r.getMonth(),this.currentYear=r.getFullYear())}},onMonthChange:function(e){this.currentMonth=e},onYearChange:function(e){this.currentYear=e,this.currentMonth=void 0},onDateChange:function(e){if(e&&oe(e.date)&&oe(e.month)&&oe(e.year)){var t=new Date(e.year,e.month,e.date);this.$emit("input",this.format?function(e,t){try{var n=e.getFullYear(),r=e.getMonth()+1,i=e.getDate(),a=Qt[r-1];return t.replace(/yyyy/g,n).replace(/MMMM/g,a).replace(/MMM/g,a.substring(0,3)).replace(/MM/g,Jt(r,2)).replace(/dd/g,Jt(i,2)).replace(/yy/g,n).replace(/M(?!a)/g,r).replace(/d/g,i)}catch(e){return""}}(t,this.format):t),this.currentMonth=e.month,this.currentYear=e.year}else this.$emit("input","")},onViewChange:function(e){this.view=e},selectToday:function(){this.view="d",this.onDateChange({date:this.now.getDate(),month:this.now.getMonth(),year:this.now.getFullYear()})},clearSelect:function(){this.currentMonth=this.now.getMonth(),this.currentYear=this.now.getFullYear(),this.view=this.initialView,this.onDateChange()},onPickerClick:function(e){"select"===e.target.getAttribute("data-action")&&this.closeOnSelected||e.stopPropagation()}}},un=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{class:e.pickerClass,style:e.pickerStyle,attrs:{"data-role":"date-picker"},on:{click:e.onPickerClick}},[n("date-view",{directives:[{name:"show",rawName:"v-show",value:"d"===e.view,expression:"view==='d'"}],attrs:{month:e.currentMonth,year:e.currentYear,date:e.valueDateObj,today:e.now,limit:e.limit,"week-starts-with":e.weekStartsWith,"icon-control-left":e.iconControlLeft,"icon-control-right":e.iconControlRight,"date-class":e.dateClass,"year-month-formatter":e.yearMonthFormatter,"week-numbers":e.weekNumbers,locale:e.locale},on:{"month-change":e.onMonthChange,"year-change":e.onYearChange,"date-change":e.onDateChange,"view-change":e.onViewChange}}),e._v(" "),n("month-view",{directives:[{name:"show",rawName:"v-show",value:"m"===e.view,expression:"view==='m'"}],attrs:{month:e.currentMonth,year:e.currentYear,"icon-control-left":e.iconControlLeft,"icon-control-right":e.iconControlRight,locale:e.locale},on:{"month-change":e.onMonthChange,"year-change":e.onYearChange,"view-change":e.onViewChange}}),e._v(" "),n("year-view",{directives:[{name:"show",rawName:"v-show",value:"y"===e.view,expression:"view==='y'"}],attrs:{year:e.currentYear,"icon-control-left":e.iconControlLeft,"icon-control-right":e.iconControlRight},on:{"year-change":e.onYearChange,"view-change":e.onViewChange}}),e._v(" "),e.todayBtn||e.clearBtn?n("div",[n("br"),e._v(" "),n("div",{staticClass:"text-center"},[e.todayBtn?n("btn",{attrs:{"data-action":"select",type:"info",size:"sm"},domProps:{textContent:e._s(e.t("uiv.datePicker.today"))},on:{click:e.selectToday}}):e._e(),e._v(" "),e.clearBtn?n("btn",{attrs:{"data-action":"select",size:"sm"},domProps:{textContent:e._s(e.t("uiv.datePicker.clear"))},on:{click:e.clearSelect}}):e._e()],1)]):e._e()],1)};un._withStripped=!0;var ln=ce({render:un,staticRenderFns:[]},void 0,sn,void 0,!1,void 0,!1,void 0,void 0,void 0),cn="_uiv_scroll_handler",dn=[Ye,Ce],fn=function(e,t){var n=t.value;ae(n)&&(hn(e),e[cn]=n,dn.forEach((function(t){qe(window,t,e[cn])})))},hn=function(e){dn.forEach((function(t){Ge(window,t,e[cn])})),delete e[cn]},pn={directives:{scroll:{bind:fn,unbind:hn,update:function(e,t){t.value!==t.oldValue&&fn(e,t)}}},props:{offset:{type:Number,default:0}},data:function(){return{affixed:!1}},computed:{classes:function(){return{affix:this.affixed}},styles:function(){return{top:this.affixed?this.offset+"px":null}}},methods:{onScroll:function(){var e=this;if(this.$el.offsetWidth||this.$el.offsetHeight||this.$el.getClientRects().length){var t={},n={},r=this.$el.getBoundingClientRect(),i=document.body;["Top","Left"].forEach((function(a){var o=a.toLowerCase();t[o]=window["page"+("Top"===a?"Y":"X")+"Offset"],n[o]=t[o]+r[o]-(e.$el["client"+a]||i["client"+a]||0)}));var a=t.top>n.top-this.offset;this.affixed!==a&&(this.affixed=a,this.$emit(this.affixed?"affix":"unfix"),this.$nextTick((function(){e.$emit(e.affixed?"affixed":"unfixed")})))}}}},mn=function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"hidden-print"},[t("div",{directives:[{name:"scroll",rawName:"v-scroll",value:this.onScroll,expression:"onScroll"}],class:this.classes,style:this.styles},[this._t("default")],2)])};mn._withStripped=!0;var _n=ce({render:mn,staticRenderFns:[]},void 0,pn,void 0,!1,void 0,!1,void 0,void 0,void 0),gn={props:{dismissible:{type:Boolean,default:!1},duration:{type:Number,default:0},type:{type:String,default:"info"}},data:function(){return{timeout:0}},computed:{alertClass:function(){var e;return(e={alert:!0})["alert-"+this.type]=Boolean(this.type),e["alert-dismissible"]=this.dismissible,e}},methods:{closeAlert:function(){clearTimeout(this.timeout),this.$emit("dismissed")}},mounted:function(){this.duration>0&&(this.timeout=setTimeout(this.closeAlert,this.duration))},destroyed:function(){clearTimeout(this.timeout)}},vn=function(){var e=this.$createElement,t=this._self._c||e;return t("div",{class:this.alertClass,attrs:{role:"alert"}},[this.dismissible?t("button",{staticClass:"close",attrs:{type:"button","aria-label":"Close"},on:{click:this.closeAlert}},[t("span",{attrs:{"aria-hidden":"true"}},[this._v("×")])]):this._e(),this._v(" "),this._t("default")],2)};vn._withStripped=!0;var yn=ce({render:vn,staticRenderFns:[]},void 0,gn,void 0,!1,void 0,!1,void 0,void 0,void 0),bn={props:{value:{type:Number,required:!0,validator:function(e){return e>=1}},boundaryLinks:{type:Boolean,default:!1},directionLinks:{type:Boolean,default:!0},size:String,align:String,totalPage:{type:Number,required:!0,validator:function(e){return e>=0}},maxSize:{type:Number,default:5,validator:function(e){return e>=0}},disabled:Boolean},data:function(){return{sliceStart:0}},computed:{navClasses:function(){var e;return(e={})["text-"+this.align]=Boolean(this.align),e},classes:function(){var e;return(e={})["pagination-"+this.size]=Boolean(this.size),e},sliceArray:function(){return function(e,t,n){void 0===t&&(t=0),void 0===n&&(n=1);for(var r=[],i=t;in+t){var r=this.totalPage-t;this.sliceStart=e>r?r:e-1}else et?e-t:0)},onPageChange:function(e){!this.disabled&&e>0&&e<=this.totalPage&&e!==this.value&&(this.$emit("input",e),this.$emit("change",e))},toPage:function(e){if(!this.disabled){var t=this.maxSize,n=this.sliceStart,r=this.totalPage-t,i=e?n-t:n+t;this.sliceStart=i<0?0:i>r?r:i}}},created:function(){this.$watch((function(e){return[e.value,e.maxSize,e.totalPage].join()}),this.calculateSliceStart,{immediate:!0})}},wn=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("nav",{class:e.navClasses,attrs:{"aria-label":"Page navigation"}},[n("ul",{staticClass:"pagination",class:e.classes},[e.boundaryLinks?n("li",{class:{disabled:e.value<=1||e.disabled}},[n("a",{attrs:{href:"#",role:"button","aria-label":"First"},on:{click:function(t){return t.preventDefault(),e.onPageChange(1)}}},[n("span",{attrs:{"aria-hidden":"true"}},[e._v("«")])])]):e._e(),e._v(" "),e.directionLinks?n("li",{class:{disabled:e.value<=1||e.disabled}},[n("a",{attrs:{href:"#",role:"button","aria-label":"Previous"},on:{click:function(t){return t.preventDefault(),e.onPageChange(e.value-1)}}},[n("span",{attrs:{"aria-hidden":"true"}},[e._v("‹")])])]):e._e(),e._v(" "),e.sliceStart>0?n("li",{class:{disabled:e.disabled}},[n("a",{attrs:{href:"#",role:"button","aria-label":"Previous group"},on:{click:function(t){return t.preventDefault(),e.toPage(1)}}},[n("span",{attrs:{"aria-hidden":"true"}},[e._v("…")])])]):e._e(),e._v(" "),e._l(e.sliceArray,(function(t){return n("li",{key:t,class:{active:e.value===t+1,disabled:e.disabled}},[n("a",{attrs:{href:"#",role:"button"},on:{click:function(n){return n.preventDefault(),e.onPageChange(t+1)}}},[e._v(e._s(t+1))])])})),e._v(" "),e.sliceStart=e.totalPage||e.disabled}},[n("a",{attrs:{href:"#",role:"button","aria-label":"Next"},on:{click:function(t){return t.preventDefault(),e.onPageChange(e.value+1)}}},[n("span",{attrs:{"aria-hidden":"true"}},[e._v("›")])])]):e._e(),e._v(" "),e.boundaryLinks?n("li",{class:{disabled:e.value>=e.totalPage||e.disabled}},[n("a",{attrs:{href:"#",role:"button","aria-label":"Last"},on:{click:function(t){return t.preventDefault(),e.onPageChange(e.totalPage)}}},[n("span",{attrs:{"aria-hidden":"true"}},[e._v("»")])])]):e._e()],2)])};wn._withStripped=!0;var An=ce({render:wn,staticRenderFns:[]},void 0,bn,void 0,!1,void 0,!1,void 0,void 0,void 0),xn={props:{value:{type:Boolean,default:!1},tag:{type:String,default:"span"},placement:{type:String,default:Ne},autoPlacement:{type:Boolean,default:!0},appendTo:{type:null,default:"body"},positionBy:{type:null,default:null},transition:{type:Number,default:150},hideDelay:{type:Number,default:0},showDelay:{type:Number,default:0},enable:{type:Boolean,default:!0},enterable:{type:Boolean,default:!0},target:null,viewport:null,customClass:String},data:function(){return{triggerEl:null,hideTimeoutId:0,showTimeoutId:0,transitionTimeoutId:0,autoTimeoutId:0}},watch:{value:function(e){e?this.show():this.hide()},trigger:function(){this.clearListeners(),this.initListeners()},target:function(e){this.clearListeners(),this.initTriggerElByTarget(e),this.initListeners()},allContent:function(e){var t=this;this.isNotEmpty()?this.$nextTick((function(){t.isShown()&&t.resetPosition()})):this.hide()},enable:function(e){e||this.hide()}},mounted:function(){var e=this;Ke(),Qe(this.$refs.popup),this.$nextTick((function(){e.initTriggerElByTarget(e.target),e.initListeners(),e.value&&e.show()}))},beforeDestroy:function(){this.clearListeners(),Qe(this.$refs.popup)},methods:{initTriggerElByTarget:function(e){if(e)this.triggerEl=st(e);else{var t=this.$el.querySelector('[data-role="trigger"]');if(t)this.triggerEl=t;else{var n=this.$el.firstChild;this.triggerEl=n===this.$refs.popup?null:n}}},initListeners:function(){this.triggerEl&&(this.trigger===Oe?(qe(this.triggerEl,be,this.show),qe(this.triggerEl,we,this.hide)):this.trigger===Pe?(qe(this.triggerEl,Me,this.show),qe(this.triggerEl,ke,this.hide)):this.trigger===Ie?(qe(this.triggerEl,be,this.handleAuto),qe(this.triggerEl,we,this.handleAuto),qe(this.triggerEl,Me,this.handleAuto),qe(this.triggerEl,ke,this.handleAuto)):this.trigger!==je&&this.trigger!==He||qe(this.triggerEl,De,this.toggle)),qe(window,De,this.windowClicked)},clearListeners:function(){this.triggerEl&&(Ge(this.triggerEl,Me,this.show),Ge(this.triggerEl,ke,this.hide),Ge(this.triggerEl,be,this.show),Ge(this.triggerEl,we,this.hide),Ge(this.triggerEl,De,this.toggle),Ge(this.triggerEl,be,this.handleAuto),Ge(this.triggerEl,we,this.handleAuto),Ge(this.triggerEl,Me,this.handleAuto),Ge(this.triggerEl,ke,this.handleAuto)),Ge(window,De,this.windowClicked),this.clearTimeouts()},clearTimeouts:function(){this.hideTimeoutId&&(clearTimeout(this.hideTimeoutId),this.hideTimeoutId=0),this.showTimeoutId&&(clearTimeout(this.showTimeoutId),this.showTimeoutId=0),this.transitionTimeoutId&&(clearTimeout(this.transitionTimeoutId),this.transitionTimeoutId=0),this.autoTimeoutId&&(clearTimeout(this.autoTimeoutId),this.autoTimeoutId=0)},resetPosition:function(){var e=this.$refs.popup;e&&(!function(e,t,n,r,i,a,o){if(Je(e)&&Je(t)){var s,u,l=e&&e.className&&e.className.indexOf("popover")>=0;if(ie(i)&&"body"!==i&&"body"!==a){var c=st(a||i);u=c.scrollLeft,s=c.scrollTop}else{var d=document.documentElement;u=(window.pageXOffset||d.scrollLeft)-(d.clientLeft||0),s=(window.pageYOffset||d.scrollTop)-(d.clientTop||0)}if(r){var f=[Fe,Be,ze,Ne],h=function(t){f.forEach((function(t){Xe(e,t)})),Ze(e,t)};if(!et(t,e,n)){for(var p=0,m=f.length;pk&&(_=k-b.height),gD&&(g=D-b.width),n===Be?_-=w:n===ze?g+=w:n===Fe?g-=w:_+=w}e.style.top=_+"px",e.style.left=g+"px"}}(e,this.triggerEl,this.placement,this.autoPlacement,this.appendTo,this.positionBy,this.viewport),e.offsetHeight)},hideOnLeave:function(){(this.trigger===Oe||this.trigger===Ie&&!this.triggerEl.matches(":focus"))&&this.$hide()},toggle:function(){this.isShown()?this.hide():this.show()},show:function(){var e=this;if(this.enable&&this.triggerEl&&this.isNotEmpty()&&!this.isShown()){var t=this.hideTimeoutId>0;t&&(clearTimeout(this.hideTimeoutId),this.hideTimeoutId=0),this.transitionTimeoutId>0&&(clearTimeout(this.transitionTimeoutId),this.transitionTimeoutId=0),clearTimeout(this.showTimeoutId),this.showTimeoutId=setTimeout((function(){e.showTimeoutId=0;var n=e.$refs.popup;if(n){var r=ot();if(r>1){var i="popover"===e.name?1060:1070,a=20*(r-1);n.style.zIndex=""+(i+a)}if(!t)n.className=e.name+" "+e.placement+" "+(e.customClass?e.customClass:"")+" fade",st(e.appendTo).appendChild(n),e.resetPosition();Ze(n,"in"),e.$emit("input",!0),e.$emit("show")}}),this.showDelay)}},hide:function(){var e=this;this.showTimeoutId>0&&(clearTimeout(this.showTimeoutId),this.showTimeoutId=0),this.isShown()&&(!this.enterable||this.trigger!==Oe&&this.trigger!==Ie?this.$hide():(clearTimeout(this.hideTimeoutId),this.hideTimeoutId=setTimeout((function(){e.hideTimeoutId=0;var t=e.$refs.popup;t&&!t.matches(":hover")&&e.$hide()}),100)))},$hide:function(){var e=this;this.isShown()&&(clearTimeout(this.hideTimeoutId),this.hideTimeoutId=setTimeout((function(){e.hideTimeoutId=0,Xe(e.$refs.popup,"in"),e.transitionTimeoutId=setTimeout((function(){e.transitionTimeoutId=0,Qe(e.$refs.popup),e.$emit("input",!1),e.$emit("hide")}),e.transition)}),this.hideDelay))},isShown:function(){return function(e,t){if(!Je(e))return!1;for(var n=e.className.split(" "),r=0,i=n.length;r=1&&t<=12&&(this.meridian?this.hours=12===t?0:t:this.hours=12===t?12:t+12):t>=0&&t<=23&&(this.hours=t),this.setTime()}},minutesText:function(e){if(0!==this.minutes||""!==e){var t=parseInt(e);t>=0&&t<=59&&(this.minutes=t),this.setTime()}}},methods:{updateByValue:function(e){if(isNaN(e.getTime()))return this.hours=0,this.minutes=0,this.hoursText="",this.minutesText="",void(this.meridian=!0);this.hours=e.getHours(),this.minutes=e.getMinutes(),this.showMeridian?this.hours>=12?(12===this.hours?this.hoursText=this.hours+"":this.hoursText=Jt(this.hours-12,2),this.meridian=!1):(0===this.hours?this.hoursText=12..toString():this.hoursText=Jt(this.hours,2),this.meridian=!0):this.hoursText=Jt(this.hours,2),this.minutesText=Jt(this.minutes,2),this.$refs.hoursInput.value=this.hoursText,this.$refs.minutesInput.value=this.minutesText},addHour:function(e){e=e||this.hourStep,this.hours=this.hours>=23?0:this.hours+e},reduceHour:function(e){e=e||this.hourStep,this.hours=this.hours<=0?23:this.hours-e},addMinute:function(){this.minutes>=59?(this.minutes=0,this.addHour(1)):this.minutes+=this.minStep},reduceMinute:function(){this.minutes<=0?(this.minutes=60-this.minStep,this.reduceHour(1)):this.minutes-=this.minStep},changeTime:function(e,t){this.readonly||(e&&t?this.addHour():e&&!t?this.reduceHour():!e&&t?this.addMinute():this.reduceMinute(),this.setTime())},toggleMeridian:function(){this.meridian=!this.meridian,this.meridian?this.hours-=12:this.hours+=12,this.setTime()},onWheel:function(e,t){this.readonly||(e.preventDefault(),this.changeTime(t,e.deltaY<0))},setTime:function(){var e=this.value;if(isNaN(e.getTime())&&((e=new Date).setHours(0),e.setMinutes(0)),e.setHours(this.hours),e.setMinutes(this.minutes),this.max){var t=new Date(e);t.setHours(this.max.getHours()),t.setMinutes(this.max.getMinutes()),e=e>t?t:e}if(this.min){var n=new Date(e);n.setHours(this.min.getHours()),n.setMinutes(this.min.getMinutes()),e=e=0)&&this.items.push(i),this.items.length>=this.limit)break}}},fetchItems:function(e,t){var n=this;if(clearTimeout(this.timeoutID),""!==e||this.openOnEmpty){if(this.data)this.prepareItems(this.data),this.open=this.hasEmptySlot()||Boolean(this.items.length);else if(this.asyncSrc)this.timeoutID=setTimeout((function(){n.$emit("loading"),function(e,t){void 0===t&&(t="GET");var n=new window.XMLHttpRequest,r={},i={then:function(e,t){return i.done(e).fail(t)},catch:function(e){return i.fail(e)},always:function(e){return i.done(e).fail(e)}};return["done","fail"].forEach((function(e){r[e]=[],i[e]=function(t){return t instanceof Function&&r[e].push(t),i}})),i.done(JSON.parse),n.onreadystatechange=function(){if(4===n.readyState){var e={status:n.status};if(200===n.status){var t=n.responseText;for(var i in r.done)if(le(r.done,i)&&ae(r.done[i])){var a=r.done[i](t);ie(a)&&(t=a)}}else r.fail.forEach((function(t){return t(e)}))}},n.open(t,e),n.setRequestHeader("Accept","application/json"),n.send(),i}(n.asyncSrc+encodeURIComponent(e)).then((function(e){n.inputEl.matches(":focus")&&(n.prepareItems(n.asyncKey?e[n.asyncKey]:e,!0),n.open=n.hasEmptySlot()||Boolean(n.items.length)),n.$emit("loaded")})).catch((function(e){console.error(e),n.$emit("loaded-error")}))}),t);else if(this.asyncFunction){var r=function(e){n.inputEl.matches(":focus")&&(n.prepareItems(e,!0),n.open=n.hasEmptySlot()||Boolean(n.items.length)),n.$emit("loaded")};this.timeoutID=setTimeout((function(){n.$emit("loading"),n.asyncFunction(e,r)}),t)}}else this.open=!1},inputChanged:function(){var e=this.inputEl.value;this.fetchItems(e,this.debounce),this.$emit("input",this.forceSelect?void 0:e)},inputFocused:function(){if(this.openOnFocus){var e=this.inputEl.value;this.fetchItems(e,0)}},inputBlured:function(){var e=this;this.dropdownMenuEl.matches(":hover")||(this.open=!1),this.inputEl&&this.forceClear&&this.$nextTick((function(){void 0===e.value&&(e.inputEl.value="")}))},inputKeyPressed:function(e){if(e.stopPropagation(),this.open)switch(e.keyCode){case 13:this.activeIndex>=0?this.selectItem(this.items[this.activeIndex]):this.open=!1,e.preventDefault();break;case 27:this.open=!1;break;case 38:this.activeIndex=this.activeIndex>0?this.activeIndex-1:0;break;case 40:var t=this.items.length-1;this.activeIndex=this.activeIndex$&")}}},Yn=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("dropdown",{ref:"dropdown",attrs:{tag:"section","append-to-body":e.appendToBody,"not-close-elements":e.elements,"position-element":e.inputEl},model:{value:e.open,callback:function(t){e.open=t},expression:"open"}},[n("template",{slot:"dropdown"},[e._t("item",e._l(e.items,(function(t,r){return n("li",{class:{active:e.activeIndex===r}},[n("a",{attrs:{href:"#"},on:{click:function(n){return n.preventDefault(),e.selectItem(t)}}},[n("span",{domProps:{innerHTML:e._s(e.highlight(t))}})])])})),{items:e.items,activeIndex:e.activeIndex,select:e.selectItem,highlight:e.highlight}),e._v(" "),e.items&&0!==e.items.length?e._e():e._t("empty")],2)],2)};Yn._withStripped=!0;var Cn=ce({render:Yn,staticRenderFns:[]},void 0,Sn,void 0,!1,void 0,!1,void 0,void 0,void 0),En={functional:!0,render:function(e,t){var n,r=t.props;return e("div",At(t.data,{class:(n={"progress-bar":!0,"progress-bar-striped":r.striped,active:r.striped&&r.active},n["progress-bar-"+r.type]=Boolean(r.type),n),style:{minWidth:r.minWidth?"2em":null,width:r.value+"%"},attrs:{role:"progressbar","aria-valuemin":0,"aria-valuenow":r.value,"aria-valuemax":100}}),r.label?r.labelText?r.labelText:r.value+"%":null)},props:{value:{type:Number,required:!0,validator:function(e){return e>=0&&e<=100}},labelText:String,type:String,label:{type:Boolean,default:!1},minWidth:{type:Boolean,default:!1},striped:{type:Boolean,default:!1},active:{type:Boolean,default:!1}}},jn={functional:!0,render:function(e,t){var n=t.props,r=t.data,i=t.children;return e("div",At(r,{class:"progress"}),i&&i.length?i:[e(En,{props:n})])}},On={functional:!0,mixins:[xt],render:function(e,t){var n,r=t.props,i=t.data,a=t.children;return n=r.active?a:r.to?[e("router-link",{props:{to:r.to,replace:r.replace,append:r.append,exact:r.exact}},a)]:[e("a",{attrs:{href:r.href,target:r.target}},a)],e("li",At(i,{class:{active:r.active}}),n)},props:{active:{type:Boolean,default:!1}}},Pn={functional:!0,render:function(e,t){var n=t.props,r=t.data,i=t.children,a=[];return i&&i.length?a=i:n.items&&(a=n.items.map((function(t,r){return e(On,{key:le(t,"key")?t.key:r,props:{active:le(t,"active")?t.active:r===n.items.length-1,href:t.href,target:t.target,to:t.to,replace:t.replace,append:t.append,exact:t.exact}},t.text)}))),e("ol",At(r,{class:"breadcrumb"}),a)},props:{items:Array}},In={functional:!0,render:function(e,t){var n=t.children;return e("div",At(t.data,{class:{"btn-toolbar":!0},attrs:{role:"toolbar"}}),n)}},Hn={mixins:[mt],components:{Dropdown:lt},props:{value:{type:Array,required:!0},options:{type:Array,required:!0},labelKey:{type:String,default:"label"},valueKey:{type:String,default:"value"},limit:{type:Number,default:0},size:String,placeholder:String,split:{type:String,default:", "},disabled:{type:Boolean,default:!1},appendToBody:{type:Boolean,default:!1},block:{type:Boolean,default:!1},collapseSelected:{type:Boolean,default:!1},filterable:{type:Boolean,default:!1},filterAutoFocus:{type:Boolean,default:!0},filterFunction:Function,filterPlaceholder:String,selectedIcon:{type:String,default:"glyphicon glyphicon-ok"},itemSelectedClass:String},data:function(){return{showDropdown:!1,els:[],filterInput:"",currentActive:-1}},computed:{containerStyles:function(){return{width:this.block?"100%":""}},filteredOptions:function(){var e=this;if(this.filterable&&this.filterInput){if(this.filterFunction)return this.filterFunction(this.filterInput);var t=this.filterInput.toLowerCase();return this.options.filter((function(n){return n[e.valueKey].toString().toLowerCase().indexOf(t)>=0||n[e.labelKey].toString().toLowerCase().indexOf(t)>=0}))}return this.options},groupedOptions:function(){var e=this;return this.filteredOptions.map((function(e){return e.group})).filter(_e).map((function(t){return{options:e.filteredOptions.filter((function(e){return e.group===t})),$group:t}}))},flattenGroupedOptions:function(){var e;return(e=[]).concat.apply(e,this.groupedOptions.map((function(e){return e.options})))},selectClasses:function(){var e;return(e={})["input-"+this.size]=this.size,e},selectedIconClasses:function(){var e;return(e={})[this.selectedIcon]=!0,e["pull-right"]=!0,e},selectTextClasses:function(){return{"text-muted":0===this.value.length}},labelValue:function(){var e=this,t=this.options.map((function(t){return t[e.valueKey]}));return this.value.map((function(n){var r=t.indexOf(n);return r>=0?e.options[r][e.labelKey]:n}))},selectedText:function(){if(this.value.length){var e=this.labelValue;if(this.collapseSelected){var t=e[0];return t+=e.length>1?this.split+"+"+(e.length-1):""}return e.join(this.split)}return this.placeholder||this.t("uiv.multiSelect.placeholder")},customOptionsVisible:function(){return!!this.$slots.option||!!this.$scopedSlots.option}},watch:{showDropdown:function(e){var t=this;this.filterInput="",this.currentActive=-1,this.$emit("visible-change",e),e&&this.filterable&&this.filterAutoFocus&&this.$nextTick((function(){t.$refs.filterInput.focus()}))}},mounted:function(){this.els=[this.$el]},methods:{goPrevOption:function(){this.showDropdown&&(this.currentActive>0?this.currentActive--:this.currentActive=this.flattenGroupedOptions.length-1)},goNextOption:function(){this.showDropdown&&(this.currentActive=0&&e=0},toggle:function(e){if(!e.disabled){var t=e[this.valueKey],n=this.value.indexOf(t);if(1===this.limit){var r=n>=0?[]:[t];this.$emit("input",r),this.$emit("change",r)}else if(n>=0){var i=this.value.slice();i.splice(n,1),this.$emit("input",i),this.$emit("change",i)}else if(0===this.limit||this.value.length a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.scrollElement&&(this.refresh(),this.process())}tr.DEFAULTS={offset:10,callback:function(e){return 0}},tr.prototype.getScrollHeight=function(){return this.scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},tr.prototype.refresh=function(){var e=this;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight();var t=me(this.el.querySelectorAll(this.selector)),n=this.scrollElement===window;t.map((function(t){var r=t.getAttribute("href");if(/^#./.test(r)){var i=(n?document:e.scrollElement).querySelector("[id='"+r.slice(1)+"']");return[n?i.getBoundingClientRect().top:i.offsetTop,r]}return null})).filter((function(e){return e})).sort((function(e,t){return e[0]-t[0]})).forEach((function(t){e.offsets.push(t[0]),e.targets.push(t[1])}))},tr.prototype.process=function(){var e,t=this.scrollElement===window,n=(t?window.pageYOffset:this.scrollElement.scrollTop)+this.opts.offset,r=this.getScrollHeight(),i=t?Re().height:this.scrollElement.getBoundingClientRect().height,a=this.opts.offset+r-i,o=this.offsets,s=this.targets,u=this.activeTarget;if(this.scrollHeight!==r&&this.refresh(),n>=a)return u!==(e=s[s.length-1])&&this.activate(e);if(u&&n=o[e]&&(void 0===o[e+1]||n-1:e.input},on:{change:[function(t){var n=e.input,r=t.target,i=!!r.checked;if(Array.isArray(n)){var a=e._i(n,null);r.checked?a<0&&(e.input=n.concat([null])):a>-1&&(e.input=n.slice(0,a).concat(n.slice(a+1)))}else e.input=i},function(t){e.dirty=!0}],keyup:function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"enter",13,t.key,"Enter")?null:e.validate(t)}}}):"radio"===e.inputType?n("input",{directives:[{name:"model",rawName:"v-model",value:e.input,expression:"input"}],ref:"input",staticClass:"form-control",attrs:{required:"","data-action":"auto-focus",type:"radio"},domProps:{checked:e._q(e.input,null)},on:{change:[function(t){e.input=null},function(t){e.dirty=!0}],keyup:function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"enter",13,t.key,"Enter")?null:e.validate(t)}}}):n("input",{directives:[{name:"model",rawName:"v-model",value:e.input,expression:"input"}],ref:"input",staticClass:"form-control",attrs:{required:"","data-action":"auto-focus",type:e.inputType},domProps:{value:e.input},on:{change:function(t){e.dirty=!0},keyup:function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"enter",13,t.key,"Enter")?null:e.validate(t)},input:function(t){t.target.composing||(e.input=t.target.value)}}}),e._v(" "),n("span",{directives:[{name:"show",rawName:"v-show",value:e.inputNotValid,expression:"inputNotValid"}],staticClass:"help-block"},[e._v(e._s(e.inputError))])])]):e._e(),e._v(" "),e.type===e.TYPES.ALERT?n("template",{slot:"footer"},[n("btn",{attrs:{type:e.okType,"data-action":"ok"===e.autoFocus?"auto-focus":""},domProps:{textContent:e._s(e.okBtnText)},on:{click:function(t){return e.toggle(!1,"ok")}}})],1):n("template",{slot:"footer"},[e.reverseButtons?[e.type===e.TYPES.CONFIRM?n("btn",{attrs:{type:e.okType,"data-action":"ok"===e.autoFocus?"auto-focus":""},domProps:{textContent:e._s(e.okBtnText)},on:{click:function(t){return e.toggle(!1,"ok")}}}):n("btn",{attrs:{type:e.okType},domProps:{textContent:e._s(e.okBtnText)},on:{click:e.validate}}),e._v(" "),n("btn",{attrs:{type:e.cancelType,"data-action":"cancel"===e.autoFocus?"auto-focus":""},domProps:{textContent:e._s(e.cancelBtnText)},on:{click:function(t){return e.toggle(!1,"cancel")}}})]:[n("btn",{attrs:{type:e.cancelType,"data-action":"cancel"===e.autoFocus?"auto-focus":""},domProps:{textContent:e._s(e.cancelBtnText)},on:{click:function(t){return e.toggle(!1,"cancel")}}}),e._v(" "),e.type===e.TYPES.CONFIRM?n("btn",{attrs:{type:e.okType,"data-action":"ok"===e.autoFocus?"auto-focus":""},domProps:{textContent:e._s(e.okBtnText)},on:{click:function(t){return e.toggle(!1,"ok")}}}):n("btn",{attrs:{type:e.okType},domProps:{textContent:e._s(e.okBtnText)},on:{click:e.validate}})]],2)],2)};dr._withStripped=!0;var fr=ce({render:dr,staticRenderFns:[]},void 0,cr,void 0,!1,void 0,!1,void 0,void 0,void 0),hr=[],pr=function(e,t){return e===lr.CONFIRM?"ok"===t:ie(t)&&se(t.value)},mr=function(e,t,n,r,i){void 0===r&&(r=null),void 0===i&&(i=null);var a=this.$i18n,o=new ne.a({extends:fr,i18n:a,propsData:re({},{type:e},t,{cb:function(t){!function(e){Qe(e.$el),e.$destroy(),pe(hr,e)}(o),ae(n)?e===lr.CONFIRM?pr(e,t)?n(null,t):n(t):e===lr.PROMPT&&pr(e,t)?n(null,t.value):n(t):r&&i&&(e===lr.CONFIRM?pr(e,t)?r(t):i(t):e===lr.PROMPT?pr(e,t)?r(t.value):i(t):r(t))}})});o.$mount(),document.body.appendChild(o.$el),o.show=!0,hr.push(o)},_r=function(e,t,n){var r=this;if(void 0===t&&(t={}),ue())return new Promise((function(i,a){mr.apply(r,[e,t,n,i,a])}));mr.apply(this,[e,t,n])},gr={alert:function(e,t){return _r.apply(this,[lr.ALERT,e,t])},confirm:function(e,t){return _r.apply(this,[lr.CONFIRM,e,t])},prompt:function(e,t){return _r.apply(this,[lr.PROMPT,e,t])}},vr="success",yr="info",br="danger",wr="warning",Ar="top-left",xr="top-right",Mr="bottom-left",kr="bottom-right",Dr="glyphicon",Lr={components:{Alert:yn},props:{title:String,content:String,html:{type:Boolean,default:!1},duration:{type:Number,default:5e3},dismissible:{type:Boolean,default:!0},type:String,placement:String,icon:String,customClass:null,cb:{type:Function,required:!0},queue:{type:Array,required:!0},offsetY:{type:Number,default:15},offsetX:{type:Number,default:15},offset:{type:Number,default:15}},data:function(){return{height:0,top:0,horizontal:this.placement===Ar||this.placement===Mr?"left":"right",vertical:this.placement===Ar||this.placement===xr?"top":"bottom"}},created:function(){this.top=this.getTotalHeightOfQueue(this.queue)},mounted:function(){var e=this,t=this.$el;t.style[this.vertical]=this.top+"px",this.$nextTick((function(){t.style[e.horizontal]="-300px",e.height=t.offsetHeight,t.style[e.horizontal]=e.offsetX+"px",Ze(t,"in")}))},computed:{styles:function(){var e,t=this.queue,n=t.indexOf(this);return(e={position:"fixed"})[this.vertical]=this.getTotalHeightOfQueue(t,n)+"px",e.width="300px",e.transition="all 0.3s ease-in-out",e},icons:function(){if(se(this.icon))return this.icon;switch(this.type){case yr:case wr:return Dr+" "+Dr+"-info-sign";case vr:return Dr+" "+Dr+"-ok-sign";case br:return Dr+" "+Dr+"-remove-sign";default:return null}}},methods:{getTotalHeightOfQueue:function(e,t){void 0===t&&(t=e.length);for(var n=this.offsetY,r=0;r"']/g,N=RegExp(I.source),F=RegExp(H.source),B=/<%-([\s\S]+?)%>/g,z=/<%([\s\S]+?)%>/g,$=/<%=([\s\S]+?)%>/g,R=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,W=/^\w*$/,V=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,U=/[\\^$.*+?()[\]{}|]/g,q=RegExp(U.source),G=/^\s+|\s+$/g,J=/^\s+/,Q=/\s+$/,K=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,Z=/\{\n\/\* \[wrapped with (.+)\] \*/,X=/,? & /,ee=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,te=/\\(\\)?/g,ne=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,re=/\w*$/,ie=/^[-+]0x[0-9a-f]+$/i,ae=/^0b[01]+$/i,oe=/^\[object .+?Constructor\]$/,se=/^0o[0-7]+$/i,ue=/^(?:0|[1-9]\d*)$/,le=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,ce=/($^)/,de=/['\n\r\u2028\u2029\\]/g,fe="\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff",he="\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",pe="[\\ud800-\\udfff]",me="["+he+"]",_e="["+fe+"]",ge="\\d+",ve="[\\u2700-\\u27bf]",ye="[a-z\\xdf-\\xf6\\xf8-\\xff]",be="[^\\ud800-\\udfff"+he+ge+"\\u2700-\\u27bfa-z\\xdf-\\xf6\\xf8-\\xffA-Z\\xc0-\\xd6\\xd8-\\xde]",we="\\ud83c[\\udffb-\\udfff]",Ae="[^\\ud800-\\udfff]",xe="(?:\\ud83c[\\udde6-\\uddff]){2}",Me="[\\ud800-\\udbff][\\udc00-\\udfff]",ke="[A-Z\\xc0-\\xd6\\xd8-\\xde]",De="(?:"+ye+"|"+be+")",Le="(?:"+ke+"|"+be+")",Te="(?:"+_e+"|"+we+")"+"?",Se="[\\ufe0e\\ufe0f]?"+Te+("(?:\\u200d(?:"+[Ae,xe,Me].join("|")+")[\\ufe0e\\ufe0f]?"+Te+")*"),Ye="(?:"+[ve,xe,Me].join("|")+")"+Se,Ce="(?:"+[Ae+_e+"?",_e,xe,Me,pe].join("|")+")",Ee=RegExp("['’]","g"),je=RegExp(_e,"g"),Oe=RegExp(we+"(?="+we+")|"+Ce+Se,"g"),Pe=RegExp([ke+"?"+ye+"+(?:['’](?:d|ll|m|re|s|t|ve))?(?="+[me,ke,"$"].join("|")+")",Le+"+(?:['’](?:D|LL|M|RE|S|T|VE))?(?="+[me,ke+De,"$"].join("|")+")",ke+"?"+De+"+(?:['’](?:d|ll|m|re|s|t|ve))?",ke+"+(?:['’](?:D|LL|M|RE|S|T|VE))?","\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])","\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",ge,Ye].join("|"),"g"),Ie=RegExp("[\\u200d\\ud800-\\udfff"+fe+"\\ufe0e\\ufe0f]"),He=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,Ne=["Array","Buffer","DataView","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Map","Math","Object","Promise","RegExp","Set","String","Symbol","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","_","clearTimeout","isFinite","parseInt","setTimeout"],Fe=-1,Be={};Be[k]=Be[D]=Be[L]=Be[T]=Be[S]=Be[Y]=Be["[object Uint8ClampedArray]"]=Be[C]=Be[E]=!0,Be[u]=Be[l]=Be[x]=Be[c]=Be[M]=Be[d]=Be[f]=Be[h]=Be[m]=Be[_]=Be[g]=Be[v]=Be[y]=Be[b]=Be[A]=!1;var ze={};ze[u]=ze[l]=ze[x]=ze[M]=ze[c]=ze[d]=ze[k]=ze[D]=ze[L]=ze[T]=ze[S]=ze[m]=ze[_]=ze[g]=ze[v]=ze[y]=ze[b]=ze[w]=ze[Y]=ze["[object Uint8ClampedArray]"]=ze[C]=ze[E]=!0,ze[f]=ze[h]=ze[A]=!1;var $e={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},Re=parseFloat,We=parseInt,Ve="object"==typeof e&&e&&e.Object===Object&&e,Ue="object"==typeof self&&self&&self.Object===Object&&self,qe=Ve||Ue||Function("return this")(),Ge=t&&!t.nodeType&&t,Je=Ge&&"object"==typeof r&&r&&!r.nodeType&&r,Qe=Je&&Je.exports===Ge,Ke=Qe&&Ve.process,Ze=function(){try{var e=Je&&Je.require&&Je.require("util").types;return e||Ke&&Ke.binding&&Ke.binding("util")}catch(e){}}(),Xe=Ze&&Ze.isArrayBuffer,et=Ze&&Ze.isDate,tt=Ze&&Ze.isMap,nt=Ze&&Ze.isRegExp,rt=Ze&&Ze.isSet,it=Ze&&Ze.isTypedArray;function at(e,t,n){switch(n.length){case 0:return e.call(t);case 1:return e.call(t,n[0]);case 2:return e.call(t,n[0],n[1]);case 3:return e.call(t,n[0],n[1],n[2])}return e.apply(t,n)}function ot(e,t,n,r){for(var i=-1,a=null==e?0:e.length;++i-1}function ft(e,t,n){for(var r=-1,i=null==e?0:e.length;++r-1;);return n}function Ot(e,t){for(var n=e.length;n--&&wt(t,e[n],0)>-1;);return n}function Pt(e,t){for(var n=e.length,r=0;n--;)e[n]===t&&++r;return r}var It=Dt({"À":"A","Á":"A","Â":"A","Ã":"A","Ä":"A","Å":"A","à":"a","á":"a","â":"a","ã":"a","ä":"a","å":"a","Ç":"C","ç":"c","Ð":"D","ð":"d","È":"E","É":"E","Ê":"E","Ë":"E","è":"e","é":"e","ê":"e","ë":"e","Ì":"I","Í":"I","Î":"I","Ï":"I","ì":"i","í":"i","î":"i","ï":"i","Ñ":"N","ñ":"n","Ò":"O","Ó":"O","Ô":"O","Õ":"O","Ö":"O","Ø":"O","ò":"o","ó":"o","ô":"o","õ":"o","ö":"o","ø":"o","Ù":"U","Ú":"U","Û":"U","Ü":"U","ù":"u","ú":"u","û":"u","ü":"u","Ý":"Y","ý":"y","ÿ":"y","Æ":"Ae","æ":"ae","Þ":"Th","þ":"th","ß":"ss","Ā":"A","Ă":"A","Ą":"A","ā":"a","ă":"a","ą":"a","Ć":"C","Ĉ":"C","Ċ":"C","Č":"C","ć":"c","ĉ":"c","ċ":"c","č":"c","Ď":"D","Đ":"D","ď":"d","đ":"d","Ē":"E","Ĕ":"E","Ė":"E","Ę":"E","Ě":"E","ē":"e","ĕ":"e","ė":"e","ę":"e","ě":"e","Ĝ":"G","Ğ":"G","Ġ":"G","Ģ":"G","ĝ":"g","ğ":"g","ġ":"g","ģ":"g","Ĥ":"H","Ħ":"H","ĥ":"h","ħ":"h","Ĩ":"I","Ī":"I","Ĭ":"I","Į":"I","İ":"I","ĩ":"i","ī":"i","ĭ":"i","į":"i","ı":"i","Ĵ":"J","ĵ":"j","Ķ":"K","ķ":"k","ĸ":"k","Ĺ":"L","Ļ":"L","Ľ":"L","Ŀ":"L","Ł":"L","ĺ":"l","ļ":"l","ľ":"l","ŀ":"l","ł":"l","Ń":"N","Ņ":"N","Ň":"N","Ŋ":"N","ń":"n","ņ":"n","ň":"n","ŋ":"n","Ō":"O","Ŏ":"O","Ő":"O","ō":"o","ŏ":"o","ő":"o","Ŕ":"R","Ŗ":"R","Ř":"R","ŕ":"r","ŗ":"r","ř":"r","Ś":"S","Ŝ":"S","Ş":"S","Š":"S","ś":"s","ŝ":"s","ş":"s","š":"s","Ţ":"T","Ť":"T","Ŧ":"T","ţ":"t","ť":"t","ŧ":"t","Ũ":"U","Ū":"U","Ŭ":"U","Ů":"U","Ű":"U","Ų":"U","ũ":"u","ū":"u","ŭ":"u","ů":"u","ű":"u","ų":"u","Ŵ":"W","ŵ":"w","Ŷ":"Y","ŷ":"y","Ÿ":"Y","Ź":"Z","Ż":"Z","Ž":"Z","ź":"z","ż":"z","ž":"z","IJ":"IJ","ij":"ij","Œ":"Oe","œ":"oe","ʼn":"'n","ſ":"s"}),Ht=Dt({"&":"&","<":"<",">":">",'"':""","'":"'"});function Nt(e){return"\\"+$e[e]}function Ft(e){return Ie.test(e)}function Bt(e){var t=-1,n=Array(e.size);return e.forEach((function(e,r){n[++t]=[r,e]})),n}function zt(e,t){return function(n){return e(t(n))}}function $t(e,t){for(var n=-1,r=e.length,i=0,a=[];++n",""":'"',"'":"'"});var Gt=function e(t){var n,r=(t=null==t?qe:Gt.defaults(qe.Object(),t,Gt.pick(qe,Ne))).Array,i=t.Date,fe=t.Error,he=t.Function,pe=t.Math,me=t.Object,_e=t.RegExp,ge=t.String,ve=t.TypeError,ye=r.prototype,be=he.prototype,we=me.prototype,Ae=t["__core-js_shared__"],xe=be.toString,Me=we.hasOwnProperty,ke=0,De=(n=/[^.]+$/.exec(Ae&&Ae.keys&&Ae.keys.IE_PROTO||""))?"Symbol(src)_1."+n:"",Le=we.toString,Te=xe.call(me),Se=qe._,Ye=_e("^"+xe.call(Me).replace(U,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),Ce=Qe?t.Buffer:void 0,Oe=t.Symbol,Ie=t.Uint8Array,$e=Ce?Ce.allocUnsafe:void 0,Ve=zt(me.getPrototypeOf,me),Ue=me.create,Ge=we.propertyIsEnumerable,Je=ye.splice,Ke=Oe?Oe.isConcatSpreadable:void 0,Ze=Oe?Oe.iterator:void 0,vt=Oe?Oe.toStringTag:void 0,Dt=function(){try{var e=Xi(me,"defineProperty");return e({},"",{}),e}catch(e){}}(),Jt=t.clearTimeout!==qe.clearTimeout&&t.clearTimeout,Qt=i&&i.now!==qe.Date.now&&i.now,Kt=t.setTimeout!==qe.setTimeout&&t.setTimeout,Zt=pe.ceil,Xt=pe.floor,en=me.getOwnPropertySymbols,tn=Ce?Ce.isBuffer:void 0,nn=t.isFinite,rn=ye.join,an=zt(me.keys,me),on=pe.max,sn=pe.min,un=i.now,ln=t.parseInt,cn=pe.random,dn=ye.reverse,fn=Xi(t,"DataView"),hn=Xi(t,"Map"),pn=Xi(t,"Promise"),mn=Xi(t,"Set"),_n=Xi(t,"WeakMap"),gn=Xi(me,"create"),vn=_n&&new _n,yn={},bn=Da(fn),wn=Da(hn),An=Da(pn),xn=Da(mn),Mn=Da(_n),kn=Oe?Oe.prototype:void 0,Dn=kn?kn.valueOf:void 0,Ln=kn?kn.toString:void 0;function Tn(e){if(Wo(e)&&!jo(e)&&!(e instanceof En)){if(e instanceof Cn)return e;if(Me.call(e,"__wrapped__"))return La(e)}return new Cn(e)}var Sn=function(){function e(){}return function(t){if(!Ro(t))return{};if(Ue)return Ue(t);e.prototype=t;var n=new e;return e.prototype=void 0,n}}();function Yn(){}function Cn(e,t){this.__wrapped__=e,this.__actions__=[],this.__chain__=!!t,this.__index__=0,this.__values__=void 0}function En(e){this.__wrapped__=e,this.__actions__=[],this.__dir__=1,this.__filtered__=!1,this.__iteratees__=[],this.__takeCount__=4294967295,this.__views__=[]}function jn(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t=t?e:t)),e}function Qn(e,t,n,r,i,a){var o,s=1&t,l=2&t,f=4&t;if(n&&(o=i?n(e,r,i,a):n(e)),void 0!==o)return o;if(!Ro(e))return e;var A=jo(e);if(A){if(o=function(e){var t=e.length,n=new e.constructor(t);t&&"string"==typeof e[0]&&Me.call(e,"index")&&(n.index=e.index,n.input=e.input);return n}(e),!s)return gi(e,o)}else{var j=na(e),O=j==h||j==p;if(Ho(e))return di(e,s);if(j==g||j==u||O&&!i){if(o=l||O?{}:ia(e),!s)return l?function(e,t){return vi(e,ta(e),t)}(e,function(e,t){return e&&vi(t,ws(t),e)}(o,e)):function(e,t){return vi(e,ea(e),t)}(e,Un(o,e))}else{if(!ze[j])return i?e:{};o=function(e,t,n){var r=e.constructor;switch(t){case x:return fi(e);case c:case d:return new r(+e);case M:return function(e,t){var n=t?fi(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.byteLength)}(e,n);case k:case D:case L:case T:case S:case Y:case"[object Uint8ClampedArray]":case C:case E:return hi(e,n);case m:return new r;case _:case b:return new r(e);case v:return function(e){var t=new e.constructor(e.source,re.exec(e));return t.lastIndex=e.lastIndex,t}(e);case y:return new r;case w:return i=e,Dn?me(Dn.call(i)):{}}var i}(e,j,s)}}a||(a=new Hn);var P=a.get(e);if(P)return P;a.set(e,o),Jo(e)?e.forEach((function(r){o.add(Qn(r,t,n,r,e,a))})):Vo(e)&&e.forEach((function(r,i){o.set(i,Qn(r,t,n,i,e,a))}));var I=A?void 0:(f?l?Ui:Vi:l?ws:bs)(e);return st(I||e,(function(r,i){I&&(r=e[i=r]),Rn(o,i,Qn(r,t,n,i,e,a))})),o}function Kn(e,t,n){var r=n.length;if(null==e)return!r;for(e=me(e);r--;){var i=n[r],a=t[i],o=e[i];if(void 0===o&&!(i in e)||!a(o))return!1}return!0}function Zn(e,t,n){if("function"!=typeof e)throw new ve(a);return ya((function(){e.apply(void 0,n)}),t)}function Xn(e,t,n,r){var i=-1,a=dt,o=!0,s=e.length,u=[],l=t.length;if(!s)return u;n&&(t=ht(t,Yt(n))),r?(a=ft,o=!1):t.length>=200&&(a=Et,o=!1,t=new In(t));e:for(;++i-1},On.prototype.set=function(e,t){var n=this.__data__,r=Wn(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this},Pn.prototype.clear=function(){this.size=0,this.__data__={hash:new jn,map:new(hn||On),string:new jn}},Pn.prototype.delete=function(e){var t=Ki(this,e).delete(e);return this.size-=t?1:0,t},Pn.prototype.get=function(e){return Ki(this,e).get(e)},Pn.prototype.has=function(e){return Ki(this,e).has(e)},Pn.prototype.set=function(e,t){var n=Ki(this,e),r=n.size;return n.set(e,t),this.size+=n.size==r?0:1,this},In.prototype.add=In.prototype.push=function(e){return this.__data__.set(e,"__lodash_hash_undefined__"),this},In.prototype.has=function(e){return this.__data__.has(e)},Hn.prototype.clear=function(){this.__data__=new On,this.size=0},Hn.prototype.delete=function(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n},Hn.prototype.get=function(e){return this.__data__.get(e)},Hn.prototype.has=function(e){return this.__data__.has(e)},Hn.prototype.set=function(e,t){var n=this.__data__;if(n instanceof On){var r=n.__data__;if(!hn||r.length<199)return r.push([e,t]),this.size=++n.size,this;n=this.__data__=new Pn(r)}return n.set(e,t),this.size=n.size,this};var er=wi(ur),tr=wi(lr,!0);function nr(e,t){var n=!0;return er(e,(function(e,r,i){return n=!!t(e,r,i)})),n}function rr(e,t,n){for(var r=-1,i=e.length;++r0&&n(s)?t>1?ar(s,t-1,n,r,i):pt(i,s):r||(i[i.length]=s)}return i}var or=Ai(),sr=Ai(!0);function ur(e,t){return e&&or(e,t,bs)}function lr(e,t){return e&&sr(e,t,bs)}function cr(e,t){return ct(t,(function(t){return Bo(e[t])}))}function dr(e,t){for(var n=0,r=(t=si(t,e)).length;null!=e&&nt}function mr(e,t){return null!=e&&Me.call(e,t)}function _r(e,t){return null!=e&&t in me(e)}function gr(e,t,n){for(var i=n?ft:dt,a=e[0].length,o=e.length,s=o,u=r(o),l=1/0,c=[];s--;){var d=e[s];s&&t&&(d=ht(d,Yt(t))),l=sn(d.length,l),u[s]=!n&&(t||a>=120&&d.length>=120)?new In(s&&d):void 0}d=e[0];var f=-1,h=u[0];e:for(;++f=s)return u;var l=n[r];return u*("desc"==l?-1:1)}}return e.index-t.index}(e,t,n)}))}function jr(e,t,n){for(var r=-1,i=t.length,a={};++r-1;)s!==e&&Je.call(s,u,1),Je.call(e,u,1);return e}function Pr(e,t){for(var n=e?t.length:0,r=n-1;n--;){var i=t[n];if(n==r||i!==a){var a=i;oa(i)?Je.call(e,i,1):Xr(e,i)}}return e}function Ir(e,t){return e+Xt(cn()*(t-e+1))}function Hr(e,t){var n="";if(!e||t<1||t>9007199254740991)return n;do{t%2&&(n+=e),(t=Xt(t/2))&&(e+=e)}while(t);return n}function Nr(e,t){return ba(pa(e,t,Us),e+"")}function Fr(e){return Fn(Ss(e))}function Br(e,t){var n=Ss(e);return xa(n,Jn(t,0,n.length))}function zr(e,t,n,r){if(!Ro(e))return e;for(var i=-1,a=(t=si(t,e)).length,o=a-1,s=e;null!=s&&++ia?0:a+t),(n=n>a?a:n)<0&&(n+=a),a=t>n?0:n-t>>>0,t>>>=0;for(var o=r(a);++i>>1,o=e[a];null!==o&&!Ko(o)&&(n?o<=t:o=200){var l=t?null:Hi(e);if(l)return Rt(l);o=!1,i=Et,u=new In}else u=t?[]:s;e:for(;++r=r?e:Vr(e,t,n)}var ci=Jt||function(e){return qe.clearTimeout(e)};function di(e,t){if(t)return e.slice();var n=e.length,r=$e?$e(n):new e.constructor(n);return e.copy(r),r}function fi(e){var t=new e.constructor(e.byteLength);return new Ie(t).set(new Ie(e)),t}function hi(e,t){var n=t?fi(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.length)}function pi(e,t){if(e!==t){var n=void 0!==e,r=null===e,i=e==e,a=Ko(e),o=void 0!==t,s=null===t,u=t==t,l=Ko(t);if(!s&&!l&&!a&&e>t||a&&o&&u&&!s&&!l||r&&o&&u||!n&&u||!i)return 1;if(!r&&!a&&!l&&e1?n[i-1]:void 0,o=i>2?n[2]:void 0;for(a=e.length>3&&"function"==typeof a?(i--,a):void 0,o&&sa(n[0],n[1],o)&&(a=i<3?void 0:a,i=1),t=me(t);++r-1?i[a?t[o]:o]:void 0}}function Li(e){return Wi((function(t){var n=t.length,r=n,i=Cn.prototype.thru;for(e&&t.reverse();r--;){var o=t[r];if("function"!=typeof o)throw new ve(a);if(i&&!s&&"wrapper"==Gi(o))var s=new Cn([],!0)}for(r=s?r:n;++r1&&y.reverse(),d&&ls))return!1;var l=a.get(e),c=a.get(t);if(l&&c)return l==t&&c==e;var d=-1,f=!0,h=2&n?new In:void 0;for(a.set(e,t),a.set(t,e);++d-1&&e%1==0&&e1?"& ":"")+t[r],t=t.join(n>2?", ":" "),e.replace(K,"{\n/* [wrapped with "+t+"] */\n")}(r,function(e,t){return st(s,(function(n){var r="_."+n[0];t&n[1]&&!dt(e,r)&&e.push(r)})),e.sort()}(function(e){var t=e.match(Z);return t?t[1].split(X):[]}(r),n)))}function Aa(e){var t=0,n=0;return function(){var r=un(),i=16-(r-n);if(n=r,i>0){if(++t>=800)return arguments[0]}else t=0;return e.apply(void 0,arguments)}}function xa(e,t){var n=-1,r=e.length,i=r-1;for(t=void 0===t?r:t;++n1?e[t-1]:void 0;return n="function"==typeof n?(e.pop(),n):void 0,qa(e,n)}));function eo(e){var t=Tn(e);return t.__chain__=!0,t}function to(e,t){return t(e)}var no=Wi((function(e){var t=e.length,n=t?e[0]:0,r=this.__wrapped__,i=function(t){return Gn(t,e)};return!(t>1||this.__actions__.length)&&r instanceof En&&oa(n)?((r=r.slice(n,+n+(t?1:0))).__actions__.push({func:to,args:[i],thisArg:void 0}),new Cn(r,this.__chain__).thru((function(e){return t&&!e.length&&e.push(void 0),e}))):this.thru(i)}));var ro=yi((function(e,t,n){Me.call(e,n)?++e[n]:qn(e,n,1)}));var io=Di(Ca),ao=Di(Ea);function oo(e,t){return(jo(e)?st:er)(e,Qi(t,3))}function so(e,t){return(jo(e)?ut:tr)(e,Qi(t,3))}var uo=yi((function(e,t,n){Me.call(e,n)?e[n].push(t):qn(e,n,[t])}));var lo=Nr((function(e,t,n){var i=-1,a="function"==typeof t,o=Po(e)?r(e.length):[];return er(e,(function(e){o[++i]=a?at(t,e,n):vr(e,t,n)})),o})),co=yi((function(e,t,n){qn(e,n,t)}));function fo(e,t){return(jo(e)?ht:Lr)(e,Qi(t,3))}var ho=yi((function(e,t,n){e[n?0:1].push(t)}),(function(){return[[],[]]}));var po=Nr((function(e,t){if(null==e)return[];var n=t.length;return n>1&&sa(e,t[0],t[1])?t=[]:n>2&&sa(t[0],t[1],t[2])&&(t=[t[0]]),Er(e,ar(t,1),[])})),mo=Qt||function(){return qe.Date.now()};function _o(e,t,n){return t=n?void 0:t,Fi(e,128,void 0,void 0,void 0,void 0,t=e&&null==t?e.length:t)}function go(e,t){var n;if("function"!=typeof t)throw new ve(a);return e=rs(e),function(){return--e>0&&(n=t.apply(this,arguments)),e<=1&&(t=void 0),n}}var vo=Nr((function(e,t,n){var r=1;if(n.length){var i=$t(n,Ji(vo));r|=32}return Fi(e,r,t,n,i)})),yo=Nr((function(e,t,n){var r=3;if(n.length){var i=$t(n,Ji(yo));r|=32}return Fi(t,r,e,n,i)}));function bo(e,t,n){var r,i,o,s,u,l,c=0,d=!1,f=!1,h=!0;if("function"!=typeof e)throw new ve(a);function p(t){var n=r,a=i;return r=i=void 0,c=t,s=e.apply(a,n)}function m(e){return c=e,u=ya(g,t),d?p(e):s}function _(e){var n=e-l;return void 0===l||n>=t||n<0||f&&e-c>=o}function g(){var e=mo();if(_(e))return v(e);u=ya(g,function(e){var n=t-(e-l);return f?sn(n,o-(e-c)):n}(e))}function v(e){return u=void 0,h&&r?p(e):(r=i=void 0,s)}function y(){var e=mo(),n=_(e);if(r=arguments,i=this,l=e,n){if(void 0===u)return m(l);if(f)return ci(u),u=ya(g,t),p(l)}return void 0===u&&(u=ya(g,t)),s}return t=as(t)||0,Ro(n)&&(d=!!n.leading,o=(f="maxWait"in n)?on(as(n.maxWait)||0,t):o,h="trailing"in n?!!n.trailing:h),y.cancel=function(){void 0!==u&&ci(u),c=0,r=l=i=u=void 0},y.flush=function(){return void 0===u?s:v(mo())},y}var wo=Nr((function(e,t){return Zn(e,1,t)})),Ao=Nr((function(e,t,n){return Zn(e,as(t)||0,n)}));function xo(e,t){if("function"!=typeof e||null!=t&&"function"!=typeof t)throw new ve(a);var n=function(){var r=arguments,i=t?t.apply(this,r):r[0],a=n.cache;if(a.has(i))return a.get(i);var o=e.apply(this,r);return n.cache=a.set(i,o)||a,o};return n.cache=new(xo.Cache||Pn),n}function Mo(e){if("function"!=typeof e)throw new ve(a);return function(){var t=arguments;switch(t.length){case 0:return!e.call(this);case 1:return!e.call(this,t[0]);case 2:return!e.call(this,t[0],t[1]);case 3:return!e.call(this,t[0],t[1],t[2])}return!e.apply(this,t)}}xo.Cache=Pn;var ko=ui((function(e,t){var n=(t=1==t.length&&jo(t[0])?ht(t[0],Yt(Qi())):ht(ar(t,1),Yt(Qi()))).length;return Nr((function(r){for(var i=-1,a=sn(r.length,n);++i=t})),Eo=yr(function(){return arguments}())?yr:function(e){return Wo(e)&&Me.call(e,"callee")&&!Ge.call(e,"callee")},jo=r.isArray,Oo=Xe?Yt(Xe):function(e){return Wo(e)&&hr(e)==x};function Po(e){return null!=e&&$o(e.length)&&!Bo(e)}function Io(e){return Wo(e)&&Po(e)}var Ho=tn||au,No=et?Yt(et):function(e){return Wo(e)&&hr(e)==d};function Fo(e){if(!Wo(e))return!1;var t=hr(e);return t==f||"[object DOMException]"==t||"string"==typeof e.message&&"string"==typeof e.name&&!qo(e)}function Bo(e){if(!Ro(e))return!1;var t=hr(e);return t==h||t==p||"[object AsyncFunction]"==t||"[object Proxy]"==t}function zo(e){return"number"==typeof e&&e==rs(e)}function $o(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=9007199254740991}function Ro(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}function Wo(e){return null!=e&&"object"==typeof e}var Vo=tt?Yt(tt):function(e){return Wo(e)&&na(e)==m};function Uo(e){return"number"==typeof e||Wo(e)&&hr(e)==_}function qo(e){if(!Wo(e)||hr(e)!=g)return!1;var t=Ve(e);if(null===t)return!0;var n=Me.call(t,"constructor")&&t.constructor;return"function"==typeof n&&n instanceof n&&xe.call(n)==Te}var Go=nt?Yt(nt):function(e){return Wo(e)&&hr(e)==v};var Jo=rt?Yt(rt):function(e){return Wo(e)&&na(e)==y};function Qo(e){return"string"==typeof e||!jo(e)&&Wo(e)&&hr(e)==b}function Ko(e){return"symbol"==typeof e||Wo(e)&&hr(e)==w}var Zo=it?Yt(it):function(e){return Wo(e)&&$o(e.length)&&!!Be[hr(e)]};var Xo=Oi(Dr),es=Oi((function(e,t){return e<=t}));function ts(e){if(!e)return[];if(Po(e))return Qo(e)?Ut(e):gi(e);if(Ze&&e[Ze])return function(e){for(var t,n=[];!(t=e.next()).done;)n.push(t.value);return n}(e[Ze]());var t=na(e);return(t==m?Bt:t==y?Rt:Ss)(e)}function ns(e){return e?(e=as(e))===1/0||e===-1/0?17976931348623157e292*(e<0?-1:1):e==e?e:0:0===e?e:0}function rs(e){var t=ns(e),n=t%1;return t==t?n?t-n:t:0}function is(e){return e?Jn(rs(e),0,4294967295):0}function as(e){if("number"==typeof e)return e;if(Ko(e))return NaN;if(Ro(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=Ro(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(G,"");var n=ae.test(e);return n||se.test(e)?We(e.slice(2),n?2:8):ie.test(e)?NaN:+e}function os(e){return vi(e,ws(e))}function ss(e){return null==e?"":Kr(e)}var us=bi((function(e,t){if(da(t)||Po(t))vi(t,bs(t),e);else for(var n in t)Me.call(t,n)&&Rn(e,n,t[n])})),ls=bi((function(e,t){vi(t,ws(t),e)})),cs=bi((function(e,t,n,r){vi(t,ws(t),e,r)})),ds=bi((function(e,t,n,r){vi(t,bs(t),e,r)})),fs=Wi(Gn);var hs=Nr((function(e,t){e=me(e);var n=-1,r=t.length,i=r>2?t[2]:void 0;for(i&&sa(t[0],t[1],i)&&(r=1);++n1),t})),vi(e,Ui(e),n),r&&(n=Qn(n,7,$i));for(var i=t.length;i--;)Xr(n,t[i]);return n}));var ks=Wi((function(e,t){return null==e?{}:function(e,t){return jr(e,t,(function(t,n){return _s(e,n)}))}(e,t)}));function Ds(e,t){if(null==e)return{};var n=ht(Ui(e),(function(e){return[e]}));return t=Qi(t),jr(e,n,(function(e,n){return t(e,n[0])}))}var Ls=Ni(bs),Ts=Ni(ws);function Ss(e){return null==e?[]:Ct(e,bs(e))}var Ys=Mi((function(e,t,n){return t=t.toLowerCase(),e+(n?Cs(t):t)}));function Cs(e){return Fs(ss(e).toLowerCase())}function Es(e){return(e=ss(e))&&e.replace(le,It).replace(je,"")}var js=Mi((function(e,t,n){return e+(n?"-":"")+t.toLowerCase()})),Os=Mi((function(e,t,n){return e+(n?" ":"")+t.toLowerCase()})),Ps=xi("toLowerCase");var Is=Mi((function(e,t,n){return e+(n?"_":"")+t.toLowerCase()}));var Hs=Mi((function(e,t,n){return e+(n?" ":"")+Fs(t)}));var Ns=Mi((function(e,t,n){return e+(n?" ":"")+t.toUpperCase()})),Fs=xi("toUpperCase");function Bs(e,t,n){return e=ss(e),void 0===(t=n?void 0:t)?function(e){return He.test(e)}(e)?function(e){return e.match(Pe)||[]}(e):function(e){return e.match(ee)||[]}(e):e.match(t)||[]}var zs=Nr((function(e,t){try{return at(e,void 0,t)}catch(e){return Fo(e)?e:new fe(e)}})),$s=Wi((function(e,t){return st(t,(function(t){t=ka(t),qn(e,t,vo(e[t],e))})),e}));function Rs(e){return function(){return e}}var Ws=Li(),Vs=Li(!0);function Us(e){return e}function qs(e){return xr("function"==typeof e?e:Qn(e,1))}var Gs=Nr((function(e,t){return function(n){return vr(n,e,t)}})),Js=Nr((function(e,t){return function(n){return vr(e,n,t)}}));function Qs(e,t,n){var r=bs(t),i=cr(t,r);null!=n||Ro(t)&&(i.length||!r.length)||(n=t,t=e,e=this,i=cr(t,bs(t)));var a=!(Ro(n)&&"chain"in n&&!n.chain),o=Bo(e);return st(i,(function(n){var r=t[n];e[n]=r,o&&(e.prototype[n]=function(){var t=this.__chain__;if(a||t){var n=e(this.__wrapped__),i=n.__actions__=gi(this.__actions__);return i.push({func:r,args:arguments,thisArg:e}),n.__chain__=t,n}return r.apply(e,pt([this.value()],arguments))})})),e}function Ks(){}var Zs=Ci(ht),Xs=Ci(lt),eu=Ci(gt);function tu(e){return ua(e)?kt(ka(e)):function(e){return function(t){return dr(t,e)}}(e)}var nu=ji(),ru=ji(!0);function iu(){return[]}function au(){return!1}var ou=Yi((function(e,t){return e+t}),0),su=Ii("ceil"),uu=Yi((function(e,t){return e/t}),1),lu=Ii("floor");var cu,du=Yi((function(e,t){return e*t}),1),fu=Ii("round"),hu=Yi((function(e,t){return e-t}),0);return Tn.after=function(e,t){if("function"!=typeof t)throw new ve(a);return e=rs(e),function(){if(--e<1)return t.apply(this,arguments)}},Tn.ary=_o,Tn.assign=us,Tn.assignIn=ls,Tn.assignInWith=cs,Tn.assignWith=ds,Tn.at=fs,Tn.before=go,Tn.bind=vo,Tn.bindAll=$s,Tn.bindKey=yo,Tn.castArray=function(){if(!arguments.length)return[];var e=arguments[0];return jo(e)?e:[e]},Tn.chain=eo,Tn.chunk=function(e,t,n){t=(n?sa(e,t,n):void 0===t)?1:on(rs(t),0);var i=null==e?0:e.length;if(!i||t<1)return[];for(var a=0,o=0,s=r(Zt(i/t));ai?0:i+n),(r=void 0===r||r>i?i:rs(r))<0&&(r+=i),r=n>r?0:is(r);n>>0)?(e=ss(e))&&("string"==typeof t||null!=t&&!Go(t))&&!(t=Kr(t))&&Ft(e)?li(Ut(e),0,n):e.split(t,n):[]},Tn.spread=function(e,t){if("function"!=typeof e)throw new ve(a);return t=null==t?0:on(rs(t),0),Nr((function(n){var r=n[t],i=li(n,0,t);return r&&pt(i,r),at(e,this,i)}))},Tn.tail=function(e){var t=null==e?0:e.length;return t?Vr(e,1,t):[]},Tn.take=function(e,t,n){return e&&e.length?Vr(e,0,(t=n||void 0===t?1:rs(t))<0?0:t):[]},Tn.takeRight=function(e,t,n){var r=null==e?0:e.length;return r?Vr(e,(t=r-(t=n||void 0===t?1:rs(t)))<0?0:t,r):[]},Tn.takeRightWhile=function(e,t){return e&&e.length?ti(e,Qi(t,3),!1,!0):[]},Tn.takeWhile=function(e,t){return e&&e.length?ti(e,Qi(t,3)):[]},Tn.tap=function(e,t){return t(e),e},Tn.throttle=function(e,t,n){var r=!0,i=!0;if("function"!=typeof e)throw new ve(a);return Ro(n)&&(r="leading"in n?!!n.leading:r,i="trailing"in n?!!n.trailing:i),bo(e,t,{leading:r,maxWait:t,trailing:i})},Tn.thru=to,Tn.toArray=ts,Tn.toPairs=Ls,Tn.toPairsIn=Ts,Tn.toPath=function(e){return jo(e)?ht(e,ka):Ko(e)?[e]:gi(Ma(ss(e)))},Tn.toPlainObject=os,Tn.transform=function(e,t,n){var r=jo(e),i=r||Ho(e)||Zo(e);if(t=Qi(t,4),null==n){var a=e&&e.constructor;n=i?r?new a:[]:Ro(e)&&Bo(a)?Sn(Ve(e)):{}}return(i?st:ur)(e,(function(e,r,i){return t(n,e,r,i)})),n},Tn.unary=function(e){return _o(e,1)},Tn.union=Ra,Tn.unionBy=Wa,Tn.unionWith=Va,Tn.uniq=function(e){return e&&e.length?Zr(e):[]},Tn.uniqBy=function(e,t){return e&&e.length?Zr(e,Qi(t,2)):[]},Tn.uniqWith=function(e,t){return t="function"==typeof t?t:void 0,e&&e.length?Zr(e,void 0,t):[]},Tn.unset=function(e,t){return null==e||Xr(e,t)},Tn.unzip=Ua,Tn.unzipWith=qa,Tn.update=function(e,t,n){return null==e?e:ei(e,t,oi(n))},Tn.updateWith=function(e,t,n,r){return r="function"==typeof r?r:void 0,null==e?e:ei(e,t,oi(n),r)},Tn.values=Ss,Tn.valuesIn=function(e){return null==e?[]:Ct(e,ws(e))},Tn.without=Ga,Tn.words=Bs,Tn.wrap=function(e,t){return Do(oi(t),e)},Tn.xor=Ja,Tn.xorBy=Qa,Tn.xorWith=Ka,Tn.zip=Za,Tn.zipObject=function(e,t){return ii(e||[],t||[],Rn)},Tn.zipObjectDeep=function(e,t){return ii(e||[],t||[],zr)},Tn.zipWith=Xa,Tn.entries=Ls,Tn.entriesIn=Ts,Tn.extend=ls,Tn.extendWith=cs,Qs(Tn,Tn),Tn.add=ou,Tn.attempt=zs,Tn.camelCase=Ys,Tn.capitalize=Cs,Tn.ceil=su,Tn.clamp=function(e,t,n){return void 0===n&&(n=t,t=void 0),void 0!==n&&(n=(n=as(n))==n?n:0),void 0!==t&&(t=(t=as(t))==t?t:0),Jn(as(e),t,n)},Tn.clone=function(e){return Qn(e,4)},Tn.cloneDeep=function(e){return Qn(e,5)},Tn.cloneDeepWith=function(e,t){return Qn(e,5,t="function"==typeof t?t:void 0)},Tn.cloneWith=function(e,t){return Qn(e,4,t="function"==typeof t?t:void 0)},Tn.conformsTo=function(e,t){return null==t||Kn(e,t,bs(t))},Tn.deburr=Es,Tn.defaultTo=function(e,t){return null==e||e!=e?t:e},Tn.divide=uu,Tn.endsWith=function(e,t,n){e=ss(e),t=Kr(t);var r=e.length,i=n=void 0===n?r:Jn(rs(n),0,r);return(n-=t.length)>=0&&e.slice(n,i)==t},Tn.eq=So,Tn.escape=function(e){return(e=ss(e))&&F.test(e)?e.replace(H,Ht):e},Tn.escapeRegExp=function(e){return(e=ss(e))&&q.test(e)?e.replace(U,"\\$&"):e},Tn.every=function(e,t,n){var r=jo(e)?lt:nr;return n&&sa(e,t,n)&&(t=void 0),r(e,Qi(t,3))},Tn.find=io,Tn.findIndex=Ca,Tn.findKey=function(e,t){return yt(e,Qi(t,3),ur)},Tn.findLast=ao,Tn.findLastIndex=Ea,Tn.findLastKey=function(e,t){return yt(e,Qi(t,3),lr)},Tn.floor=lu,Tn.forEach=oo,Tn.forEachRight=so,Tn.forIn=function(e,t){return null==e?e:or(e,Qi(t,3),ws)},Tn.forInRight=function(e,t){return null==e?e:sr(e,Qi(t,3),ws)},Tn.forOwn=function(e,t){return e&&ur(e,Qi(t,3))},Tn.forOwnRight=function(e,t){return e&&lr(e,Qi(t,3))},Tn.get=ms,Tn.gt=Yo,Tn.gte=Co,Tn.has=function(e,t){return null!=e&&ra(e,t,mr)},Tn.hasIn=_s,Tn.head=Oa,Tn.identity=Us,Tn.includes=function(e,t,n,r){e=Po(e)?e:Ss(e),n=n&&!r?rs(n):0;var i=e.length;return n<0&&(n=on(i+n,0)),Qo(e)?n<=i&&e.indexOf(t,n)>-1:!!i&&wt(e,t,n)>-1},Tn.indexOf=function(e,t,n){var r=null==e?0:e.length;if(!r)return-1;var i=null==n?0:rs(n);return i<0&&(i=on(r+i,0)),wt(e,t,i)},Tn.inRange=function(e,t,n){return t=ns(t),void 0===n?(n=t,t=0):n=ns(n),function(e,t,n){return e>=sn(t,n)&&e=-9007199254740991&&e<=9007199254740991},Tn.isSet=Jo,Tn.isString=Qo,Tn.isSymbol=Ko,Tn.isTypedArray=Zo,Tn.isUndefined=function(e){return void 0===e},Tn.isWeakMap=function(e){return Wo(e)&&na(e)==A},Tn.isWeakSet=function(e){return Wo(e)&&"[object WeakSet]"==hr(e)},Tn.join=function(e,t){return null==e?"":rn.call(e,t)},Tn.kebabCase=js,Tn.last=Na,Tn.lastIndexOf=function(e,t,n){var r=null==e?0:e.length;if(!r)return-1;var i=r;return void 0!==n&&(i=(i=rs(n))<0?on(r+i,0):sn(i,r-1)),t==t?function(e,t,n){for(var r=n+1;r--;)if(e[r]===t)return r;return r}(e,t,i):bt(e,xt,i,!0)},Tn.lowerCase=Os,Tn.lowerFirst=Ps,Tn.lt=Xo,Tn.lte=es,Tn.max=function(e){return e&&e.length?rr(e,Us,pr):void 0},Tn.maxBy=function(e,t){return e&&e.length?rr(e,Qi(t,2),pr):void 0},Tn.mean=function(e){return Mt(e,Us)},Tn.meanBy=function(e,t){return Mt(e,Qi(t,2))},Tn.min=function(e){return e&&e.length?rr(e,Us,Dr):void 0},Tn.minBy=function(e,t){return e&&e.length?rr(e,Qi(t,2),Dr):void 0},Tn.stubArray=iu,Tn.stubFalse=au,Tn.stubObject=function(){return{}},Tn.stubString=function(){return""},Tn.stubTrue=function(){return!0},Tn.multiply=du,Tn.nth=function(e,t){return e&&e.length?Cr(e,rs(t)):void 0},Tn.noConflict=function(){return qe._===this&&(qe._=Se),this},Tn.noop=Ks,Tn.now=mo,Tn.pad=function(e,t,n){e=ss(e);var r=(t=rs(t))?Vt(e):0;if(!t||r>=t)return e;var i=(t-r)/2;return Ei(Xt(i),n)+e+Ei(Zt(i),n)},Tn.padEnd=function(e,t,n){e=ss(e);var r=(t=rs(t))?Vt(e):0;return t&&rt){var r=e;e=t,t=r}if(n||e%1||t%1){var i=cn();return sn(e+i*(t-e+Re("1e-"+((i+"").length-1))),t)}return Ir(e,t)},Tn.reduce=function(e,t,n){var r=jo(e)?mt:Lt,i=arguments.length<3;return r(e,Qi(t,4),n,i,er)},Tn.reduceRight=function(e,t,n){var r=jo(e)?_t:Lt,i=arguments.length<3;return r(e,Qi(t,4),n,i,tr)},Tn.repeat=function(e,t,n){return t=(n?sa(e,t,n):void 0===t)?1:rs(t),Hr(ss(e),t)},Tn.replace=function(){var e=arguments,t=ss(e[0]);return e.length<3?t:t.replace(e[1],e[2])},Tn.result=function(e,t,n){var r=-1,i=(t=si(t,e)).length;for(i||(i=1,e=void 0);++r9007199254740991)return[];var n=4294967295,r=sn(e,4294967295);e-=4294967295;for(var i=St(r,t=Qi(t));++n=a)return e;var s=n-Vt(r);if(s<1)return r;var u=o?li(o,0,s).join(""):e.slice(0,s);if(void 0===i)return u+r;if(o&&(s+=u.length-s),Go(i)){if(e.slice(s).search(i)){var l,c=u;for(i.global||(i=_e(i.source,ss(re.exec(i))+"g")),i.lastIndex=0;l=i.exec(c);)var d=l.index;u=u.slice(0,void 0===d?s:d)}}else if(e.indexOf(Kr(i),s)!=s){var f=u.lastIndexOf(i);f>-1&&(u=u.slice(0,f))}return u+r},Tn.unescape=function(e){return(e=ss(e))&&N.test(e)?e.replace(I,qt):e},Tn.uniqueId=function(e){var t=++ke;return ss(e)+t},Tn.upperCase=Ns,Tn.upperFirst=Fs,Tn.each=oo,Tn.eachRight=so,Tn.first=Oa,Qs(Tn,(cu={},ur(Tn,(function(e,t){Me.call(Tn.prototype,t)||(cu[t]=e)})),cu),{chain:!1}),Tn.VERSION="4.17.20",st(["bind","bindKey","curry","curryRight","partial","partialRight"],(function(e){Tn[e].placeholder=Tn})),st(["drop","take"],(function(e,t){En.prototype[e]=function(n){n=void 0===n?1:on(rs(n),0);var r=this.__filtered__&&!t?new En(this):this.clone();return r.__filtered__?r.__takeCount__=sn(n,r.__takeCount__):r.__views__.push({size:sn(n,4294967295),type:e+(r.__dir__<0?"Right":"")}),r},En.prototype[e+"Right"]=function(t){return this.reverse()[e](t).reverse()}})),st(["filter","map","takeWhile"],(function(e,t){var n=t+1,r=1==n||3==n;En.prototype[e]=function(e){var t=this.clone();return t.__iteratees__.push({iteratee:Qi(e,3),type:n}),t.__filtered__=t.__filtered__||r,t}})),st(["head","last"],(function(e,t){var n="take"+(t?"Right":"");En.prototype[e]=function(){return this[n](1).value()[0]}})),st(["initial","tail"],(function(e,t){var n="drop"+(t?"":"Right");En.prototype[e]=function(){return this.__filtered__?new En(this):this[n](1)}})),En.prototype.compact=function(){return this.filter(Us)},En.prototype.find=function(e){return this.filter(e).head()},En.prototype.findLast=function(e){return this.reverse().find(e)},En.prototype.invokeMap=Nr((function(e,t){return"function"==typeof e?new En(this):this.map((function(n){return vr(n,e,t)}))})),En.prototype.reject=function(e){return this.filter(Mo(Qi(e)))},En.prototype.slice=function(e,t){e=rs(e);var n=this;return n.__filtered__&&(e>0||t<0)?new En(n):(e<0?n=n.takeRight(-e):e&&(n=n.drop(e)),void 0!==t&&(n=(t=rs(t))<0?n.dropRight(-t):n.take(t-e)),n)},En.prototype.takeRightWhile=function(e){return this.reverse().takeWhile(e).reverse()},En.prototype.toArray=function(){return this.take(4294967295)},ur(En.prototype,(function(e,t){var n=/^(?:filter|find|map|reject)|While$/.test(t),r=/^(?:head|last)$/.test(t),i=Tn[r?"take"+("last"==t?"Right":""):t],a=r||/^find/.test(t);i&&(Tn.prototype[t]=function(){var t=this.__wrapped__,o=r?[1]:arguments,s=t instanceof En,u=o[0],l=s||jo(t),c=function(e){var t=i.apply(Tn,pt([e],o));return r&&d?t[0]:t};l&&n&&"function"==typeof u&&1!=u.length&&(s=l=!1);var d=this.__chain__,f=!!this.__actions__.length,h=a&&!d,p=s&&!f;if(!a&&l){t=p?t:new En(this);var m=e.apply(t,o);return m.__actions__.push({func:to,args:[c],thisArg:void 0}),new Cn(m,d)}return h&&p?e.apply(this,o):(m=this.thru(c),h?r?m.value()[0]:m.value():m)})})),st(["pop","push","shift","sort","splice","unshift"],(function(e){var t=ye[e],n=/^(?:push|sort|unshift)$/.test(e)?"tap":"thru",r=/^(?:pop|shift)$/.test(e);Tn.prototype[e]=function(){var e=arguments;if(r&&!this.__chain__){var i=this.value();return t.apply(jo(i)?i:[],e)}return this[n]((function(n){return t.apply(jo(n)?n:[],e)}))}})),ur(En.prototype,(function(e,t){var n=Tn[t];if(n){var r=n.name+"";Me.call(yn,r)||(yn[r]=[]),yn[r].push({name:t,func:n})}})),yn[Ti(void 0,2).name]=[{name:"wrapper",func:void 0}],En.prototype.clone=function(){var e=new En(this.__wrapped__);return e.__actions__=gi(this.__actions__),e.__dir__=this.__dir__,e.__filtered__=this.__filtered__,e.__iteratees__=gi(this.__iteratees__),e.__takeCount__=this.__takeCount__,e.__views__=gi(this.__views__),e},En.prototype.reverse=function(){if(this.__filtered__){var e=new En(this);e.__dir__=-1,e.__filtered__=!0}else(e=this.clone()).__dir__*=-1;return e},En.prototype.value=function(){var e=this.__wrapped__.value(),t=this.__dir__,n=jo(e),r=t<0,i=n?e.length:0,a=function(e,t,n){var r=-1,i=n.length;for(;++r=this.__values__.length;return{done:e,value:e?void 0:this.__values__[this.__index__++]}},Tn.prototype.plant=function(e){for(var t,n=this;n instanceof Yn;){var r=La(n);r.__index__=0,r.__values__=void 0,t?i.__wrapped__=r:t=r;var i=r;n=n.__wrapped__}return i.__wrapped__=e,t},Tn.prototype.reverse=function(){var e=this.__wrapped__;if(e instanceof En){var t=e;return this.__actions__.length&&(t=new En(this)),(t=t.reverse()).__actions__.push({func:to,args:[$a],thisArg:void 0}),new Cn(t,this.__chain__)}return this.thru($a)},Tn.prototype.toJSON=Tn.prototype.valueOf=Tn.prototype.value=function(){return ni(this.__wrapped__,this.__actions__)},Tn.prototype.first=Tn.prototype.head,Ze&&(Tn.prototype[Ze]=function(){return this}),Tn}();qe._=Gt,void 0===(i=function(){return Gt}.call(t,n,t,r))||(r.exports=i)}).call(this)}).call(this,n(6),n(12)(e))},function(e,t,n){"use strict";var r={name:"VueTypeaheadBootstrapListItem",props:{active:{type:Boolean},data:{},screenReaderText:{type:String},htmlText:{type:String},disabled:{type:Boolean},backgroundVariant:{type:String},backgroundVariantResolver:{type:Function,default:e=>null,validator:e=>e instanceof Function},textVariant:{type:String}},data:function(){return{baseTextClasses:["vbst-item","list-group-item","list-group-item-action"]}},computed:{textClasses(){const e=[...this.baseTextClasses],t=this.backgroundVariantResolver(this.data),n="string"==typeof t&&t.trim()||this.backgroundVariant;return n&&e.push("list-group-item-"+n),this.textVariant&&e.push("text-"+this.textVariant),this.disabled&&e.push("disabled"),e.join(" ")}}},i=(n(295),n(1)),a=Object(i.a)(r,(function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("a",{class:e.textClasses,attrs:{tabindex:"0",href:"#"},on:{keydown:[function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"tab",9,t.key,"Tab")?null:e.$emit("listItemBlur")},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"esc",27,t.key,["Esc","Escape"])?null:(t.stopPropagation(),t.preventDefault(),e.$emit("listItemBlur"))},function(t){if(!t.type.indexOf("key")&&e._k(t.keyCode,"down",40,t.key,["Down","ArrowDown"]))return null;t.preventDefault()},function(t){if(!t.type.indexOf("key")&&e._k(t.keyCode,"up",38,t.key,["Up","ArrowUp"]))return null;t.preventDefault()}],keyup:[function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"down",40,t.key,["Down","ArrowDown"])?null:e.$parent.selectNextListItem(t)},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"up",38,t.key,["Up","ArrowUp"])?null:e.$parent.selectPreviousListItem(t)}]}},[n("div",{staticClass:"sr-only"},[e._v(e._s(e.screenReaderText))]),e._v(" "),n("div",{attrs:{"aria-hidden":"true"}},[e._t("suggestion",[n("span",{domProps:{innerHTML:e._s(e.htmlText)}})],null,{data:e.data,htmlText:e.htmlText})],2)])}),[],!1,null,"5bf8ce45",null).exports,o=n(228),s=n.n(o),u=n(229),l=n.n(u),c=n(230),d=n.n(c),f=n(231),h=n.n(f),p=n(232),m=n.n(p),_=n(49),g=n.n(_);function v(e){return e.normalize("NFD").replace(/[\u0300-\u036f]/g,"")}function y(e){return e.replace(//g,">")}const b=new Map([["A","[AⒶAÀÁÂẦẤẪẨÃĀĂẰẮẴẲȦǠÄǞẢÅǺǍȀȂẠẬẶḀĄȺⱯ]"],["AA","[Ꜳ]"],["AE","[ÆǼǢ]"],["AO","[Ꜵ]"],["AU","[Ꜷ]"],["AV","[ꜸꜺ]"],["AY","[Ꜽ]"],["B","[BⒷBḂḄḆɃƂƁ]"],["C","[CⒸCĆĈĊČÇḈƇȻꜾ]"],["D","[DⒹDḊĎḌḐḒḎĐƋƊƉꝹ]"],["DZ","[DZDŽ]"],["Dz","[DzDž]"],["E","[EⒺEÈÉÊỀẾỄỂẼĒḔḖĔĖËẺĚȄȆẸỆȨḜĘḘḚƐƎ]"],["F","[FⒻFḞƑꝻ]"],["G","[GⒼGǴĜḠĞĠǦĢǤƓꞠꝽꝾ]"],["H","[HⒽHĤḢḦȞḤḨḪĦⱧⱵꞍ]"],["I","[IⒾIÌÍÎĨĪĬİÏḮỈǏȈȊỊĮḬƗ]"],["J","[JⒿJĴɈ]"],["K","[KⓀKḰǨḲĶḴƘⱩꝀꝂꝄꞢ]"],["L","[LⓁLĿĹĽḶḸĻḼḺŁȽⱢⱠꝈꝆꞀ]"],["LJ","[LJ]"],["Lj","[Lj]"],["M","[MⓂMḾṀṂⱮƜ]"],["N","[NⓃNǸŃÑṄŇṆŅṊṈȠƝꞐꞤ]"],["NJ","[NJ]"],["Nj","[Nj]"],["O","[OⓄOÒÓÔỒỐỖỔÕṌȬṎŌṐṒŎȮȰÖȪỎŐǑȌȎƠỜỚỠỞỢỌỘǪǬØǾƆƟꝊꝌ]"],["OI","[Ƣ]"],["OO","[Ꝏ]"],["OU","[Ȣ]"],["P","[PⓅPṔṖƤⱣꝐꝒꝔ]"],["Q","[QⓆQꝖꝘɊ]"],["R","[RⓇRŔṘŘȐȒṚṜŖṞɌⱤꝚꞦꞂ]"],["S","[SⓈSẞŚṤŜṠŠṦṢṨȘŞⱾꞨꞄ]"],["T","[TⓉTṪŤṬȚŢṰṮŦƬƮȾꞆ]"],["TZ","[Ꜩ]"],["U","[UⓊUÙÚÛŨṸŪṺŬÜǛǗǕǙỦŮŰǓȔȖƯỪỨỮỬỰỤṲŲṶṴɄ]"],["V","[VⓋVṼṾƲꝞɅ]"],["VY","[Ꝡ]"],["W","[WⓌWẀẂŴẆẄẈⱲ]"],["X","[XⓍXẊẌ]"],["Y","[YⓎYỲÝŶỸȲẎŸỶỴƳɎỾ]"],["Z","[ZⓏZŹẐŻŽẒẔƵȤⱿⱫꝢ]"],["a","[aⓐaẚàáâầấẫẩãāăằắẵẳȧǡäǟảåǻǎȁȃạậặḁąⱥɐ]"],["aa","[ꜳ]"],["ae","[æǽǣ]"],["ao","[ꜵ]"],["au","[ꜷ]"],["av","[ꜹꜻ]"],["ay","[ꜽ]"],["b","[bⓑbḃḅḇƀƃɓ]"],["c","[cⓒcćĉċčçḉƈȼꜿↄ]"],["d","[dⓓdḋďḍḑḓḏđƌɖɗꝺ]"],["dz","[dzdž]"],["e","[eⓔeèéêềếễểẽēḕḗĕėëẻěȅȇẹệȩḝęḙḛɇɛǝ]"],["f","[fⓕfḟƒꝼ]"],["g","[gⓖgǵĝḡğġǧģǥɠꞡᵹꝿ]"],["h","[hⓗhĥḣḧȟḥḩḫẖħⱨⱶɥ]"],["hv","[ƕ]"],["i","[iⓘiìíîĩīĭïḯỉǐȉȋịįḭɨı]"],["j","[jⓙjĵǰɉ]"],["k","[kⓚkḱǩḳķḵƙⱪꝁꝃꝅꞣ]"],["l","[lⓛlŀĺľḷḹļḽḻſłƚɫⱡꝉꞁꝇ]"],["lj","[lj]"],["m","[mⓜmḿṁṃɱɯ]"],["n","[nⓝnǹńñṅňṇņṋṉƞɲʼnꞑꞥ]"],["nj","[nj]"],["o","[oⓞoòóôồốỗổõṍȭṏōṑṓŏȯȱöȫỏőǒȍȏơờớỡởợọộǫǭøǿɔꝋꝍɵ]"],["oi","[ƣ]"],["ou","[ȣ]"],["oo","[ꝏ]"],["p","[pⓟpṕṗƥᵽꝑꝓꝕ]"],["q","[qⓠqɋꝗꝙ]"],["r","[rⓡrŕṙřȑȓṛṝŗṟɍɽꝛꞧꞃ]"],["s","[sⓢsßśṥŝṡšṧṣṩșşȿꞩꞅẛ]"],["t","[tⓣtṫẗťṭțţṱṯŧƭʈⱦꞇ]"],["tz","[ꜩ]"],["u","[uⓤuùúûũṹūṻŭüǜǘǖǚủůűǔȕȗưừứữửựụṳųṷṵʉ]"],["v","[vⓥvṽṿʋꝟʌ]"],["vy","[ꝡ]"],["w","[wⓦwẁẃŵẇẅẘẉⱳ]"],["x","[xⓧxẋẍ]"],["y","[yⓨyỳýŷỹȳẏÿỷẙỵƴɏỿ]"],["z","[zⓩzźẑżžẓẕƶȥɀⱬꝣ"]]);var w={name:"VueTypeaheadBootstrapList",components:{VueTypeaheadBootstrapListItem:a},props:{data:{type:Array,required:!0,validator:e=>e instanceof Array},query:{type:String,default:""},vbtUniqueId:{type:Number,required:!0},backgroundVariant:{type:String},backgroundVariantResolver:{type:Function,default:e=>null,validator:e=>e instanceof Function},disableSort:{type:Boolean},textVariant:{type:String},maxMatches:{type:Number,default:10},minMatchingChars:{type:Number,default:2},disabledValues:{type:Array,default:()=>[]},showOnFocus:{type:Boolean,default:!1},showAllResults:{type:Boolean,default:!1},highlightClass:{type:String,default:"vbt-matched-text"}},created(){this.$parent.$on("input",this.resetActiveListItem),this.$parent.$on("keyup",this.handleParentInputKeyup)},data:()=>({activeListItem:-1}),computed:{highlight(){return e=>(e=y(e),0===this.query.length?e:e.replace(this.highlightQuery,`$&`))},highlightQuery(){let e="";for(let t of this.query){const n=v(y(t));b.has(n)?e+=b.get(n):e+=t}return new RegExp(e,"gi")},escapedQuery(){return y(v(this.query)).replace(/[.*+?^${}()|[\]\\]/g,"\\$&")},actionableItems(){return h()(this.matchedItems,e=>this.isDisabledItem(e))},matchedItems(){if(!this.showOnFocus&&(d()(this.query)||this.query.lengthnull!==v(t.text).match(e)).sort((t,n)=>{if(this.disableSort)return 0;const r=v(t.text),i=v(n.text),a=r.indexOf(r.match(e)[0]),o=i.indexOf(i.match(e)[0]);return ao?1:0}).slice(0,this.maxMatches)}},methods:{handleParentInputKeyup(e){switch(e.keyCode){case 40:this.selectNextListItem();break;case 38:this.selectPreviousListItem();break;case 13:this.hitActiveListItem()}},handleHit(e,t){this.$emit("hit",e),t.preventDefault()},hitActiveListItem(){this.activeListItem>=0&&this.$emit("hit",this.matchedItems[this.activeListItem])},isDisabledItem(e){return l()(this.disabledValues,e.text)},isListItemActive(e){return this.activeListItem===e},resetActiveListItem(){this.activeListItem=-1},findIndexForNextActiveItem(e,t){e||(e=this.matchedItems),void 0===t&&(t=this.activeListItem);let n=g()(e,function(e){return!this.isDisabledItem(e)}.bind(this),t+1);return-1===n&&(n=g()(e,function(e){return!this.isDisabledItem(e)}.bind(this))),n},selectNextListItem(){if(this.actionableItems.length<=0)return this.activeListItem=-1,!0;this.activeListItem=this.findIndexForNextActiveItem()},selectPreviousListItem(){if(this.actionableItems.length<=0)return this.activeListItem=-1,!0;0===this.activeListItem&&(this.activeListItem=-1);let e=m()(s()(this.matchedItems)),t=this.matchedItems.length-1-this.activeListItem,n=this.findIndexForNextActiveItem(e,t);this.activeListItem=this.matchedItems.length-1-n}},watch:{activeListItem(e,t){if(this.$parent.autoClose||!1!==this.$parent.isFocused||(this.$parent.isFocused=!0),e>=0){const t=this.$refs.suggestionList,n=t.children[this.activeListItem],r=t.clientHeight,i=n.clientHeight,a=Math.floor(r/(i+20));t.scrollTop=e>=a?i*this.activeListItem:0,n.focus()}}}},A=Object(i.a)(w,(function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{ref:"suggestionList",staticClass:"list-group shadow"},e._l(e.matchedItems,(function(t,r){return n("vue-typeahead-bootstrap-list-item",e._g({key:r,attrs:{active:e.isListItemActive(r),id:!!e.isListItemActive(r)&&"selected-option-"+e.vbtUniqueId,data:t.data,"html-text":e.highlight(t.text),role:"option","aria-selected":e.isListItemActive(r)?"true":"false","screen-reader-text":t.text,disabled:e.isDisabledItem(t),"background-variant":e.backgroundVariant,"background-variant-resolver":e.backgroundVariantResolver,"text-variant":e.textVariant},nativeOn:{click:function(n){return e.handleHit(t,n)}},scopedSlots:e._u([{key:"suggestion",fn:function(t){var n=t.data,r=t.htmlText;return e.$scopedSlots.suggestion?[e._t("suggestion",null,null,{data:n,htmlText:r})]:void 0}}],null,!0)},e.$listeners))})),1)}),[],!1,null,null,null).exports,x=n(233),M={name:"VueTypeaheadBootstrap",components:{VueTypeaheadBootstrapList:A},props:{ariaLabelledBy:{type:String,default:null},size:{type:String,default:null,validator:e=>["lg","md","sm"].indexOf(e)>-1},value:{type:String},disabled:{type:Boolean,default:!1},data:{type:Array,required:!0,validator:e=>e instanceof Array},serializer:{type:Function,default:e=>e,validator:e=>e instanceof Function},backgroundVariant:String,backgroundVariantResolver:{type:Function,default:e=>e,validator:e=>e instanceof Function},disabledValues:{type:Array,default:()=>[]},textVariant:String,inputClass:{type:String,default:""},inputName:{type:String,default:void 0},maxMatches:{type:Number,default:10},minMatchingChars:{type:Number,default:2},disableSort:{type:Boolean,default:!1},showOnFocus:{type:Boolean,default:!1},showAllResults:{type:Boolean,default:!1},autoClose:{type:Boolean,default:!0},ieCloseFix:{type:Boolean,default:!0},placeholder:String,prepend:String,append:String,highlightClass:String},computed:{id:()=>Math.floor(1e5*Math.random()),inputGroupClasses(){return this.size?"input-group input-group-"+this.size:"input-group"},formattedData(){return this.data instanceof Array?this.data.map((e,t)=>({id:t,data:e,text:this.serializer(e)})):[]}},methods:{resizeList(e){const t=e.getBoundingClientRect(),n=this.$refs.list.$el.style;if(n.width=t.width+"px",this.$refs.prependDiv){const e=this.$refs.prependDiv.getBoundingClientRect();n.marginLeft=e.width+"px"}},handleHit(e){void 0!==this.value&&this.$emit("input",e.text),this.inputValue=e.text,this.$emit("hit",e.data),this.autoClose&&(this.$refs.input.blur(),this.isFocused=!1)},handleChildBlur(){this.$refs.input.focus(),this.isFocused=!1},runFocusOut(e){e&&e.classList.contains("vbst-item")||(this.isFocused=!1)},handleFocusOut(e){const t=e.relatedTarget;navigator.userAgent.match(/Trident.*rv:11\./)&&this.ieCloseFix?setTimeout(()=>{this.runFocusOut(t)},300):this.runFocusOut(t)},handleInput(e){this.isFocused=!0,this.inputValue=e,void 0!==this.value&&this.$emit("input",e)},handleEsc(e){""===e?(this.$refs.input.blur(),this.isFocused=!1):this.inputValue=""}},data(){return{isFocused:!1,inputValue:this.value||""}},mounted(){this.$_ro=new x.a(e=>{this.resizeList(this.$refs.input)}),this.$_ro.observe(this.$refs.input),this.$_ro.observe(this.$refs.list.$el)},beforeDestroy(){this.$_ro.disconnect()},watch:{value:function(e){this.inputValue=e}}},k=(n(400),Object(i.a)(M,(function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{attrs:{id:"typeahead-"+e.id,role:"combobox","aria-haspopup":"listbox","aria-owns":"result-list-"+e.id,"aria-expanded":e.isFocused&&e.data.length>0?"true":"false"}},[n("div",{class:e.inputGroupClasses},[e.$slots.prepend||e.prepend?n("div",{ref:"prependDiv",staticClass:"input-group-prepend"},[e._t("prepend",[n("span",{staticClass:"input-group-text"},[e._v(e._s(e.prepend))])])],2):e._e(),e._v(" "),n("input",{ref:"input",class:"form-control "+e.inputClass,attrs:{id:"typeahead-input-"+e.id,type:"text",role:"searchbox","aria-labelledby":e.ariaLabelledBy,"aria-multiline":"false","aria-autocomplete":"list","aria-controls":"result-list-"+e.id,"aria-activedescendant":"selected-option-"+e.id,name:e.inputName,placeholder:e.placeholder,"aria-label":!e.ariaLabelledBy&&e.placeholder,disabled:e.disabled},domProps:{value:e.inputValue},on:{focus:function(t){e.isFocused=!0},blur:e.handleFocusOut,input:function(t){return e.handleInput(t.target.value)},keydown:function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"esc",27,t.key,["Esc","Escape"])?null:e.handleEsc(t.target.value)},keyup:function(t){return e.$emit("keyup",t)}}}),e._v(" "),e.$slots.append||e.append?n("div",{staticClass:"input-group-append"},[e._t("append",[n("span",{staticClass:"input-group-text"},[e._v(e._s(e.append))])])],2):e._e()]),e._v(" "),n("vue-typeahead-bootstrap-list",{directives:[{name:"show",rawName:"v-show",value:e.isFocused&&e.data.length>0,expression:"isFocused && data.length > 0"}],ref:"list",staticClass:"vbt-autcomplete-list",attrs:{id:"result-list-"+e.id,query:e.inputValue,data:e.formattedData,"background-variant":e.backgroundVariant,"background-variant-resolver":e.backgroundVariantResolver,"text-variant":e.textVariant,maxMatches:e.maxMatches,minMatchingChars:e.minMatchingChars,disableSort:e.disableSort,showOnFocus:e.showOnFocus,showAllResults:e.showAllResults,highlightClass:e.highlightClass,disabledValues:e.disabledValues,vbtUniqueId:e.id,role:"listbox"},on:{hit:e.handleHit,listItemBlur:e.handleChildBlur},scopedSlots:e._u([e._l(e.$scopedSlots,(function(t,n){return{key:n,fn:function(t){var r=t.data,i=t.htmlText;return[e._t(n,null,null,{data:r,htmlText:i})]}}}))],null,!0)})],1)}),[],!1,null,"dbe69e32",null));t.a=k.exports},function(e,t,n){var r;!function(t,n){"use strict";"object"==typeof e.exports?e.exports=t.document?n(t,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return n(e)}:n(t)}("undefined"!=typeof window?window:this,(function(n,i){"use strict";var a=[],o=Object.getPrototypeOf,s=a.slice,u=a.flat?function(e){return a.flat.call(e)}:function(e){return a.concat.apply([],e)},l=a.push,c=a.indexOf,d={},f=d.toString,h=d.hasOwnProperty,p=h.toString,m=p.call(Object),_={},g=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},v=function(e){return null!=e&&e===e.window},y=n.document,b={type:!0,src:!0,nonce:!0,noModule:!0};function w(e,t,n){var r,i,a=(n=n||y).createElement("script");if(a.text=e,t)for(r in b)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&a.setAttribute(r,i);n.head.appendChild(a).parentNode.removeChild(a)}function A(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?d[f.call(e)]||"object":typeof e}var x=function(e,t){return new x.fn.init(e,t)};function M(e){var t=!!e&&"length"in e&&e.length,n=A(e);return!g(e)&&!v(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}x.fn=x.prototype={jquery:"3.5.1",constructor:x,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return x.each(this,e)},map:function(e){return this.pushStack(x.map(this,(function(t,n){return e.call(t,n,t)})))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(x.grep(this,(function(e,t){return(t+1)%2})))},odd:function(){return this.pushStack(x.grep(this,(function(e,t){return t%2})))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n+~]|"+H+")"+H+"*"),V=new RegExp(H+"|>"),U=new RegExp(B),q=new RegExp("^"+N+"$"),G={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N+"|[*])"),ATTR:new RegExp("^"+F),PSEUDO:new RegExp("^"+B),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+H+"*(even|odd|(([+-]|)(\\d*)n|)"+H+"*(?:([+-]|)"+H+"*(\\d+)|))"+H+"*\\)|)","i"),bool:new RegExp("^(?:"+I+")$","i"),needsContext:new RegExp("^"+H+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+H+"*((?:-\\d)?\\d*)"+H+"*\\)|)(?=[^-]|$)","i")},J=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,K=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,X=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+H+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},ae=function(){f()},oe=be((function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()}),{dir:"parentNode",next:"legend"});try{j.apply(Y=O.call(w.childNodes),w.childNodes),Y[w.childNodes.length].nodeType}catch(e){j={apply:Y.length?function(e,t){E.apply(e,O.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}function se(e,t,r,i){var a,s,l,c,d,p,g,v=t&&t.ownerDocument,w=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==w&&9!==w&&11!==w)return r;if(!i&&(f(t),t=t||h,m)){if(11!==w&&(d=X.exec(e)))if(a=d[1]){if(9===w){if(!(l=t.getElementById(a)))return r;if(l.id===a)return r.push(l),r}else if(v&&(l=v.getElementById(a))&&y(t,l)&&l.id===a)return r.push(l),r}else{if(d[2])return j.apply(r,t.getElementsByTagName(e)),r;if((a=d[3])&&n.getElementsByClassName&&t.getElementsByClassName)return j.apply(r,t.getElementsByClassName(a)),r}if(n.qsa&&!L[e+" "]&&(!_||!_.test(e))&&(1!==w||"object"!==t.nodeName.toLowerCase())){if(g=e,v=t,1===w&&(V.test(e)||W.test(e))){for((v=ee.test(e)&&ge(t.parentNode)||t)===t&&n.scope||((c=t.getAttribute("id"))?c=c.replace(re,ie):t.setAttribute("id",c=b)),s=(p=o(e)).length;s--;)p[s]=(c?"#"+c:":scope")+" "+ye(p[s]);g=p.join(",")}try{return j.apply(r,v.querySelectorAll(g)),r}catch(t){L(e,!0)}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace($,"$1"),t,r,i)}function ue(){var e=[];return function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}}function le(e){return e[b]=!0,e}function ce(e){var t=h.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function de(e,t){for(var n=e.split("|"),i=n.length;i--;)r.attrHandle[n[i]]=t}function fe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function he(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function me(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&oe(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function _e(e){return le((function(t){return t=+t,le((function(n,r){for(var i,a=e([],n.length,t),o=a.length;o--;)n[i=a[o]]&&(n[i]=!(r[i]=n[i]))}))}))}function ge(e){return e&&void 0!==e.getElementsByTagName&&e}for(t in n=se.support={},a=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!J.test(t||n&&n.nodeName||"HTML")},f=se.setDocument=function(e){var t,i,o=e?e.ownerDocument||e:w;return o!=h&&9===o.nodeType&&o.documentElement?(p=(h=o).documentElement,m=!a(h),w!=h&&(i=h.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",ae,!1):i.attachEvent&&i.attachEvent("onunload",ae)),n.scope=ce((function(e){return p.appendChild(e).appendChild(h.createElement("div")),void 0!==e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length})),n.attributes=ce((function(e){return e.className="i",!e.getAttribute("className")})),n.getElementsByTagName=ce((function(e){return e.appendChild(h.createComment("")),!e.getElementsByTagName("*").length})),n.getElementsByClassName=Z.test(h.getElementsByClassName),n.getById=ce((function(e){return p.appendChild(e).id=b,!h.getElementsByName||!h.getElementsByName(b).length})),n.getById?(r.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if(void 0!==t.getElementById&&m){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(te,ne);return function(e){var n=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if(void 0!==t.getElementById&&m){var n,r,i,a=t.getElementById(e);if(a){if((n=a.getAttributeNode("id"))&&n.value===e)return[a];for(i=t.getElementsByName(e),r=0;a=i[r++];)if((n=a.getAttributeNode("id"))&&n.value===e)return[a]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,a=t.getElementsByTagName(e);if("*"===e){for(;n=a[i++];)1===n.nodeType&&r.push(n);return r}return a},r.find.CLASS=n.getElementsByClassName&&function(e,t){if(void 0!==t.getElementsByClassName&&m)return t.getElementsByClassName(e)},g=[],_=[],(n.qsa=Z.test(h.querySelectorAll))&&(ce((function(e){var t;p.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&_.push("[*^$]="+H+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||_.push("\\["+H+"*(?:value|"+I+")"),e.querySelectorAll("[id~="+b+"-]").length||_.push("~="),(t=h.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||_.push("\\["+H+"*name"+H+"*="+H+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||_.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||_.push(".#.+[+~]"),e.querySelectorAll("\\\f"),_.push("[\\r\\n\\f]")})),ce((function(e){e.innerHTML="";var t=h.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&_.push("name"+H+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&_.push(":enabled",":disabled"),p.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&_.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),_.push(",.*:")}))),(n.matchesSelector=Z.test(v=p.matches||p.webkitMatchesSelector||p.mozMatchesSelector||p.oMatchesSelector||p.msMatchesSelector))&&ce((function(e){n.disconnectedMatch=v.call(e,"*"),v.call(e,"[s!='']:x"),g.push("!=",B)})),_=_.length&&new RegExp(_.join("|")),g=g.length&&new RegExp(g.join("|")),t=Z.test(p.compareDocumentPosition),y=t||Z.test(p.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},T=t?function(e,t){if(e===t)return d=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e==h||e.ownerDocument==w&&y(w,e)?-1:t==h||t.ownerDocument==w&&y(w,t)?1:c?P(c,e)-P(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return d=!0,0;var n,r=0,i=e.parentNode,a=t.parentNode,o=[e],s=[t];if(!i||!a)return e==h?-1:t==h?1:i?-1:a?1:c?P(c,e)-P(c,t):0;if(i===a)return fe(e,t);for(n=e;n=n.parentNode;)o.unshift(n);for(n=t;n=n.parentNode;)s.unshift(n);for(;o[r]===s[r];)r++;return r?fe(o[r],s[r]):o[r]==w?-1:s[r]==w?1:0},h):h},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(f(e),n.matchesSelector&&m&&!L[t+" "]&&(!g||!g.test(t))&&(!_||!_.test(t)))try{var r=v.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){L(t,!0)}return se(t,h,null,[e]).length>0},se.contains=function(e,t){return(e.ownerDocument||e)!=h&&f(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=h&&f(e);var i=r.attrHandle[t.toLowerCase()],a=i&&S.call(r.attrHandle,t.toLowerCase())?i(e,t,!m):void 0;return void 0!==a?a:n.attributes||!m?e.getAttribute(t):(a=e.getAttributeNode(t))&&a.specified?a.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,r=[],i=0,a=0;if(d=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(T),d){for(;t=e[a++];)t===e[a]&&(i=r.push(a));for(;i--;)e.splice(r[i],1)}return c=null,e},i=se.getText=function(e){var t,n="",r=0,a=e.nodeType;if(a){if(1===a||9===a||11===a){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===a||4===a)return e.nodeValue}else for(;t=e[r++];)n+=i(t);return n},(r=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&U.test(n)&&(t=o(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=M[e+" "];return t||(t=new RegExp("(^|"+H+")"+e+"("+H+"|$)"))&&M(e,(function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")}))},ATTR:function(e,t,n){return function(r){var i=se.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace(z," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var a="nth"!==e.slice(0,3),o="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,d,f,h,p,m=a!==o?"nextSibling":"previousSibling",_=t.parentNode,g=s&&t.nodeName.toLowerCase(),v=!u&&!s,y=!1;if(_){if(a){for(;m;){for(f=t;f=f[m];)if(s?f.nodeName.toLowerCase()===g:1===f.nodeType)return!1;p=m="only"===e&&!p&&"nextSibling"}return!0}if(p=[o?_.firstChild:_.lastChild],o&&v){for(y=(h=(l=(c=(d=(f=_)[b]||(f[b]={}))[f.uniqueID]||(d[f.uniqueID]={}))[e]||[])[0]===A&&l[1])&&l[2],f=h&&_.childNodes[h];f=++h&&f&&f[m]||(y=h=0)||p.pop();)if(1===f.nodeType&&++y&&f===t){c[e]=[A,h,y];break}}else if(v&&(y=h=(l=(c=(d=(f=t)[b]||(f[b]={}))[f.uniqueID]||(d[f.uniqueID]={}))[e]||[])[0]===A&&l[1]),!1===y)for(;(f=++h&&f&&f[m]||(y=h=0)||p.pop())&&((s?f.nodeName.toLowerCase()!==g:1!==f.nodeType)||!++y||(v&&((c=(d=f[b]||(f[b]={}))[f.uniqueID]||(d[f.uniqueID]={}))[e]=[A,y]),f!==t)););return(y-=i)===r||y%r==0&&y/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?le((function(e,n){for(var r,a=i(e,t),o=a.length;o--;)e[r=P(e,a[o])]=!(n[r]=a[o])})):function(e){return i(e,0,n)}):i}},pseudos:{not:le((function(e){var t=[],n=[],r=s(e.replace($,"$1"));return r[b]?le((function(e,t,n,i){for(var a,o=r(e,null,i,[]),s=e.length;s--;)(a=o[s])&&(e[s]=!(t[s]=a))})):function(e,i,a){return t[0]=e,r(t,null,a,n),t[0]=null,!n.pop()}})),has:le((function(e){return function(t){return se(e,t).length>0}})),contains:le((function(e){return e=e.replace(te,ne),function(t){return(t.textContent||i(t)).indexOf(e)>-1}})),lang:le((function(e){return q.test(e||"")||se.error("unsupported lang: "+e),e=e.replace(te,ne).toLowerCase(),function(t){var n;do{if(n=m?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}})),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===p},focus:function(e){return e===h.activeElement&&(!h.hasFocus||h.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:me(!1),disabled:me(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return K.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:_e((function(){return[0]})),last:_e((function(e,t){return[t-1]})),eq:_e((function(e,t,n){return[n<0?n+t:n]})),even:_e((function(e,t){for(var n=0;nt?t:n;--r>=0;)e.push(r);return e})),gt:_e((function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function Ae(e,t,n,r,i){for(var a,o=[],s=0,u=e.length,l=null!=t;s-1&&(a[l]=!(o[l]=d))}}else g=Ae(g===o?g.splice(p,g.length):g),i?i(null,o,g,u):j.apply(o,g)}))}function Me(e){for(var t,n,i,a=e.length,o=r.relative[e[0].type],s=o||r.relative[" "],u=o?1:0,c=be((function(e){return e===t}),s,!0),d=be((function(e){return P(t,e)>-1}),s,!0),f=[function(e,n,r){var i=!o&&(r||n!==l)||((t=n).nodeType?c(e,n,r):d(e,n,r));return t=null,i}];u1&&we(f),u>1&&ye(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace($,"$1"),n,u0,i=e.length>0,a=function(a,o,s,u,c){var d,p,_,g=0,v="0",y=a&&[],b=[],w=l,x=a||i&&r.find.TAG("*",c),M=A+=null==w?1:Math.random()||.1,k=x.length;for(c&&(l=o==h||o||c);v!==k&&null!=(d=x[v]);v++){if(i&&d){for(p=0,o||d.ownerDocument==h||(f(d),s=!m);_=e[p++];)if(_(d,o||h,s)){u.push(d);break}c&&(A=M)}n&&((d=!_&&d)&&g--,a&&y.push(d))}if(g+=v,n&&v!==g){for(p=0;_=t[p++];)_(y,b,o,s);if(a){if(g>0)for(;v--;)y[v]||b[v]||(b[v]=C.call(u));b=Ae(b)}j.apply(u,b),c&&!a&&b.length>0&&g+t.length>1&&se.uniqueSort(u)}return c&&(A=M,l=w),y};return n?le(a):a}(a,i))).selector=e}return s},u=se.select=function(e,t,n,i){var a,u,l,c,d,f="function"==typeof e&&e,h=!i&&o(e=f.selector||e);if(n=n||[],1===h.length){if((u=h[0]=h[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&m&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(te,ne),t)||[])[0]))return n;f&&(t=t.parentNode),e=e.slice(u.shift().value.length)}for(a=G.needsContext.test(e)?0:u.length;a--&&(l=u[a],!r.relative[c=l.type]);)if((d=r.find[c])&&(i=d(l.matches[0].replace(te,ne),ee.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(a,1),!(e=i.length&&ye(u)))return j.apply(n,i),n;break}}return(f||s(e,h))(i,t,!m,n,!t||ee.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(T).join("")===b,n.detectDuplicates=!!d,f(),n.sortDetached=ce((function(e){return 1&e.compareDocumentPosition(h.createElement("fieldset"))})),ce((function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")}))||de("type|href|height|width",(function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)})),n.attributes&&ce((function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")}))||de("value",(function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue})),ce((function(e){return null==e.getAttribute("disabled")}))||de(I,(function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null})),se}(n);x.find=k,x.expr=k.selectors,x.expr[":"]=x.expr.pseudos,x.uniqueSort=x.unique=k.uniqueSort,x.text=k.getText,x.isXMLDoc=k.isXML,x.contains=k.contains,x.escapeSelector=k.escape;var D=function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&x(e).is(n))break;r.push(e)}return r},L=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},T=x.expr.match.needsContext;function S(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var Y=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function C(e,t,n){return g(t)?x.grep(e,(function(e,r){return!!t.call(e,r,e)!==n})):t.nodeType?x.grep(e,(function(e){return e===t!==n})):"string"!=typeof t?x.grep(e,(function(e){return c.call(t,e)>-1!==n})):x.filter(t,e,n)}x.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,(function(e){return 1===e.nodeType})))},x.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(x(e).filter((function(){for(t=0;t1?x.uniqueSort(n):n},filter:function(e){return this.pushStack(C(this,e||[],!1))},not:function(e){return this.pushStack(C(this,e||[],!0))},is:function(e){return!!C(this,"string"==typeof e&&T.test(e)?x(e):e||[],!1).length}});var E,j=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(x.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||E,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:j.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof x?t[0]:t,x.merge(this,x.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:y,!0)),Y.test(r[1])&&x.isPlainObject(t))for(r in t)g(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=y.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(x):x.makeArray(e,this)}).prototype=x.fn,E=x(y);var O=/^(?:parents|prev(?:Until|All))/,P={children:!0,contents:!0,next:!0,prev:!0};function I(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}x.fn.extend({has:function(e){var t=x(e,this),n=t.length;return this.filter((function(){for(var e=0;e-1:1===n.nodeType&&x.find.matchesSelector(n,e))){a.push(n);break}return this.pushStack(a.length>1?x.uniqueSort(a):a)},index:function(e){return e?"string"==typeof e?c.call(x(e),this[0]):c.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(x.uniqueSort(x.merge(this.get(),x(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return D(e,"parentNode")},parentsUntil:function(e,t,n){return D(e,"parentNode",n)},next:function(e){return I(e,"nextSibling")},prev:function(e){return I(e,"previousSibling")},nextAll:function(e){return D(e,"nextSibling")},prevAll:function(e){return D(e,"previousSibling")},nextUntil:function(e,t,n){return D(e,"nextSibling",n)},prevUntil:function(e,t,n){return D(e,"previousSibling",n)},siblings:function(e){return L((e.parentNode||{}).firstChild,e)},children:function(e){return L(e.firstChild)},contents:function(e){return null!=e.contentDocument&&o(e.contentDocument)?e.contentDocument:(S(e,"template")&&(e=e.content||e),x.merge([],e.childNodes))}},(function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(P[e]||x.uniqueSort(i),O.test(e)&&i.reverse()),this.pushStack(i)}}));var H=/[^\x20\t\r\n\f]+/g;function N(e){return e}function F(e){throw e}function B(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}x.Callbacks=function(e){e="string"==typeof e?function(e){var t={};return x.each(e.match(H)||[],(function(e,n){t[n]=!0})),t}(e):x.extend({},e);var t,n,r,i,a=[],o=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;o.length;s=-1)for(n=o.shift();++s-1;)a.splice(n,1),n<=s&&s--})),this},has:function(e){return e?x.inArray(e,a)>-1:a.length>0},empty:function(){return a&&(a=[]),this},disable:function(){return i=o=[],a=n="",this},disabled:function(){return!a},lock:function(){return i=o=[],n||t||(a=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],o.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l},x.extend({Deferred:function(e){var t=[["notify","progress",x.Callbacks("memory"),x.Callbacks("memory"),2],["resolve","done",x.Callbacks("once memory"),x.Callbacks("once memory"),0,"resolved"],["reject","fail",x.Callbacks("once memory"),x.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return a.done(arguments).fail(arguments),this},catch:function(e){return i.then(null,e)},pipe:function(){var e=arguments;return x.Deferred((function(n){x.each(t,(function(t,r){var i=g(e[r[4]])&&e[r[4]];a[r[1]]((function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(n.notify).done(n.resolve).fail(n.reject):n[r[0]+"With"](this,i?[e]:arguments)}))})),e=null})).promise()},then:function(e,r,i){var a=0;function o(e,t,r,i){return function(){var s=this,u=arguments,l=function(){var n,l;if(!(e=a&&(r!==F&&(s=void 0,u=[n]),t.rejectWith(s,u))}};e?c():(x.Deferred.getStackHook&&(c.stackTrace=x.Deferred.getStackHook()),n.setTimeout(c))}}return x.Deferred((function(n){t[0][3].add(o(0,n,g(i)?i:N,n.notifyWith)),t[1][3].add(o(0,n,g(e)?e:N)),t[2][3].add(o(0,n,g(r)?r:F))})).promise()},promise:function(e){return null!=e?x.extend(e,i):i}},a={};return x.each(t,(function(e,n){var o=n[2],s=n[5];i[n[1]]=o.add,s&&o.add((function(){r=s}),t[3-e][2].disable,t[3-e][3].disable,t[0][2].lock,t[0][3].lock),o.add(n[3].fire),a[n[0]]=function(){return a[n[0]+"With"](this===a?void 0:this,arguments),this},a[n[0]+"With"]=o.fireWith})),i.promise(a),e&&e.call(a,a),a},when:function(e){var t=arguments.length,n=t,r=Array(n),i=s.call(arguments),a=x.Deferred(),o=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?s.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&(B(e,a.done(o(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();for(;n--;)B(i[n],o(n),a.reject);return a.promise()}});var z=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;x.Deferred.exceptionHook=function(e,t){n.console&&n.console.warn&&e&&z.test(e.name)&&n.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},x.readyException=function(e){n.setTimeout((function(){throw e}))};var $=x.Deferred();function R(){y.removeEventListener("DOMContentLoaded",R),n.removeEventListener("load",R),x.ready()}x.fn.ready=function(e){return $.then(e).catch((function(e){x.readyException(e)})),this},x.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--x.readyWait:x.isReady)||(x.isReady=!0,!0!==e&&--x.readyWait>0||$.resolveWith(y,[x]))}}),x.ready.then=$.then,"complete"===y.readyState||"loading"!==y.readyState&&!y.documentElement.doScroll?n.setTimeout(x.ready):(y.addEventListener("DOMContentLoaded",R),n.addEventListener("load",R));var W=function(e,t,n,r,i,a,o){var s=0,u=e.length,l=null==n;if("object"===A(n))for(s in i=!0,n)W(e,t,s,n[s],!0,a,o);else if(void 0!==r&&(i=!0,g(r)||(o=!0),l&&(o?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(x(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each((function(){Z.remove(this,e)}))}}),x.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=K.get(e,t),n&&(!r||Array.isArray(n)?r=K.access(e,t,x.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),a=x._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete a.stop,i.call(e,(function(){x.dequeue(e,t)}),a)),!r&&a&&a.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return K.get(e,n)||K.access(e,n,{empty:x.Callbacks("once memory").add((function(){K.remove(e,[t+"queue",n])}))})}}),x.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]*)/i,ge=/^$|^module$|\/(?:java|ecma)script/i;he=y.createDocumentFragment().appendChild(y.createElement("div")),(pe=y.createElement("input")).setAttribute("type","radio"),pe.setAttribute("checked","checked"),pe.setAttribute("name","t"),he.appendChild(pe),_.checkClone=he.cloneNode(!0).cloneNode(!0).lastChild.checked,he.innerHTML="",_.noCloneChecked=!!he.cloneNode(!0).lastChild.defaultValue,he.innerHTML="",_.option=!!he.lastChild;var ve={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ye(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&S(e,t)?x.merge([e],n):n}function be(e,t){for(var n=0,r=e.length;n",""]);var we=/<|&#?\w+;/;function Ae(e,t,n,r,i){for(var a,o,s,u,l,c,d=t.createDocumentFragment(),f=[],h=0,p=e.length;h-1)i&&i.push(a);else if(l=oe(a),o=ye(d.appendChild(a),"script"),l&&be(o),n)for(c=0;a=o[c++];)ge.test(a.type||"")&&n.push(a);return d}var xe=/^key/,Me=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ke=/^([^.]*)(?:\.(.+)|)/;function De(){return!0}function Le(){return!1}function Te(e,t){return e===function(){try{return y.activeElement}catch(e){}}()==("focus"===t)}function Se(e,t,n,r,i,a){var o,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Se(e,s,n,r,t[s],a);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Le;else if(!i)return e;return 1===a&&(o=i,(i=function(e){return x().off(e),o.apply(this,arguments)}).guid=o.guid||(o.guid=x.guid++)),e.each((function(){x.event.add(this,t,i,r,n)}))}function Ye(e,t,n){n?(K.set(e,t,!1),x.event.add(e,t,{namespace:!1,handler:function(e){var r,i,a=K.get(this,t);if(1&e.isTrigger&&this[t]){if(a.length)(x.event.special[t]||{}).delegateType&&e.stopPropagation();else if(a=s.call(arguments),K.set(this,t,a),r=n(this,t),this[t](),a!==(i=K.get(this,t))||r?K.set(this,t,!1):i={},a!==i)return e.stopImmediatePropagation(),e.preventDefault(),i.value}else a.length&&(K.set(this,t,{value:x.event.trigger(x.extend(a[0],x.Event.prototype),a.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===K.get(e,t)&&x.event.add(e,t,De)}x.event={global:{},add:function(e,t,n,r,i){var a,o,s,u,l,c,d,f,h,p,m,_=K.get(e);if(J(e))for(n.handler&&(n=(a=n).handler,i=a.selector),i&&x.find.matchesSelector(ae,i),n.guid||(n.guid=x.guid++),(u=_.events)||(u=_.events=Object.create(null)),(o=_.handle)||(o=_.handle=function(t){return void 0!==x&&x.event.triggered!==t.type?x.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(H)||[""]).length;l--;)h=m=(s=ke.exec(t[l])||[])[1],p=(s[2]||"").split(".").sort(),h&&(d=x.event.special[h]||{},h=(i?d.delegateType:d.bindType)||h,d=x.event.special[h]||{},c=x.extend({type:h,origType:m,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&x.expr.match.needsContext.test(i),namespace:p.join(".")},a),(f=u[h])||((f=u[h]=[]).delegateCount=0,d.setup&&!1!==d.setup.call(e,r,p,o)||e.addEventListener&&e.addEventListener(h,o)),d.add&&(d.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?f.splice(f.delegateCount++,0,c):f.push(c),x.event.global[h]=!0)},remove:function(e,t,n,r,i){var a,o,s,u,l,c,d,f,h,p,m,_=K.hasData(e)&&K.get(e);if(_&&(u=_.events)){for(l=(t=(t||"").match(H)||[""]).length;l--;)if(h=m=(s=ke.exec(t[l])||[])[1],p=(s[2]||"").split(".").sort(),h){for(d=x.event.special[h]||{},f=u[h=(r?d.delegateType:d.bindType)||h]||[],s=s[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),o=a=f.length;a--;)c=f[a],!i&&m!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(f.splice(a,1),c.selector&&f.delegateCount--,d.remove&&d.remove.call(e,c));o&&!f.length&&(d.teardown&&!1!==d.teardown.call(e,p,_.handle)||x.removeEvent(e,h,_.handle),delete u[h])}else for(h in u)x.event.remove(e,h+t[l],n,r,!0);x.isEmptyObject(u)&&K.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,a,o,s=new Array(arguments.length),u=x.event.fix(e),l=(K.get(this,"events")||Object.create(null))[u.type]||[],c=x.event.special[u.type]||{};for(s[0]=u,t=1;t=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(a=[],o={},n=0;n-1:x.find(i,this,null,[l]).length),o[i]&&a.push(r);a.length&&s.push({elem:l,handlers:a})}return l=this,u\s*$/g;function Oe(e,t){return S(e,"table")&&S(11!==t.nodeType?t:t.firstChild,"tr")&&x(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Ie(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function He(e,t){var n,r,i,a,o,s;if(1===t.nodeType){if(K.hasData(e)&&(s=K.get(e).events))for(i in K.remove(t,"handle events"),s)for(n=0,r=s[i].length;n1&&"string"==typeof p&&!_.checkClone&&Ee.test(p))return e.each((function(i){var a=e.eq(i);m&&(t[0]=p.call(this,i,a.html())),Fe(a,t,n,r)}));if(f&&(a=(i=Ae(t,e[0].ownerDocument,!1,e,r)).firstChild,1===i.childNodes.length&&(i=a),a||r)){for(s=(o=x.map(ye(i,"script"),Pe)).length;d0&&be(o,!u&&ye(e,"script")),s},cleanData:function(e){for(var t,n,r,i=x.event.special,a=0;void 0!==(n=e[a]);a++)if(J(n)){if(t=n[K.expando]){if(t.events)for(r in t.events)i[r]?x.event.remove(n,r):x.removeEvent(n,r,t.handle);n[K.expando]=void 0}n[Z.expando]&&(n[Z.expando]=void 0)}}}),x.fn.extend({detach:function(e){return Be(this,e,!0)},remove:function(e){return Be(this,e)},text:function(e){return W(this,(function(e){return void 0===e?x.text(this):this.empty().each((function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)}))}),null,e,arguments.length)},append:function(){return Fe(this,arguments,(function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Oe(this,e).appendChild(e)}))},prepend:function(){return Fe(this,arguments,(function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Oe(this,e);t.insertBefore(e,t.firstChild)}}))},before:function(){return Fe(this,arguments,(function(e){this.parentNode&&this.parentNode.insertBefore(e,this)}))},after:function(){return Fe(this,arguments,(function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)}))},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(x.cleanData(ye(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map((function(){return x.clone(this,e,t)}))},html:function(e){return W(this,(function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ce.test(e)&&!ve[(_e.exec(e)||["",""])[1].toLowerCase()]){e=x.htmlPrefilter(e);try{for(;n3,ae.removeChild(e)),s}}))}();var qe=["Webkit","Moz","ms"],Ge=y.createElement("div").style,Je={};function Qe(e){var t=x.cssProps[e]||Je[e];return t||(e in Ge?e:Je[e]=function(e){for(var t=e[0].toUpperCase()+e.slice(1),n=qe.length;n--;)if((e=qe[n]+t)in Ge)return e}(e)||e)}var Ke=/^(none|table(?!-c[ea]).+)/,Ze=/^--/,Xe={position:"absolute",visibility:"hidden",display:"block"},et={letterSpacing:"0",fontWeight:"400"};function tt(e,t,n){var r=re.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function nt(e,t,n,r,i,a){var o="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;o<4;o+=2)"margin"===n&&(u+=x.css(e,n+ie[o],!0,i)),r?("content"===n&&(u-=x.css(e,"padding"+ie[o],!0,i)),"margin"!==n&&(u-=x.css(e,"border"+ie[o]+"Width",!0,i))):(u+=x.css(e,"padding"+ie[o],!0,i),"padding"!==n?u+=x.css(e,"border"+ie[o]+"Width",!0,i):s+=x.css(e,"border"+ie[o]+"Width",!0,i));return!r&&a>=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-a-u-s-.5))||0),u}function rt(e,t,n){var r=$e(e),i=(!_.boxSizingReliable()||n)&&"border-box"===x.css(e,"boxSizing",!1,r),a=i,o=Ve(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(ze.test(o)){if(!n)return o;o="auto"}return(!_.boxSizingReliable()&&i||!_.reliableTrDimensions()&&S(e,"tr")||"auto"===o||!parseFloat(o)&&"inline"===x.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===x.css(e,"boxSizing",!1,r),(a=s in e)&&(o=e[s])),(o=parseFloat(o)||0)+nt(e,t,n||(i?"border":"content"),a,r,o)+"px"}function it(e,t,n,r,i){return new it.prototype.init(e,t,n,r,i)}x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Ve(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,a,o,s=G(t),u=Ze.test(t),l=e.style;if(u||(t=Qe(s)),o=x.cssHooks[t]||x.cssHooks[s],void 0===n)return o&&"get"in o&&void 0!==(i=o.get(e,!1,r))?i:l[t];"string"===(a=typeof n)&&(i=re.exec(n))&&i[1]&&(n=le(e,t,i),a="number"),null!=n&&n==n&&("number"!==a||u||(n+=i&&i[3]||(x.cssNumber[s]?"":"px")),_.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),o&&"set"in o&&void 0===(n=o.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,a,o,s=G(t);return Ze.test(t)||(t=Qe(s)),(o=x.cssHooks[t]||x.cssHooks[s])&&"get"in o&&(i=o.get(e,!0,n)),void 0===i&&(i=Ve(e,t,r)),"normal"===i&&t in et&&(i=et[t]),""===n||n?(a=parseFloat(i),!0===n||isFinite(a)?a||0:i):i}}),x.each(["height","width"],(function(e,t){x.cssHooks[t]={get:function(e,n,r){if(n)return!Ke.test(x.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?rt(e,t,r):Re(e,Xe,(function(){return rt(e,t,r)}))},set:function(e,n,r){var i,a=$e(e),o=!_.scrollboxSize()&&"absolute"===a.position,s=(o||r)&&"border-box"===x.css(e,"boxSizing",!1,a),u=r?nt(e,t,r,s,a):0;return s&&o&&(u-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(a[t])-nt(e,t,"border",!1,a)-.5)),u&&(i=re.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=x.css(e,t)),tt(0,n,u)}}})),x.cssHooks.marginLeft=Ue(_.reliableMarginLeft,(function(e,t){if(t)return(parseFloat(Ve(e,"marginLeft"))||e.getBoundingClientRect().left-Re(e,{marginLeft:0},(function(){return e.getBoundingClientRect().left})))+"px"})),x.each({margin:"",padding:"",border:"Width"},(function(e,t){x.cssHooks[e+t]={expand:function(n){for(var r=0,i={},a="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+ie[r]+t]=a[r]||a[r-2]||a[0];return i}},"margin"!==e&&(x.cssHooks[e+t].set=tt)})),x.fn.extend({css:function(e,t){return W(this,(function(e,t,n){var r,i,a={},o=0;if(Array.isArray(t)){for(r=$e(e),i=t.length;o1)}}),x.Tween=it,it.prototype={constructor:it,init:function(e,t,n,r,i,a){this.elem=e,this.prop=n,this.easing=i||x.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=a||(x.cssNumber[n]?"":"px")},cur:function(){var e=it.propHooks[this.prop];return e&&e.get?e.get(this):it.propHooks._default.get(this)},run:function(e){var t,n=it.propHooks[this.prop];return this.options.duration?this.pos=t=x.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):it.propHooks._default.set(this),this}},it.prototype.init.prototype=it.prototype,it.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=x.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){x.fx.step[e.prop]?x.fx.step[e.prop](e):1!==e.elem.nodeType||!x.cssHooks[e.prop]&&null==e.elem.style[Qe(e.prop)]?e.elem[e.prop]=e.now:x.style(e.elem,e.prop,e.now+e.unit)}}},it.propHooks.scrollTop=it.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},x.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},x.fx=it.prototype.init,x.fx.step={};var at,ot,st=/^(?:toggle|show|hide)$/,ut=/queueHooks$/;function lt(){ot&&(!1===y.hidden&&n.requestAnimationFrame?n.requestAnimationFrame(lt):n.setTimeout(lt,x.fx.interval),x.fx.tick())}function ct(){return n.setTimeout((function(){at=void 0})),at=Date.now()}function dt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=ie[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function ft(e,t,n){for(var r,i=(ht.tweeners[t]||[]).concat(ht.tweeners["*"]),a=0,o=i.length;a1)},removeAttr:function(e){return this.each((function(){x.removeAttr(this,e)}))}}),x.extend({attr:function(e,t,n){var r,i,a=e.nodeType;if(3!==a&&8!==a&&2!==a)return void 0===e.getAttribute?x.prop(e,t,n):(1===a&&x.isXMLDoc(e)||(i=x.attrHooks[t.toLowerCase()]||(x.expr.match.bool.test(t)?pt:void 0)),void 0!==n?null===n?void x.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=x.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!_.radioValue&&"radio"===t&&S(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(H);if(i&&1===e.nodeType)for(;n=i[r++];)e.removeAttribute(n)}}),pt={set:function(e,t,n){return!1===t?x.removeAttr(e,n):e.setAttribute(n,n),n}},x.each(x.expr.match.bool.source.match(/\w+/g),(function(e,t){var n=mt[t]||x.find.attr;mt[t]=function(e,t,r){var i,a,o=t.toLowerCase();return r||(a=mt[o],mt[o]=i,i=null!=n(e,t,r)?o:null,mt[o]=a),i}}));var _t=/^(?:input|select|textarea|button)$/i,gt=/^(?:a|area)$/i;function vt(e){return(e.match(H)||[]).join(" ")}function yt(e){return e.getAttribute&&e.getAttribute("class")||""}function bt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(H)||[]}x.fn.extend({prop:function(e,t){return W(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each((function(){delete this[x.propFix[e]||e]}))}}),x.extend({prop:function(e,t,n){var r,i,a=e.nodeType;if(3!==a&&8!==a&&2!==a)return 1===a&&x.isXMLDoc(e)||(t=x.propFix[t]||t,i=x.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):_t.test(e.nodeName)||gt.test(e.nodeName)&&e.href?0:-1}}},propFix:{for:"htmlFor",class:"className"}}),_.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],(function(){x.propFix[this.toLowerCase()]=this})),x.fn.extend({addClass:function(e){var t,n,r,i,a,o,s,u=0;if(g(e))return this.each((function(t){x(this).addClass(e.call(this,t,yt(this)))}));if((t=bt(e)).length)for(;n=this[u++];)if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){for(o=0;a=t[o++];)r.indexOf(" "+a+" ")<0&&(r+=a+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,a,o,s,u=0;if(g(e))return this.each((function(t){x(this).removeClass(e.call(this,t,yt(this)))}));if(!arguments.length)return this.attr("class","");if((t=bt(e)).length)for(;n=this[u++];)if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){for(o=0;a=t[o++];)for(;r.indexOf(" "+a+" ")>-1;)r=r.replace(" "+a+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each((function(n){x(this).toggleClass(e.call(this,n,yt(this),t),t)})):this.each((function(){var t,i,a,o;if(r)for(i=0,a=x(this),o=bt(e);t=o[i++];)a.hasClass(t)?a.removeClass(t):a.addClass(t);else void 0!==e&&"boolean"!==n||((t=yt(this))&&K.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":K.get(this,"__className__")||""))}))},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+vt(yt(n))+" ").indexOf(t)>-1)return!0;return!1}});var wt=/\r/g;x.fn.extend({val:function(e){var t,n,r,i=this[0];return arguments.length?(r=g(e),this.each((function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,x(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=x.map(i,(function(e){return null==e?"":e+""}))),(t=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))}))):i?(t=x.valHooks[i.type]||x.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(wt,""):null==n?"":n:void 0}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:vt(x.text(e))}},select:{get:function(e){var t,n,r,i=e.options,a=e.selectedIndex,o="select-one"===e.type,s=o?null:[],u=o?a+1:i.length;for(r=a<0?u:o?a:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),a}}}}),x.each(["radio","checkbox"],(function(){x.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=x.inArray(x(e).val(),t)>-1}},_.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})})),_.focusin="onfocusin"in n;var At=/^(?:focusinfocus|focusoutblur)$/,xt=function(e){e.stopPropagation()};x.extend(x.event,{trigger:function(e,t,r,i){var a,o,s,u,l,c,d,f,p=[r||y],m=h.call(e,"type")?e.type:e,_=h.call(e,"namespace")?e.namespace.split("."):[];if(o=f=s=r=r||y,3!==r.nodeType&&8!==r.nodeType&&!At.test(m+x.event.triggered)&&(m.indexOf(".")>-1&&(_=m.split("."),m=_.shift(),_.sort()),l=m.indexOf(":")<0&&"on"+m,(e=e[x.expando]?e:new x.Event(m,"object"==typeof e&&e)).isTrigger=i?2:3,e.namespace=_.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+_.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=r),t=null==t?[e]:x.makeArray(t,[e]),d=x.event.special[m]||{},i||!d.trigger||!1!==d.trigger.apply(r,t))){if(!i&&!d.noBubble&&!v(r)){for(u=d.delegateType||m,At.test(u+m)||(o=o.parentNode);o;o=o.parentNode)p.push(o),s=o;s===(r.ownerDocument||y)&&p.push(s.defaultView||s.parentWindow||n)}for(a=0;(o=p[a++])&&!e.isPropagationStopped();)f=o,e.type=a>1?u:d.bindType||m,(c=(K.get(o,"events")||Object.create(null))[e.type]&&K.get(o,"handle"))&&c.apply(o,t),(c=l&&o[l])&&c.apply&&J(o)&&(e.result=c.apply(o,t),!1===e.result&&e.preventDefault());return e.type=m,i||e.isDefaultPrevented()||d._default&&!1!==d._default.apply(p.pop(),t)||!J(r)||l&&g(r[m])&&!v(r)&&((s=r[l])&&(r[l]=null),x.event.triggered=m,e.isPropagationStopped()&&f.addEventListener(m,xt),r[m](),e.isPropagationStopped()&&f.removeEventListener(m,xt),x.event.triggered=void 0,s&&(r[l]=s)),e.result}},simulate:function(e,t,n){var r=x.extend(new x.Event,n,{type:e,isSimulated:!0});x.event.trigger(r,null,t)}}),x.fn.extend({trigger:function(e,t){return this.each((function(){x.event.trigger(e,t,this)}))},triggerHandler:function(e,t){var n=this[0];if(n)return x.event.trigger(e,t,n,!0)}}),_.focusin||x.each({focus:"focusin",blur:"focusout"},(function(e,t){var n=function(e){x.event.simulate(t,e.target,x.event.fix(e))};x.event.special[t]={setup:function(){var r=this.ownerDocument||this.document||this,i=K.access(r,t);i||r.addEventListener(e,n,!0),K.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this.document||this,i=K.access(r,t)-1;i?K.access(r,t,i):(r.removeEventListener(e,n,!0),K.remove(r,t))}}}));var Mt=n.location,kt={guid:Date.now()},Dt=/\?/;x.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new n.DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+e),t};var Lt=/\[\]$/,Tt=/\r?\n/g,St=/^(?:submit|button|image|reset|file)$/i,Yt=/^(?:input|select|textarea|keygen)/i;function Ct(e,t,n,r){var i;if(Array.isArray(t))x.each(t,(function(t,i){n||Lt.test(e)?r(e,i):Ct(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)}));else if(n||"object"!==A(t))r(e,t);else for(i in t)Ct(e+"["+i+"]",t[i],n,r)}x.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!x.isPlainObject(e))x.each(e,(function(){i(this.name,this.value)}));else for(n in e)Ct(n,e[n],t,i);return r.join("&")},x.fn.extend({serialize:function(){return x.param(this.serializeArray())},serializeArray:function(){return this.map((function(){var e=x.prop(this,"elements");return e?x.makeArray(e):this})).filter((function(){var e=this.type;return this.name&&!x(this).is(":disabled")&&Yt.test(this.nodeName)&&!St.test(e)&&(this.checked||!me.test(e))})).map((function(e,t){var n=x(this).val();return null==n?null:Array.isArray(n)?x.map(n,(function(e){return{name:t.name,value:e.replace(Tt,"\r\n")}})):{name:t.name,value:n.replace(Tt,"\r\n")}})).get()}});var Et=/%20/g,jt=/#.*$/,Ot=/([?&])_=[^&]*/,Pt=/^(.*?):[ \t]*([^\r\n]*)$/gm,It=/^(?:GET|HEAD)$/,Ht=/^\/\//,Nt={},Ft={},Bt="*/".concat("*"),zt=y.createElement("a");function $t(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,a=t.toLowerCase().match(H)||[];if(g(n))for(;r=a[i++];)"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function Rt(e,t,n,r){var i={},a=e===Ft;function o(s){var u;return i[s]=!0,x.each(e[s]||[],(function(e,s){var l=s(t,n,r);return"string"!=typeof l||a||i[l]?a?!(u=l):void 0:(t.dataTypes.unshift(l),o(l),!1)})),u}return o(t.dataTypes[0])||!i["*"]&&o("*")}function Wt(e,t){var n,r,i=x.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&x.extend(!0,e,r),e}zt.href=Mt.href,x.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Mt.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Mt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Bt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":x.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Wt(Wt(e,x.ajaxSettings),t):Wt(x.ajaxSettings,e)},ajaxPrefilter:$t(Nt),ajaxTransport:$t(Ft),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var r,i,a,o,s,u,l,c,d,f,h=x.ajaxSetup({},t),p=h.context||h,m=h.context&&(p.nodeType||p.jquery)?x(p):x.event,_=x.Deferred(),g=x.Callbacks("once memory"),v=h.statusCode||{},b={},w={},A="canceled",M={readyState:0,getResponseHeader:function(e){var t;if(l){if(!o)for(o={};t=Pt.exec(a);)o[t[1].toLowerCase()+" "]=(o[t[1].toLowerCase()+" "]||[]).concat(t[2]);t=o[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return l?a:null},setRequestHeader:function(e,t){return null==l&&(e=w[e.toLowerCase()]=w[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==l&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(l)M.always(e[M.status]);else for(t in e)v[t]=[v[t],e[t]];return this},abort:function(e){var t=e||A;return r&&r.abort(t),k(0,t),this}};if(_.promise(M),h.url=((e||h.url||Mt.href)+"").replace(Ht,Mt.protocol+"//"),h.type=t.method||t.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(H)||[""],null==h.crossDomain){u=y.createElement("a");try{u.href=h.url,u.href=u.href,h.crossDomain=zt.protocol+"//"+zt.host!=u.protocol+"//"+u.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=x.param(h.data,h.traditional)),Rt(Nt,h,t,M),l)return M;for(d in(c=x.event&&h.global)&&0==x.active++&&x.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!It.test(h.type),i=h.url.replace(jt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(Et,"+")):(f=h.url.slice(i.length),h.data&&(h.processData||"string"==typeof h.data)&&(i+=(Dt.test(i)?"&":"?")+h.data,delete h.data),!1===h.cache&&(i=i.replace(Ot,"$1"),f=(Dt.test(i)?"&":"?")+"_="+kt.guid+++f),h.url=i+f),h.ifModified&&(x.lastModified[i]&&M.setRequestHeader("If-Modified-Since",x.lastModified[i]),x.etag[i]&&M.setRequestHeader("If-None-Match",x.etag[i])),(h.data&&h.hasContent&&!1!==h.contentType||t.contentType)&&M.setRequestHeader("Content-Type",h.contentType),M.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+Bt+"; q=0.01":""):h.accepts["*"]),h.headers)M.setRequestHeader(d,h.headers[d]);if(h.beforeSend&&(!1===h.beforeSend.call(p,M,h)||l))return M.abort();if(A="abort",g.add(h.complete),M.done(h.success),M.fail(h.error),r=Rt(Ft,h,t,M)){if(M.readyState=1,c&&m.trigger("ajaxSend",[M,h]),l)return M;h.async&&h.timeout>0&&(s=n.setTimeout((function(){M.abort("timeout")}),h.timeout));try{l=!1,r.send(b,k)}catch(e){if(l)throw e;k(-1,e)}}else k(-1,"No Transport");function k(e,t,o,u){var d,f,y,b,w,A=t;l||(l=!0,s&&n.clearTimeout(s),r=void 0,a=u||"",M.readyState=e>0?4:0,d=e>=200&&e<300||304===e,o&&(b=function(e,t,n){for(var r,i,a,o,s=e.contents,u=e.dataTypes;"*"===u[0];)u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)a=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){a=i;break}o||(o=i)}a=a||o}if(a)return a!==u[0]&&u.unshift(a),n[a]}(h,M,o)),!d&&x.inArray("script",h.dataTypes)>-1&&(h.converters["text script"]=function(){}),b=function(e,t,n,r){var i,a,o,s,u,l={},c=e.dataTypes.slice();if(c[1])for(o in e.converters)l[o.toLowerCase()]=e.converters[o];for(a=c.shift();a;)if(e.responseFields[a]&&(n[e.responseFields[a]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=a,a=c.shift())if("*"===a)a=u;else if("*"!==u&&u!==a){if(!(o=l[u+" "+a]||l["* "+a]))for(i in l)if((s=i.split(" "))[1]===a&&(o=l[u+" "+s[0]]||l["* "+s[0]])){!0===o?o=l[i]:!0!==l[i]&&(a=s[0],c.unshift(s[1]));break}if(!0!==o)if(o&&e.throws)t=o(t);else try{t=o(t)}catch(e){return{state:"parsererror",error:o?e:"No conversion from "+u+" to "+a}}}return{state:"success",data:t}}(h,b,M,d),d?(h.ifModified&&((w=M.getResponseHeader("Last-Modified"))&&(x.lastModified[i]=w),(w=M.getResponseHeader("etag"))&&(x.etag[i]=w)),204===e||"HEAD"===h.type?A="nocontent":304===e?A="notmodified":(A=b.state,f=b.data,d=!(y=b.error))):(y=A,!e&&A||(A="error",e<0&&(e=0))),M.status=e,M.statusText=(t||A)+"",d?_.resolveWith(p,[f,A,M]):_.rejectWith(p,[M,A,y]),M.statusCode(v),v=void 0,c&&m.trigger(d?"ajaxSuccess":"ajaxError",[M,h,d?f:y]),g.fireWith(p,[M,A]),c&&(m.trigger("ajaxComplete",[M,h]),--x.active||x.event.trigger("ajaxStop")))}return M},getJSON:function(e,t,n){return x.get(e,t,n,"json")},getScript:function(e,t){return x.get(e,void 0,t,"script")}}),x.each(["get","post"],(function(e,t){x[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),x.ajax(x.extend({url:e,type:t,dataType:i,data:n,success:r},x.isPlainObject(e)&&e))}})),x.ajaxPrefilter((function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")})),x._evalUrl=function(e,t,n){return x.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){x.globalEval(e,t,n)}})},x.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=x(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map((function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e})).append(this)),this},wrapInner:function(e){return g(e)?this.each((function(t){x(this).wrapInner(e.call(this,t))})):this.each((function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)}))},wrap:function(e){var t=g(e);return this.each((function(n){x(this).wrapAll(t?e.call(this,n):e)}))},unwrap:function(e){return this.parent(e).not("body").each((function(){x(this).replaceWith(this.childNodes)})),this}}),x.expr.pseudos.hidden=function(e){return!x.expr.pseudos.visible(e)},x.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},x.ajaxSettings.xhr=function(){try{return new n.XMLHttpRequest}catch(e){}};var Vt={0:200,1223:204},Ut=x.ajaxSettings.xhr();_.cors=!!Ut&&"withCredentials"in Ut,_.ajax=Ut=!!Ut,x.ajaxTransport((function(e){var t,r;if(_.cors||Ut&&!e.crossDomain)return{send:function(i,a){var o,s=e.xhr();if(s.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for(o in e.xhrFields)s[o]=e.xhrFields[o];for(o in e.mimeType&&s.overrideMimeType&&s.overrideMimeType(e.mimeType),e.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest"),i)s.setRequestHeader(o,i[o]);t=function(e){return function(){t&&(t=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?a(0,"error"):a(s.status,s.statusText):a(Vt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=t(),r=s.onerror=s.ontimeout=t("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&n.setTimeout((function(){t&&r()}))},t=t("abort");try{s.send(e.hasContent&&e.data||null)}catch(e){if(t)throw e}},abort:function(){t&&t()}}})),x.ajaxPrefilter((function(e){e.crossDomain&&(e.contents.script=!1)})),x.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return x.globalEval(e),e}}}),x.ajaxPrefilter("script",(function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")})),x.ajaxTransport("script",(function(e){var t,n;if(e.crossDomain||e.scriptAttrs)return{send:function(r,i){t=x("\n\n\n","import { render, staticRenderFns } from \"./VueTypeaheadBootstrapListItem.vue?vue&type=template&id=5bf8ce45&scoped=true&\"\nimport script from \"./VueTypeaheadBootstrapListItem.vue?vue&type=script&lang=js&\"\nexport * from \"./VueTypeaheadBootstrapListItem.vue?vue&type=script&lang=js&\"\nimport style0 from \"./VueTypeaheadBootstrapListItem.vue?vue&type=style&index=0&id=5bf8ce45&scoped=true&lang=css&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"5bf8ce45\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('a',{class:_vm.textClasses,attrs:{\"tabindex\":\"0\",\"href\":\"#\"},on:{\"keydown\":[function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,\"tab\",9,$event.key,\"Tab\")){ return null; }return _vm.$emit('listItemBlur')},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,\"esc\",27,$event.key,[\"Esc\",\"Escape\"])){ return null; }$event.stopPropagation();$event.preventDefault();return _vm.$emit('listItemBlur')},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,\"down\",40,$event.key,[\"Down\",\"ArrowDown\"])){ return null; }$event.preventDefault();},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,\"up\",38,$event.key,[\"Up\",\"ArrowUp\"])){ return null; }$event.preventDefault();}],\"keyup\":[function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,\"down\",40,$event.key,[\"Down\",\"ArrowDown\"])){ return null; }return _vm.$parent.selectNextListItem($event)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,\"up\",38,$event.key,[\"Up\",\"ArrowUp\"])){ return null; }return _vm.$parent.selectPreviousListItem($event)}]}},[_c('div',{staticClass:\"sr-only\"},[_vm._v(_vm._s(_vm.screenReaderText))]),_vm._v(\" \"),_c('div',{attrs:{\"aria-hidden\":\"true\"}},[_vm._t(\"suggestion\",[_c('span',{domProps:{\"innerHTML\":_vm._s(_vm.htmlText)}})],null,{ data: _vm.data, htmlText: _vm.htmlText })],2)])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n","import mod from \"-!../../../vue-loader/lib/index.js??vue-loader-options!./VueTypeaheadBootstrapList.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../vue-loader/lib/index.js??vue-loader-options!./VueTypeaheadBootstrapList.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./VueTypeaheadBootstrapList.vue?vue&type=template&id=5143ced6&\"\nimport script from \"./VueTypeaheadBootstrapList.vue?vue&type=script&lang=js&\"\nexport * from \"./VueTypeaheadBootstrapList.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{ref:\"suggestionList\",staticClass:\"list-group shadow\"},_vm._l((_vm.matchedItems),function(item,id){return _c('vue-typeahead-bootstrap-list-item',_vm._g({key:id,attrs:{\"active\":_vm.isListItemActive(id),\"id\":(_vm.isListItemActive(id)) ? (\"selected-option-\" + _vm.vbtUniqueId) : false,\"data\":item.data,\"html-text\":_vm.highlight(item.text),\"role\":\"option\",\"aria-selected\":(_vm.isListItemActive(id)) ? 'true' : 'false',\"screen-reader-text\":item.text,\"disabled\":_vm.isDisabledItem(item),\"background-variant\":_vm.backgroundVariant,\"background-variant-resolver\":_vm.backgroundVariantResolver,\"text-variant\":_vm.textVariant},nativeOn:{\"click\":function($event){return _vm.handleHit(item, $event)}},scopedSlots:_vm._u([{key:\"suggestion\",fn:function(ref){\nvar data = ref.data;\nvar htmlText = ref.htmlText;\nreturn (_vm.$scopedSlots.suggestion)?[_vm._t(\"suggestion\",null,null,{ data: data, htmlText: htmlText })]:undefined}}],null,true)},_vm.$listeners))}),1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../vue-loader/lib/index.js??vue-loader-options!./VueTypeaheadBootstrap.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../vue-loader/lib/index.js??vue-loader-options!./VueTypeaheadBootstrap.vue?vue&type=script&lang=js&\"","\n\n\n\n\n","import { render, staticRenderFns } from \"./VueTypeaheadBootstrap.vue?vue&type=template&id=dbe69e32&scoped=true&\"\nimport script from \"./VueTypeaheadBootstrap.vue?vue&type=script&lang=js&\"\nexport * from \"./VueTypeaheadBootstrap.vue?vue&type=script&lang=js&\"\nimport style0 from \"./VueTypeaheadBootstrap.vue?vue&type=style&index=0&id=dbe69e32&scoped=true&lang=css&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"dbe69e32\",\n null\n \n)\n\nexport default component.exports","/*!\n * jQuery JavaScript Library v3.5.1\n * https://jquery.com/\n *\n * Includes Sizzle.js\n * https://sizzlejs.com/\n *\n * Copyright JS Foundation and other contributors\n * Released under the MIT license\n * https://jquery.org/license\n *\n * Date: 2020-05-04T22:49Z\n */\n( function( global, factory ) {\n\n\t\"use strict\";\n\n\tif ( typeof module === \"object\" && typeof module.exports === \"object\" ) {\n\n\t\t// For CommonJS and CommonJS-like environments where a proper `window`\n\t\t// is present, execute the factory and get jQuery.\n\t\t// For environments that do not have a `window` with a `document`\n\t\t// (such as Node.js), expose a factory as module.exports.\n\t\t// This accentuates the need for the creation of a real `window`.\n\t\t// e.g. var jQuery = require(\"jquery\")(window);\n\t\t// See ticket #14549 for more info.\n\t\tmodule.exports = global.document ?\n\t\t\tfactory( global, true ) :\n\t\t\tfunction( w ) {\n\t\t\t\tif ( !w.document ) {\n\t\t\t\t\tthrow new Error( \"jQuery requires a window with a document\" );\n\t\t\t\t}\n\t\t\t\treturn factory( w );\n\t\t\t};\n\t} else {\n\t\tfactory( global );\n\t}\n\n// Pass this if window is not defined yet\n} )( typeof window !== \"undefined\" ? window : this, function( window, noGlobal ) {\n\n// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1\n// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode\n// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common\n// enough that all such attempts are guarded in a try block.\n\"use strict\";\n\nvar arr = [];\n\nvar getProto = Object.getPrototypeOf;\n\nvar slice = arr.slice;\n\nvar flat = arr.flat ? function( array ) {\n\treturn arr.flat.call( array );\n} : function( array ) {\n\treturn arr.concat.apply( [], array );\n};\n\n\nvar push = arr.push;\n\nvar indexOf = arr.indexOf;\n\nvar class2type = {};\n\nvar toString = class2type.toString;\n\nvar hasOwn = class2type.hasOwnProperty;\n\nvar fnToString = hasOwn.toString;\n\nvar ObjectFunctionString = fnToString.call( Object );\n\nvar support = {};\n\nvar isFunction = function isFunction( obj ) {\n\n // Support: Chrome <=57, Firefox <=52\n // In some browsers, typeof returns \"function\" for HTML elements\n // (i.e., `typeof document.createElement( \"object\" ) === \"function\"`).\n // We don't want to classify *any* DOM node as a function.\n return typeof obj === \"function\" && typeof obj.nodeType !== \"number\";\n };\n\n\nvar isWindow = function isWindow( obj ) {\n\t\treturn obj != null && obj === obj.window;\n\t};\n\n\nvar document = window.document;\n\n\n\n\tvar preservedScriptAttributes = {\n\t\ttype: true,\n\t\tsrc: true,\n\t\tnonce: true,\n\t\tnoModule: true\n\t};\n\n\tfunction DOMEval( code, node, doc ) {\n\t\tdoc = doc || document;\n\n\t\tvar i, val,\n\t\t\tscript = doc.createElement( \"script\" );\n\n\t\tscript.text = code;\n\t\tif ( node ) {\n\t\t\tfor ( i in preservedScriptAttributes ) {\n\n\t\t\t\t// Support: Firefox 64+, Edge 18+\n\t\t\t\t// Some browsers don't support the \"nonce\" property on scripts.\n\t\t\t\t// On the other hand, just using `getAttribute` is not enough as\n\t\t\t\t// the `nonce` attribute is reset to an empty string whenever it\n\t\t\t\t// becomes browsing-context connected.\n\t\t\t\t// See https://github.com/whatwg/html/issues/2369\n\t\t\t\t// See https://html.spec.whatwg.org/#nonce-attributes\n\t\t\t\t// The `node.getAttribute` check was added for the sake of\n\t\t\t\t// `jQuery.globalEval` so that it can fake a nonce-containing node\n\t\t\t\t// via an object.\n\t\t\t\tval = node[ i ] || node.getAttribute && node.getAttribute( i );\n\t\t\t\tif ( val ) {\n\t\t\t\t\tscript.setAttribute( i, val );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tdoc.head.appendChild( script ).parentNode.removeChild( script );\n\t}\n\n\nfunction toType( obj ) {\n\tif ( obj == null ) {\n\t\treturn obj + \"\";\n\t}\n\n\t// Support: Android <=2.3 only (functionish RegExp)\n\treturn typeof obj === \"object\" || typeof obj === \"function\" ?\n\t\tclass2type[ toString.call( obj ) ] || \"object\" :\n\t\ttypeof obj;\n}\n/* global Symbol */\n// Defining this global in .eslintrc.json would create a danger of using the global\n// unguarded in another place, it seems safer to define global only for this module\n\n\n\nvar\n\tversion = \"3.5.1\",\n\n\t// Define a local copy of jQuery\n\tjQuery = function( selector, context ) {\n\n\t\t// The jQuery object is actually just the init constructor 'enhanced'\n\t\t// Need init if jQuery is called (just allow error to be thrown if not included)\n\t\treturn new jQuery.fn.init( selector, context );\n\t};\n\njQuery.fn = jQuery.prototype = {\n\n\t// The current version of jQuery being used\n\tjquery: version,\n\n\tconstructor: jQuery,\n\n\t// The default length of a jQuery object is 0\n\tlength: 0,\n\n\ttoArray: function() {\n\t\treturn slice.call( this );\n\t},\n\n\t// Get the Nth element in the matched element set OR\n\t// Get the whole matched element set as a clean array\n\tget: function( num ) {\n\n\t\t// Return all the elements in a clean array\n\t\tif ( num == null ) {\n\t\t\treturn slice.call( this );\n\t\t}\n\n\t\t// Return just the one element from the set\n\t\treturn num < 0 ? this[ num + this.length ] : this[ num ];\n\t},\n\n\t// Take an array of elements and push it onto the stack\n\t// (returning the new matched element set)\n\tpushStack: function( elems ) {\n\n\t\t// Build a new jQuery matched element set\n\t\tvar ret = jQuery.merge( this.constructor(), elems );\n\n\t\t// Add the old object onto the stack (as a reference)\n\t\tret.prevObject = this;\n\n\t\t// Return the newly-formed element set\n\t\treturn ret;\n\t},\n\n\t// Execute a callback for every element in the matched set.\n\teach: function( callback ) {\n\t\treturn jQuery.each( this, callback );\n\t},\n\n\tmap: function( callback ) {\n\t\treturn this.pushStack( jQuery.map( this, function( elem, i ) {\n\t\t\treturn callback.call( elem, i, elem );\n\t\t} ) );\n\t},\n\n\tslice: function() {\n\t\treturn this.pushStack( slice.apply( this, arguments ) );\n\t},\n\n\tfirst: function() {\n\t\treturn this.eq( 0 );\n\t},\n\n\tlast: function() {\n\t\treturn this.eq( -1 );\n\t},\n\n\teven: function() {\n\t\treturn this.pushStack( jQuery.grep( this, function( _elem, i ) {\n\t\t\treturn ( i + 1 ) % 2;\n\t\t} ) );\n\t},\n\n\todd: function() {\n\t\treturn this.pushStack( jQuery.grep( this, function( _elem, i ) {\n\t\t\treturn i % 2;\n\t\t} ) );\n\t},\n\n\teq: function( i ) {\n\t\tvar len = this.length,\n\t\t\tj = +i + ( i < 0 ? len : 0 );\n\t\treturn this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );\n\t},\n\n\tend: function() {\n\t\treturn this.prevObject || this.constructor();\n\t},\n\n\t// For internal use only.\n\t// Behaves like an Array's method, not like a jQuery method.\n\tpush: push,\n\tsort: arr.sort,\n\tsplice: arr.splice\n};\n\njQuery.extend = jQuery.fn.extend = function() {\n\tvar options, name, src, copy, copyIsArray, clone,\n\t\ttarget = arguments[ 0 ] || {},\n\t\ti = 1,\n\t\tlength = arguments.length,\n\t\tdeep = false;\n\n\t// Handle a deep copy situation\n\tif ( typeof target === \"boolean\" ) {\n\t\tdeep = target;\n\n\t\t// Skip the boolean and the target\n\t\ttarget = arguments[ i ] || {};\n\t\ti++;\n\t}\n\n\t// Handle case when target is a string or something (possible in deep copy)\n\tif ( typeof target !== \"object\" && !isFunction( target ) ) {\n\t\ttarget = {};\n\t}\n\n\t// Extend jQuery itself if only one argument is passed\n\tif ( i === length ) {\n\t\ttarget = this;\n\t\ti--;\n\t}\n\n\tfor ( ; i < length; i++ ) {\n\n\t\t// Only deal with non-null/undefined values\n\t\tif ( ( options = arguments[ i ] ) != null ) {\n\n\t\t\t// Extend the base object\n\t\t\tfor ( name in options ) {\n\t\t\t\tcopy = options[ name ];\n\n\t\t\t\t// Prevent Object.prototype pollution\n\t\t\t\t// Prevent never-ending loop\n\t\t\t\tif ( name === \"__proto__\" || target === copy ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Recurse if we're merging plain objects or arrays\n\t\t\t\tif ( deep && copy && ( jQuery.isPlainObject( copy ) ||\n\t\t\t\t\t( copyIsArray = Array.isArray( copy ) ) ) ) {\n\t\t\t\t\tsrc = target[ name ];\n\n\t\t\t\t\t// Ensure proper type for the source value\n\t\t\t\t\tif ( copyIsArray && !Array.isArray( src ) ) {\n\t\t\t\t\t\tclone = [];\n\t\t\t\t\t} else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {\n\t\t\t\t\t\tclone = {};\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclone = src;\n\t\t\t\t\t}\n\t\t\t\t\tcopyIsArray = false;\n\n\t\t\t\t\t// Never move original objects, clone them\n\t\t\t\t\ttarget[ name ] = jQuery.extend( deep, clone, copy );\n\n\t\t\t\t// Don't bring in undefined values\n\t\t\t\t} else if ( copy !== undefined ) {\n\t\t\t\t\ttarget[ name ] = copy;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Return the modified object\n\treturn target;\n};\n\njQuery.extend( {\n\n\t// Unique for each copy of jQuery on the page\n\texpando: \"jQuery\" + ( version + Math.random() ).replace( /\\D/g, \"\" ),\n\n\t// Assume jQuery is ready without the ready module\n\tisReady: true,\n\n\terror: function( msg ) {\n\t\tthrow new Error( msg );\n\t},\n\n\tnoop: function() {},\n\n\tisPlainObject: function( obj ) {\n\t\tvar proto, Ctor;\n\n\t\t// Detect obvious negatives\n\t\t// Use toString instead of jQuery.type to catch host objects\n\t\tif ( !obj || toString.call( obj ) !== \"[object Object]\" ) {\n\t\t\treturn false;\n\t\t}\n\n\t\tproto = getProto( obj );\n\n\t\t// Objects with no prototype (e.g., `Object.create( null )`) are plain\n\t\tif ( !proto ) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Objects with prototype are plain iff they were constructed by a global Object function\n\t\tCtor = hasOwn.call( proto, \"constructor\" ) && proto.constructor;\n\t\treturn typeof Ctor === \"function\" && fnToString.call( Ctor ) === ObjectFunctionString;\n\t},\n\n\tisEmptyObject: function( obj ) {\n\t\tvar name;\n\n\t\tfor ( name in obj ) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t},\n\n\t// Evaluates a script in a provided context; falls back to the global one\n\t// if not specified.\n\tglobalEval: function( code, options, doc ) {\n\t\tDOMEval( code, { nonce: options && options.nonce }, doc );\n\t},\n\n\teach: function( obj, callback ) {\n\t\tvar length, i = 0;\n\n\t\tif ( isArrayLike( obj ) ) {\n\t\t\tlength = obj.length;\n\t\t\tfor ( ; i < length; i++ ) {\n\t\t\t\tif ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor ( i in obj ) {\n\t\t\t\tif ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn obj;\n\t},\n\n\t// results is for internal usage only\n\tmakeArray: function( arr, results ) {\n\t\tvar ret = results || [];\n\n\t\tif ( arr != null ) {\n\t\t\tif ( isArrayLike( Object( arr ) ) ) {\n\t\t\t\tjQuery.merge( ret,\n\t\t\t\t\ttypeof arr === \"string\" ?\n\t\t\t\t\t[ arr ] : arr\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tpush.call( ret, arr );\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t},\n\n\tinArray: function( elem, arr, i ) {\n\t\treturn arr == null ? -1 : indexOf.call( arr, elem, i );\n\t},\n\n\t// Support: Android <=4.0 only, PhantomJS 1 only\n\t// push.apply(_, arraylike) throws on ancient WebKit\n\tmerge: function( first, second ) {\n\t\tvar len = +second.length,\n\t\t\tj = 0,\n\t\t\ti = first.length;\n\n\t\tfor ( ; j < len; j++ ) {\n\t\t\tfirst[ i++ ] = second[ j ];\n\t\t}\n\n\t\tfirst.length = i;\n\n\t\treturn first;\n\t},\n\n\tgrep: function( elems, callback, invert ) {\n\t\tvar callbackInverse,\n\t\t\tmatches = [],\n\t\t\ti = 0,\n\t\t\tlength = elems.length,\n\t\t\tcallbackExpect = !invert;\n\n\t\t// Go through the array, only saving the items\n\t\t// that pass the validator function\n\t\tfor ( ; i < length; i++ ) {\n\t\t\tcallbackInverse = !callback( elems[ i ], i );\n\t\t\tif ( callbackInverse !== callbackExpect ) {\n\t\t\t\tmatches.push( elems[ i ] );\n\t\t\t}\n\t\t}\n\n\t\treturn matches;\n\t},\n\n\t// arg is for internal usage only\n\tmap: function( elems, callback, arg ) {\n\t\tvar length, value,\n\t\t\ti = 0,\n\t\t\tret = [];\n\n\t\t// Go through the array, translating each of the items to their new values\n\t\tif ( isArrayLike( elems ) ) {\n\t\t\tlength = elems.length;\n\t\t\tfor ( ; i < length; i++ ) {\n\t\t\t\tvalue = callback( elems[ i ], i, arg );\n\n\t\t\t\tif ( value != null ) {\n\t\t\t\t\tret.push( value );\n\t\t\t\t}\n\t\t\t}\n\n\t\t// Go through every key on the object,\n\t\t} else {\n\t\t\tfor ( i in elems ) {\n\t\t\t\tvalue = callback( elems[ i ], i, arg );\n\n\t\t\t\tif ( value != null ) {\n\t\t\t\t\tret.push( value );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Flatten any nested arrays\n\t\treturn flat( ret );\n\t},\n\n\t// A global GUID counter for objects\n\tguid: 1,\n\n\t// jQuery.support is not used in Core but other projects attach their\n\t// properties to it so it needs to exist.\n\tsupport: support\n} );\n\nif ( typeof Symbol === \"function\" ) {\n\tjQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];\n}\n\n// Populate the class2type map\njQuery.each( \"Boolean Number String Function Array Date RegExp Object Error Symbol\".split( \" \" ),\nfunction( _i, name ) {\n\tclass2type[ \"[object \" + name + \"]\" ] = name.toLowerCase();\n} );\n\nfunction isArrayLike( obj ) {\n\n\t// Support: real iOS 8.2 only (not reproducible in simulator)\n\t// `in` check used to prevent JIT error (gh-2145)\n\t// hasOwn isn't used here due to false negatives\n\t// regarding Nodelist length in IE\n\tvar length = !!obj && \"length\" in obj && obj.length,\n\t\ttype = toType( obj );\n\n\tif ( isFunction( obj ) || isWindow( obj ) ) {\n\t\treturn false;\n\t}\n\n\treturn type === \"array\" || length === 0 ||\n\t\ttypeof length === \"number\" && length > 0 && ( length - 1 ) in obj;\n}\nvar Sizzle =\n/*!\n * Sizzle CSS Selector Engine v2.3.5\n * https://sizzlejs.com/\n *\n * Copyright JS Foundation and other contributors\n * Released under the MIT license\n * https://js.foundation/\n *\n * Date: 2020-03-14\n */\n( function( window ) {\nvar i,\n\tsupport,\n\tExpr,\n\tgetText,\n\tisXML,\n\ttokenize,\n\tcompile,\n\tselect,\n\toutermostContext,\n\tsortInput,\n\thasDuplicate,\n\n\t// Local document vars\n\tsetDocument,\n\tdocument,\n\tdocElem,\n\tdocumentIsHTML,\n\trbuggyQSA,\n\trbuggyMatches,\n\tmatches,\n\tcontains,\n\n\t// Instance-specific data\n\texpando = \"sizzle\" + 1 * new Date(),\n\tpreferredDoc = window.document,\n\tdirruns = 0,\n\tdone = 0,\n\tclassCache = createCache(),\n\ttokenCache = createCache(),\n\tcompilerCache = createCache(),\n\tnonnativeSelectorCache = createCache(),\n\tsortOrder = function( a, b ) {\n\t\tif ( a === b ) {\n\t\t\thasDuplicate = true;\n\t\t}\n\t\treturn 0;\n\t},\n\n\t// Instance methods\n\thasOwn = ( {} ).hasOwnProperty,\n\tarr = [],\n\tpop = arr.pop,\n\tpushNative = arr.push,\n\tpush = arr.push,\n\tslice = arr.slice,\n\n\t// Use a stripped-down indexOf as it's faster than native\n\t// https://jsperf.com/thor-indexof-vs-for/5\n\tindexOf = function( list, elem ) {\n\t\tvar i = 0,\n\t\t\tlen = list.length;\n\t\tfor ( ; i < len; i++ ) {\n\t\t\tif ( list[ i ] === elem ) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t},\n\n\tbooleans = \"checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|\" +\n\t\t\"ismap|loop|multiple|open|readonly|required|scoped\",\n\n\t// Regular expressions\n\n\t// http://www.w3.org/TR/css3-selectors/#whitespace\n\twhitespace = \"[\\\\x20\\\\t\\\\r\\\\n\\\\f]\",\n\n\t// https://www.w3.org/TR/css-syntax-3/#ident-token-diagram\n\tidentifier = \"(?:\\\\\\\\[\\\\da-fA-F]{1,6}\" + whitespace +\n\t\t\"?|\\\\\\\\[^\\\\r\\\\n\\\\f]|[\\\\w-]|[^\\0-\\\\x7f])+\",\n\n\t// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors\n\tattributes = \"\\\\[\" + whitespace + \"*(\" + identifier + \")(?:\" + whitespace +\n\n\t\t// Operator (capture 2)\n\t\t\"*([*^$|!~]?=)\" + whitespace +\n\n\t\t// \"Attribute values must be CSS identifiers [capture 5]\n\t\t// or strings [capture 3 or capture 4]\"\n\t\t\"*(?:'((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\"|(\" + identifier + \"))|)\" +\n\t\twhitespace + \"*\\\\]\",\n\n\tpseudos = \":(\" + identifier + \")(?:\\\\((\" +\n\n\t\t// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:\n\t\t// 1. quoted (capture 3; capture 4 or capture 5)\n\t\t\"('((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\")|\" +\n\n\t\t// 2. simple (capture 6)\n\t\t\"((?:\\\\\\\\.|[^\\\\\\\\()[\\\\]]|\" + attributes + \")*)|\" +\n\n\t\t// 3. anything else (capture 2)\n\t\t\".*\" +\n\t\t\")\\\\)|)\",\n\n\t// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter\n\trwhitespace = new RegExp( whitespace + \"+\", \"g\" ),\n\trtrim = new RegExp( \"^\" + whitespace + \"+|((?:^|[^\\\\\\\\])(?:\\\\\\\\.)*)\" +\n\t\twhitespace + \"+$\", \"g\" ),\n\n\trcomma = new RegExp( \"^\" + whitespace + \"*,\" + whitespace + \"*\" ),\n\trcombinators = new RegExp( \"^\" + whitespace + \"*([>+~]|\" + whitespace + \")\" + whitespace +\n\t\t\"*\" ),\n\trdescend = new RegExp( whitespace + \"|>\" ),\n\n\trpseudo = new RegExp( pseudos ),\n\tridentifier = new RegExp( \"^\" + identifier + \"$\" ),\n\n\tmatchExpr = {\n\t\t\"ID\": new RegExp( \"^#(\" + identifier + \")\" ),\n\t\t\"CLASS\": new RegExp( \"^\\\\.(\" + identifier + \")\" ),\n\t\t\"TAG\": new RegExp( \"^(\" + identifier + \"|[*])\" ),\n\t\t\"ATTR\": new RegExp( \"^\" + attributes ),\n\t\t\"PSEUDO\": new RegExp( \"^\" + pseudos ),\n\t\t\"CHILD\": new RegExp( \"^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\\\(\" +\n\t\t\twhitespace + \"*(even|odd|(([+-]|)(\\\\d*)n|)\" + whitespace + \"*(?:([+-]|)\" +\n\t\t\twhitespace + \"*(\\\\d+)|))\" + whitespace + \"*\\\\)|)\", \"i\" ),\n\t\t\"bool\": new RegExp( \"^(?:\" + booleans + \")$\", \"i\" ),\n\n\t\t// For use in libraries implementing .is()\n\t\t// We use this for POS matching in `select`\n\t\t\"needsContext\": new RegExp( \"^\" + whitespace +\n\t\t\t\"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\\\(\" + whitespace +\n\t\t\t\"*((?:-\\\\d)?\\\\d*)\" + whitespace + \"*\\\\)|)(?=[^-]|$)\", \"i\" )\n\t},\n\n\trhtml = /HTML$/i,\n\trinputs = /^(?:input|select|textarea|button)$/i,\n\trheader = /^h\\d$/i,\n\n\trnative = /^[^{]+\\{\\s*\\[native \\w/,\n\n\t// Easily-parseable/retrievable ID or TAG or CLASS selectors\n\trquickExpr = /^(?:#([\\w-]+)|(\\w+)|\\.([\\w-]+))$/,\n\n\trsibling = /[+~]/,\n\n\t// CSS escapes\n\t// http://www.w3.org/TR/CSS21/syndata.html#escaped-characters\n\trunescape = new RegExp( \"\\\\\\\\[\\\\da-fA-F]{1,6}\" + whitespace + \"?|\\\\\\\\([^\\\\r\\\\n\\\\f])\", \"g\" ),\n\tfunescape = function( escape, nonHex ) {\n\t\tvar high = \"0x\" + escape.slice( 1 ) - 0x10000;\n\n\t\treturn nonHex ?\n\n\t\t\t// Strip the backslash prefix from a non-hex escape sequence\n\t\t\tnonHex :\n\n\t\t\t// Replace a hexadecimal escape sequence with the encoded Unicode code point\n\t\t\t// Support: IE <=11+\n\t\t\t// For values outside the Basic Multilingual Plane (BMP), manually construct a\n\t\t\t// surrogate pair\n\t\t\thigh < 0 ?\n\t\t\t\tString.fromCharCode( high + 0x10000 ) :\n\t\t\t\tString.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );\n\t},\n\n\t// CSS string/identifier serialization\n\t// https://drafts.csswg.org/cssom/#common-serializing-idioms\n\trcssescape = /([\\0-\\x1f\\x7f]|^-?\\d)|^-$|[^\\0-\\x1f\\x7f-\\uFFFF\\w-]/g,\n\tfcssescape = function( ch, asCodePoint ) {\n\t\tif ( asCodePoint ) {\n\n\t\t\t// U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER\n\t\t\tif ( ch === \"\\0\" ) {\n\t\t\t\treturn \"\\uFFFD\";\n\t\t\t}\n\n\t\t\t// Control characters and (dependent upon position) numbers get escaped as code points\n\t\t\treturn ch.slice( 0, -1 ) + \"\\\\\" +\n\t\t\t\tch.charCodeAt( ch.length - 1 ).toString( 16 ) + \" \";\n\t\t}\n\n\t\t// Other potentially-special ASCII characters get backslash-escaped\n\t\treturn \"\\\\\" + ch;\n\t},\n\n\t// Used for iframes\n\t// See setDocument()\n\t// Removing the function wrapper causes a \"Permission Denied\"\n\t// error in IE\n\tunloadHandler = function() {\n\t\tsetDocument();\n\t},\n\n\tinDisabledFieldset = addCombinator(\n\t\tfunction( elem ) {\n\t\t\treturn elem.disabled === true && elem.nodeName.toLowerCase() === \"fieldset\";\n\t\t},\n\t\t{ dir: \"parentNode\", next: \"legend\" }\n\t);\n\n// Optimize for push.apply( _, NodeList )\ntry {\n\tpush.apply(\n\t\t( arr = slice.call( preferredDoc.childNodes ) ),\n\t\tpreferredDoc.childNodes\n\t);\n\n\t// Support: Android<4.0\n\t// Detect silently failing push.apply\n\t// eslint-disable-next-line no-unused-expressions\n\tarr[ preferredDoc.childNodes.length ].nodeType;\n} catch ( e ) {\n\tpush = { apply: arr.length ?\n\n\t\t// Leverage slice if possible\n\t\tfunction( target, els ) {\n\t\t\tpushNative.apply( target, slice.call( els ) );\n\t\t} :\n\n\t\t// Support: IE<9\n\t\t// Otherwise append directly\n\t\tfunction( target, els ) {\n\t\t\tvar j = target.length,\n\t\t\t\ti = 0;\n\n\t\t\t// Can't trust NodeList.length\n\t\t\twhile ( ( target[ j++ ] = els[ i++ ] ) ) {}\n\t\t\ttarget.length = j - 1;\n\t\t}\n\t};\n}\n\nfunction Sizzle( selector, context, results, seed ) {\n\tvar m, i, elem, nid, match, groups, newSelector,\n\t\tnewContext = context && context.ownerDocument,\n\n\t\t// nodeType defaults to 9, since context defaults to document\n\t\tnodeType = context ? context.nodeType : 9;\n\n\tresults = results || [];\n\n\t// Return early from calls with invalid selector or context\n\tif ( typeof selector !== \"string\" || !selector ||\n\t\tnodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {\n\n\t\treturn results;\n\t}\n\n\t// Try to shortcut find operations (as opposed to filters) in HTML documents\n\tif ( !seed ) {\n\t\tsetDocument( context );\n\t\tcontext = context || document;\n\n\t\tif ( documentIsHTML ) {\n\n\t\t\t// If the selector is sufficiently simple, try using a \"get*By*\" DOM method\n\t\t\t// (excepting DocumentFragment context, where the methods don't exist)\n\t\t\tif ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) {\n\n\t\t\t\t// ID selector\n\t\t\t\tif ( ( m = match[ 1 ] ) ) {\n\n\t\t\t\t\t// Document context\n\t\t\t\t\tif ( nodeType === 9 ) {\n\t\t\t\t\t\tif ( ( elem = context.getElementById( m ) ) ) {\n\n\t\t\t\t\t\t\t// Support: IE, Opera, Webkit\n\t\t\t\t\t\t\t// TODO: identify versions\n\t\t\t\t\t\t\t// getElementById can match elements by name instead of ID\n\t\t\t\t\t\t\tif ( elem.id === m ) {\n\t\t\t\t\t\t\t\tresults.push( elem );\n\t\t\t\t\t\t\t\treturn results;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treturn results;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t// Element context\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// Support: IE, Opera, Webkit\n\t\t\t\t\t\t// TODO: identify versions\n\t\t\t\t\t\t// getElementById can match elements by name instead of ID\n\t\t\t\t\t\tif ( newContext && ( elem = newContext.getElementById( m ) ) &&\n\t\t\t\t\t\t\tcontains( context, elem ) &&\n\t\t\t\t\t\t\telem.id === m ) {\n\n\t\t\t\t\t\t\tresults.push( elem );\n\t\t\t\t\t\t\treturn results;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t// Type selector\n\t\t\t\t} else if ( match[ 2 ] ) {\n\t\t\t\t\tpush.apply( results, context.getElementsByTagName( selector ) );\n\t\t\t\t\treturn results;\n\n\t\t\t\t// Class selector\n\t\t\t\t} else if ( ( m = match[ 3 ] ) && support.getElementsByClassName &&\n\t\t\t\t\tcontext.getElementsByClassName ) {\n\n\t\t\t\t\tpush.apply( results, context.getElementsByClassName( m ) );\n\t\t\t\t\treturn results;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Take advantage of querySelectorAll\n\t\t\tif ( support.qsa &&\n\t\t\t\t!nonnativeSelectorCache[ selector + \" \" ] &&\n\t\t\t\t( !rbuggyQSA || !rbuggyQSA.test( selector ) ) &&\n\n\t\t\t\t// Support: IE 8 only\n\t\t\t\t// Exclude object elements\n\t\t\t\t( nodeType !== 1 || context.nodeName.toLowerCase() !== \"object\" ) ) {\n\n\t\t\t\tnewSelector = selector;\n\t\t\t\tnewContext = context;\n\n\t\t\t\t// qSA considers elements outside a scoping root when evaluating child or\n\t\t\t\t// descendant combinators, which is not what we want.\n\t\t\t\t// In such cases, we work around the behavior by prefixing every selector in the\n\t\t\t\t// list with an ID selector referencing the scope context.\n\t\t\t\t// The technique has to be used as well when a leading combinator is used\n\t\t\t\t// as such selectors are not recognized by querySelectorAll.\n\t\t\t\t// Thanks to Andrew Dupont for this technique.\n\t\t\t\tif ( nodeType === 1 &&\n\t\t\t\t\t( rdescend.test( selector ) || rcombinators.test( selector ) ) ) {\n\n\t\t\t\t\t// Expand context for sibling selectors\n\t\t\t\t\tnewContext = rsibling.test( selector ) && testContext( context.parentNode ) ||\n\t\t\t\t\t\tcontext;\n\n\t\t\t\t\t// We can use :scope instead of the ID hack if the browser\n\t\t\t\t\t// supports it & if we're not changing the context.\n\t\t\t\t\tif ( newContext !== context || !support.scope ) {\n\n\t\t\t\t\t\t// Capture the context ID, setting it first if necessary\n\t\t\t\t\t\tif ( ( nid = context.getAttribute( \"id\" ) ) ) {\n\t\t\t\t\t\t\tnid = nid.replace( rcssescape, fcssescape );\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcontext.setAttribute( \"id\", ( nid = expando ) );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Prefix every selector in the list\n\t\t\t\t\tgroups = tokenize( selector );\n\t\t\t\t\ti = groups.length;\n\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\tgroups[ i ] = ( nid ? \"#\" + nid : \":scope\" ) + \" \" +\n\t\t\t\t\t\t\ttoSelector( groups[ i ] );\n\t\t\t\t\t}\n\t\t\t\t\tnewSelector = groups.join( \",\" );\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tpush.apply( results,\n\t\t\t\t\t\tnewContext.querySelectorAll( newSelector )\n\t\t\t\t\t);\n\t\t\t\t\treturn results;\n\t\t\t\t} catch ( qsaError ) {\n\t\t\t\t\tnonnativeSelectorCache( selector, true );\n\t\t\t\t} finally {\n\t\t\t\t\tif ( nid === expando ) {\n\t\t\t\t\t\tcontext.removeAttribute( \"id\" );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// All others\n\treturn select( selector.replace( rtrim, \"$1\" ), context, results, seed );\n}\n\n/**\n * Create key-value caches of limited size\n * @returns {function(string, object)} Returns the Object data after storing it on itself with\n *\tproperty name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)\n *\tdeleting the oldest entry\n */\nfunction createCache() {\n\tvar keys = [];\n\n\tfunction cache( key, value ) {\n\n\t\t// Use (key + \" \") to avoid collision with native prototype properties (see Issue #157)\n\t\tif ( keys.push( key + \" \" ) > Expr.cacheLength ) {\n\n\t\t\t// Only keep the most recent entries\n\t\t\tdelete cache[ keys.shift() ];\n\t\t}\n\t\treturn ( cache[ key + \" \" ] = value );\n\t}\n\treturn cache;\n}\n\n/**\n * Mark a function for special use by Sizzle\n * @param {Function} fn The function to mark\n */\nfunction markFunction( fn ) {\n\tfn[ expando ] = true;\n\treturn fn;\n}\n\n/**\n * Support testing using an element\n * @param {Function} fn Passed the created element and returns a boolean result\n */\nfunction assert( fn ) {\n\tvar el = document.createElement( \"fieldset\" );\n\n\ttry {\n\t\treturn !!fn( el );\n\t} catch ( e ) {\n\t\treturn false;\n\t} finally {\n\n\t\t// Remove from its parent by default\n\t\tif ( el.parentNode ) {\n\t\t\tel.parentNode.removeChild( el );\n\t\t}\n\n\t\t// release memory in IE\n\t\tel = null;\n\t}\n}\n\n/**\n * Adds the same handler for all of the specified attrs\n * @param {String} attrs Pipe-separated list of attributes\n * @param {Function} handler The method that will be applied\n */\nfunction addHandle( attrs, handler ) {\n\tvar arr = attrs.split( \"|\" ),\n\t\ti = arr.length;\n\n\twhile ( i-- ) {\n\t\tExpr.attrHandle[ arr[ i ] ] = handler;\n\t}\n}\n\n/**\n * Checks document order of two siblings\n * @param {Element} a\n * @param {Element} b\n * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b\n */\nfunction siblingCheck( a, b ) {\n\tvar cur = b && a,\n\t\tdiff = cur && a.nodeType === 1 && b.nodeType === 1 &&\n\t\t\ta.sourceIndex - b.sourceIndex;\n\n\t// Use IE sourceIndex if available on both nodes\n\tif ( diff ) {\n\t\treturn diff;\n\t}\n\n\t// Check if b follows a\n\tif ( cur ) {\n\t\twhile ( ( cur = cur.nextSibling ) ) {\n\t\t\tif ( cur === b ) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn a ? 1 : -1;\n}\n\n/**\n * Returns a function to use in pseudos for input types\n * @param {String} type\n */\nfunction createInputPseudo( type ) {\n\treturn function( elem ) {\n\t\tvar name = elem.nodeName.toLowerCase();\n\t\treturn name === \"input\" && elem.type === type;\n\t};\n}\n\n/**\n * Returns a function to use in pseudos for buttons\n * @param {String} type\n */\nfunction createButtonPseudo( type ) {\n\treturn function( elem ) {\n\t\tvar name = elem.nodeName.toLowerCase();\n\t\treturn ( name === \"input\" || name === \"button\" ) && elem.type === type;\n\t};\n}\n\n/**\n * Returns a function to use in pseudos for :enabled/:disabled\n * @param {Boolean} disabled true for :disabled; false for :enabled\n */\nfunction createDisabledPseudo( disabled ) {\n\n\t// Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable\n\treturn function( elem ) {\n\n\t\t// Only certain elements can match :enabled or :disabled\n\t\t// https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled\n\t\t// https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled\n\t\tif ( \"form\" in elem ) {\n\n\t\t\t// Check for inherited disabledness on relevant non-disabled elements:\n\t\t\t// * listed form-associated elements in a disabled fieldset\n\t\t\t// https://html.spec.whatwg.org/multipage/forms.html#category-listed\n\t\t\t// https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled\n\t\t\t// * option elements in a disabled optgroup\n\t\t\t// https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled\n\t\t\t// All such elements have a \"form\" property.\n\t\t\tif ( elem.parentNode && elem.disabled === false ) {\n\n\t\t\t\t// Option elements defer to a parent optgroup if present\n\t\t\t\tif ( \"label\" in elem ) {\n\t\t\t\t\tif ( \"label\" in elem.parentNode ) {\n\t\t\t\t\t\treturn elem.parentNode.disabled === disabled;\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn elem.disabled === disabled;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Support: IE 6 - 11\n\t\t\t\t// Use the isDisabled shortcut property to check for disabled fieldset ancestors\n\t\t\t\treturn elem.isDisabled === disabled ||\n\n\t\t\t\t\t// Where there is no isDisabled, check manually\n\t\t\t\t\t/* jshint -W018 */\n\t\t\t\t\telem.isDisabled !== !disabled &&\n\t\t\t\t\tinDisabledFieldset( elem ) === disabled;\n\t\t\t}\n\n\t\t\treturn elem.disabled === disabled;\n\n\t\t// Try to winnow out elements that can't be disabled before trusting the disabled property.\n\t\t// Some victims get caught in our net (label, legend, menu, track), but it shouldn't\n\t\t// even exist on them, let alone have a boolean value.\n\t\t} else if ( \"label\" in elem ) {\n\t\t\treturn elem.disabled === disabled;\n\t\t}\n\n\t\t// Remaining elements are neither :enabled nor :disabled\n\t\treturn false;\n\t};\n}\n\n/**\n * Returns a function to use in pseudos for positionals\n * @param {Function} fn\n */\nfunction createPositionalPseudo( fn ) {\n\treturn markFunction( function( argument ) {\n\t\targument = +argument;\n\t\treturn markFunction( function( seed, matches ) {\n\t\t\tvar j,\n\t\t\t\tmatchIndexes = fn( [], seed.length, argument ),\n\t\t\t\ti = matchIndexes.length;\n\n\t\t\t// Match elements found at the specified indexes\n\t\t\twhile ( i-- ) {\n\t\t\t\tif ( seed[ ( j = matchIndexes[ i ] ) ] ) {\n\t\t\t\t\tseed[ j ] = !( matches[ j ] = seed[ j ] );\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t} );\n}\n\n/**\n * Checks a node for validity as a Sizzle context\n * @param {Element|Object=} context\n * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value\n */\nfunction testContext( context ) {\n\treturn context && typeof context.getElementsByTagName !== \"undefined\" && context;\n}\n\n// Expose support vars for convenience\nsupport = Sizzle.support = {};\n\n/**\n * Detects XML nodes\n * @param {Element|Object} elem An element or a document\n * @returns {Boolean} True iff elem is a non-HTML XML node\n */\nisXML = Sizzle.isXML = function( elem ) {\n\tvar namespace = elem.namespaceURI,\n\t\tdocElem = ( elem.ownerDocument || elem ).documentElement;\n\n\t// Support: IE <=8\n\t// Assume HTML when documentElement doesn't yet exist, such as inside loading iframes\n\t// https://bugs.jquery.com/ticket/4833\n\treturn !rhtml.test( namespace || docElem && docElem.nodeName || \"HTML\" );\n};\n\n/**\n * Sets document-related variables once based on the current document\n * @param {Element|Object} [doc] An element or document object to use to set the document\n * @returns {Object} Returns the current document\n */\nsetDocument = Sizzle.setDocument = function( node ) {\n\tvar hasCompare, subWindow,\n\t\tdoc = node ? node.ownerDocument || node : preferredDoc;\n\n\t// Return early if doc is invalid or already selected\n\t// Support: IE 11+, Edge 17 - 18+\n\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t// two documents; shallow comparisons work.\n\t// eslint-disable-next-line eqeqeq\n\tif ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) {\n\t\treturn document;\n\t}\n\n\t// Update global variables\n\tdocument = doc;\n\tdocElem = document.documentElement;\n\tdocumentIsHTML = !isXML( document );\n\n\t// Support: IE 9 - 11+, Edge 12 - 18+\n\t// Accessing iframe documents after unload throws \"permission denied\" errors (jQuery #13936)\n\t// Support: IE 11+, Edge 17 - 18+\n\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t// two documents; shallow comparisons work.\n\t// eslint-disable-next-line eqeqeq\n\tif ( preferredDoc != document &&\n\t\t( subWindow = document.defaultView ) && subWindow.top !== subWindow ) {\n\n\t\t// Support: IE 11, Edge\n\t\tif ( subWindow.addEventListener ) {\n\t\t\tsubWindow.addEventListener( \"unload\", unloadHandler, false );\n\n\t\t// Support: IE 9 - 10 only\n\t\t} else if ( subWindow.attachEvent ) {\n\t\t\tsubWindow.attachEvent( \"onunload\", unloadHandler );\n\t\t}\n\t}\n\n\t// Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only,\n\t// Safari 4 - 5 only, Opera <=11.6 - 12.x only\n\t// IE/Edge & older browsers don't support the :scope pseudo-class.\n\t// Support: Safari 6.0 only\n\t// Safari 6.0 supports :scope but it's an alias of :root there.\n\tsupport.scope = assert( function( el ) {\n\t\tdocElem.appendChild( el ).appendChild( document.createElement( \"div\" ) );\n\t\treturn typeof el.querySelectorAll !== \"undefined\" &&\n\t\t\t!el.querySelectorAll( \":scope fieldset div\" ).length;\n\t} );\n\n\t/* Attributes\n\t---------------------------------------------------------------------- */\n\n\t// Support: IE<8\n\t// Verify that getAttribute really returns attributes and not properties\n\t// (excepting IE8 booleans)\n\tsupport.attributes = assert( function( el ) {\n\t\tel.className = \"i\";\n\t\treturn !el.getAttribute( \"className\" );\n\t} );\n\n\t/* getElement(s)By*\n\t---------------------------------------------------------------------- */\n\n\t// Check if getElementsByTagName(\"*\") returns only elements\n\tsupport.getElementsByTagName = assert( function( el ) {\n\t\tel.appendChild( document.createComment( \"\" ) );\n\t\treturn !el.getElementsByTagName( \"*\" ).length;\n\t} );\n\n\t// Support: IE<9\n\tsupport.getElementsByClassName = rnative.test( document.getElementsByClassName );\n\n\t// Support: IE<10\n\t// Check if getElementById returns elements by name\n\t// The broken getElementById methods don't pick up programmatically-set names,\n\t// so use a roundabout getElementsByName test\n\tsupport.getById = assert( function( el ) {\n\t\tdocElem.appendChild( el ).id = expando;\n\t\treturn !document.getElementsByName || !document.getElementsByName( expando ).length;\n\t} );\n\n\t// ID filter and find\n\tif ( support.getById ) {\n\t\tExpr.filter[ \"ID\" ] = function( id ) {\n\t\t\tvar attrId = id.replace( runescape, funescape );\n\t\t\treturn function( elem ) {\n\t\t\t\treturn elem.getAttribute( \"id\" ) === attrId;\n\t\t\t};\n\t\t};\n\t\tExpr.find[ \"ID\" ] = function( id, context ) {\n\t\t\tif ( typeof context.getElementById !== \"undefined\" && documentIsHTML ) {\n\t\t\t\tvar elem = context.getElementById( id );\n\t\t\t\treturn elem ? [ elem ] : [];\n\t\t\t}\n\t\t};\n\t} else {\n\t\tExpr.filter[ \"ID\" ] = function( id ) {\n\t\t\tvar attrId = id.replace( runescape, funescape );\n\t\t\treturn function( elem ) {\n\t\t\t\tvar node = typeof elem.getAttributeNode !== \"undefined\" &&\n\t\t\t\t\telem.getAttributeNode( \"id\" );\n\t\t\t\treturn node && node.value === attrId;\n\t\t\t};\n\t\t};\n\n\t\t// Support: IE 6 - 7 only\n\t\t// getElementById is not reliable as a find shortcut\n\t\tExpr.find[ \"ID\" ] = function( id, context ) {\n\t\t\tif ( typeof context.getElementById !== \"undefined\" && documentIsHTML ) {\n\t\t\t\tvar node, i, elems,\n\t\t\t\t\telem = context.getElementById( id );\n\n\t\t\t\tif ( elem ) {\n\n\t\t\t\t\t// Verify the id attribute\n\t\t\t\t\tnode = elem.getAttributeNode( \"id\" );\n\t\t\t\t\tif ( node && node.value === id ) {\n\t\t\t\t\t\treturn [ elem ];\n\t\t\t\t\t}\n\n\t\t\t\t\t// Fall back on getElementsByName\n\t\t\t\t\telems = context.getElementsByName( id );\n\t\t\t\t\ti = 0;\n\t\t\t\t\twhile ( ( elem = elems[ i++ ] ) ) {\n\t\t\t\t\t\tnode = elem.getAttributeNode( \"id\" );\n\t\t\t\t\t\tif ( node && node.value === id ) {\n\t\t\t\t\t\t\treturn [ elem ];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn [];\n\t\t\t}\n\t\t};\n\t}\n\n\t// Tag\n\tExpr.find[ \"TAG\" ] = support.getElementsByTagName ?\n\t\tfunction( tag, context ) {\n\t\t\tif ( typeof context.getElementsByTagName !== \"undefined\" ) {\n\t\t\t\treturn context.getElementsByTagName( tag );\n\n\t\t\t// DocumentFragment nodes don't have gEBTN\n\t\t\t} else if ( support.qsa ) {\n\t\t\t\treturn context.querySelectorAll( tag );\n\t\t\t}\n\t\t} :\n\n\t\tfunction( tag, context ) {\n\t\t\tvar elem,\n\t\t\t\ttmp = [],\n\t\t\t\ti = 0,\n\n\t\t\t\t// By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too\n\t\t\t\tresults = context.getElementsByTagName( tag );\n\n\t\t\t// Filter out possible comments\n\t\t\tif ( tag === \"*\" ) {\n\t\t\t\twhile ( ( elem = results[ i++ ] ) ) {\n\t\t\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\t\t\ttmp.push( elem );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn tmp;\n\t\t\t}\n\t\t\treturn results;\n\t\t};\n\n\t// Class\n\tExpr.find[ \"CLASS\" ] = support.getElementsByClassName && function( className, context ) {\n\t\tif ( typeof context.getElementsByClassName !== \"undefined\" && documentIsHTML ) {\n\t\t\treturn context.getElementsByClassName( className );\n\t\t}\n\t};\n\n\t/* QSA/matchesSelector\n\t---------------------------------------------------------------------- */\n\n\t// QSA and matchesSelector support\n\n\t// matchesSelector(:active) reports false when true (IE9/Opera 11.5)\n\trbuggyMatches = [];\n\n\t// qSa(:focus) reports false when true (Chrome 21)\n\t// We allow this because of a bug in IE8/9 that throws an error\n\t// whenever `document.activeElement` is accessed on an iframe\n\t// So, we allow :focus to pass through QSA all the time to avoid the IE error\n\t// See https://bugs.jquery.com/ticket/13378\n\trbuggyQSA = [];\n\n\tif ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) {\n\n\t\t// Build QSA regex\n\t\t// Regex strategy adopted from Diego Perini\n\t\tassert( function( el ) {\n\n\t\t\tvar input;\n\n\t\t\t// Select is set to empty string on purpose\n\t\t\t// This is to test IE's treatment of not explicitly\n\t\t\t// setting a boolean content attribute,\n\t\t\t// since its presence should be enough\n\t\t\t// https://bugs.jquery.com/ticket/12359\n\t\t\tdocElem.appendChild( el ).innerHTML = \"\" +\n\t\t\t\t\"\";\n\n\t\t\t// Support: IE8, Opera 11-12.16\n\t\t\t// Nothing should be selected when empty strings follow ^= or $= or *=\n\t\t\t// The test attribute must be unknown in Opera but \"safe\" for WinRT\n\t\t\t// https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section\n\t\t\tif ( el.querySelectorAll( \"[msallowcapture^='']\" ).length ) {\n\t\t\t\trbuggyQSA.push( \"[*^$]=\" + whitespace + \"*(?:''|\\\"\\\")\" );\n\t\t\t}\n\n\t\t\t// Support: IE8\n\t\t\t// Boolean attributes and \"value\" are not treated correctly\n\t\t\tif ( !el.querySelectorAll( \"[selected]\" ).length ) {\n\t\t\t\trbuggyQSA.push( \"\\\\[\" + whitespace + \"*(?:value|\" + booleans + \")\" );\n\t\t\t}\n\n\t\t\t// Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+\n\t\t\tif ( !el.querySelectorAll( \"[id~=\" + expando + \"-]\" ).length ) {\n\t\t\t\trbuggyQSA.push( \"~=\" );\n\t\t\t}\n\n\t\t\t// Support: IE 11+, Edge 15 - 18+\n\t\t\t// IE 11/Edge don't find elements on a `[name='']` query in some cases.\n\t\t\t// Adding a temporary attribute to the document before the selection works\n\t\t\t// around the issue.\n\t\t\t// Interestingly, IE 10 & older don't seem to have the issue.\n\t\t\tinput = document.createElement( \"input\" );\n\t\t\tinput.setAttribute( \"name\", \"\" );\n\t\t\tel.appendChild( input );\n\t\t\tif ( !el.querySelectorAll( \"[name='']\" ).length ) {\n\t\t\t\trbuggyQSA.push( \"\\\\[\" + whitespace + \"*name\" + whitespace + \"*=\" +\n\t\t\t\t\twhitespace + \"*(?:''|\\\"\\\")\" );\n\t\t\t}\n\n\t\t\t// Webkit/Opera - :checked should return selected option elements\n\t\t\t// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked\n\t\t\t// IE8 throws error here and will not see later tests\n\t\t\tif ( !el.querySelectorAll( \":checked\" ).length ) {\n\t\t\t\trbuggyQSA.push( \":checked\" );\n\t\t\t}\n\n\t\t\t// Support: Safari 8+, iOS 8+\n\t\t\t// https://bugs.webkit.org/show_bug.cgi?id=136851\n\t\t\t// In-page `selector#id sibling-combinator selector` fails\n\t\t\tif ( !el.querySelectorAll( \"a#\" + expando + \"+*\" ).length ) {\n\t\t\t\trbuggyQSA.push( \".#.+[+~]\" );\n\t\t\t}\n\n\t\t\t// Support: Firefox <=3.6 - 5 only\n\t\t\t// Old Firefox doesn't throw on a badly-escaped identifier.\n\t\t\tel.querySelectorAll( \"\\\\\\f\" );\n\t\t\trbuggyQSA.push( \"[\\\\r\\\\n\\\\f]\" );\n\t\t} );\n\n\t\tassert( function( el ) {\n\t\t\tel.innerHTML = \"\" +\n\t\t\t\t\"\";\n\n\t\t\t// Support: Windows 8 Native Apps\n\t\t\t// The type and name attributes are restricted during .innerHTML assignment\n\t\t\tvar input = document.createElement( \"input\" );\n\t\t\tinput.setAttribute( \"type\", \"hidden\" );\n\t\t\tel.appendChild( input ).setAttribute( \"name\", \"D\" );\n\n\t\t\t// Support: IE8\n\t\t\t// Enforce case-sensitivity of name attribute\n\t\t\tif ( el.querySelectorAll( \"[name=d]\" ).length ) {\n\t\t\t\trbuggyQSA.push( \"name\" + whitespace + \"*[*^$|!~]?=\" );\n\t\t\t}\n\n\t\t\t// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)\n\t\t\t// IE8 throws error here and will not see later tests\n\t\t\tif ( el.querySelectorAll( \":enabled\" ).length !== 2 ) {\n\t\t\t\trbuggyQSA.push( \":enabled\", \":disabled\" );\n\t\t\t}\n\n\t\t\t// Support: IE9-11+\n\t\t\t// IE's :disabled selector does not pick up the children of disabled fieldsets\n\t\t\tdocElem.appendChild( el ).disabled = true;\n\t\t\tif ( el.querySelectorAll( \":disabled\" ).length !== 2 ) {\n\t\t\t\trbuggyQSA.push( \":enabled\", \":disabled\" );\n\t\t\t}\n\n\t\t\t// Support: Opera 10 - 11 only\n\t\t\t// Opera 10-11 does not throw on post-comma invalid pseudos\n\t\t\tel.querySelectorAll( \"*,:x\" );\n\t\t\trbuggyQSA.push( \",.*:\" );\n\t\t} );\n\t}\n\n\tif ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches ||\n\t\tdocElem.webkitMatchesSelector ||\n\t\tdocElem.mozMatchesSelector ||\n\t\tdocElem.oMatchesSelector ||\n\t\tdocElem.msMatchesSelector ) ) ) ) {\n\n\t\tassert( function( el ) {\n\n\t\t\t// Check to see if it's possible to do matchesSelector\n\t\t\t// on a disconnected node (IE 9)\n\t\t\tsupport.disconnectedMatch = matches.call( el, \"*\" );\n\n\t\t\t// This should fail with an exception\n\t\t\t// Gecko does not error, returns false instead\n\t\t\tmatches.call( el, \"[s!='']:x\" );\n\t\t\trbuggyMatches.push( \"!=\", pseudos );\n\t\t} );\n\t}\n\n\trbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( \"|\" ) );\n\trbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( \"|\" ) );\n\n\t/* Contains\n\t---------------------------------------------------------------------- */\n\thasCompare = rnative.test( docElem.compareDocumentPosition );\n\n\t// Element contains another\n\t// Purposefully self-exclusive\n\t// As in, an element does not contain itself\n\tcontains = hasCompare || rnative.test( docElem.contains ) ?\n\t\tfunction( a, b ) {\n\t\t\tvar adown = a.nodeType === 9 ? a.documentElement : a,\n\t\t\t\tbup = b && b.parentNode;\n\t\t\treturn a === bup || !!( bup && bup.nodeType === 1 && (\n\t\t\t\tadown.contains ?\n\t\t\t\t\tadown.contains( bup ) :\n\t\t\t\t\ta.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16\n\t\t\t) );\n\t\t} :\n\t\tfunction( a, b ) {\n\t\t\tif ( b ) {\n\t\t\t\twhile ( ( b = b.parentNode ) ) {\n\t\t\t\t\tif ( b === a ) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t};\n\n\t/* Sorting\n\t---------------------------------------------------------------------- */\n\n\t// Document order sorting\n\tsortOrder = hasCompare ?\n\tfunction( a, b ) {\n\n\t\t// Flag for duplicate removal\n\t\tif ( a === b ) {\n\t\t\thasDuplicate = true;\n\t\t\treturn 0;\n\t\t}\n\n\t\t// Sort on method existence if only one input has compareDocumentPosition\n\t\tvar compare = !a.compareDocumentPosition - !b.compareDocumentPosition;\n\t\tif ( compare ) {\n\t\t\treturn compare;\n\t\t}\n\n\t\t// Calculate position if both inputs belong to the same document\n\t\t// Support: IE 11+, Edge 17 - 18+\n\t\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t\t// two documents; shallow comparisons work.\n\t\t// eslint-disable-next-line eqeqeq\n\t\tcompare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ?\n\t\t\ta.compareDocumentPosition( b ) :\n\n\t\t\t// Otherwise we know they are disconnected\n\t\t\t1;\n\n\t\t// Disconnected nodes\n\t\tif ( compare & 1 ||\n\t\t\t( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) {\n\n\t\t\t// Choose the first element that is related to our preferred document\n\t\t\t// Support: IE 11+, Edge 17 - 18+\n\t\t\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t\t\t// two documents; shallow comparisons work.\n\t\t\t// eslint-disable-next-line eqeqeq\n\t\t\tif ( a == document || a.ownerDocument == preferredDoc &&\n\t\t\t\tcontains( preferredDoc, a ) ) {\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\t// Support: IE 11+, Edge 17 - 18+\n\t\t\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t\t\t// two documents; shallow comparisons work.\n\t\t\t// eslint-disable-next-line eqeqeq\n\t\t\tif ( b == document || b.ownerDocument == preferredDoc &&\n\t\t\t\tcontains( preferredDoc, b ) ) {\n\t\t\t\treturn 1;\n\t\t\t}\n\n\t\t\t// Maintain original order\n\t\t\treturn sortInput ?\n\t\t\t\t( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :\n\t\t\t\t0;\n\t\t}\n\n\t\treturn compare & 4 ? -1 : 1;\n\t} :\n\tfunction( a, b ) {\n\n\t\t// Exit early if the nodes are identical\n\t\tif ( a === b ) {\n\t\t\thasDuplicate = true;\n\t\t\treturn 0;\n\t\t}\n\n\t\tvar cur,\n\t\t\ti = 0,\n\t\t\taup = a.parentNode,\n\t\t\tbup = b.parentNode,\n\t\t\tap = [ a ],\n\t\t\tbp = [ b ];\n\n\t\t// Parentless nodes are either documents or disconnected\n\t\tif ( !aup || !bup ) {\n\n\t\t\t// Support: IE 11+, Edge 17 - 18+\n\t\t\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t\t\t// two documents; shallow comparisons work.\n\t\t\t/* eslint-disable eqeqeq */\n\t\t\treturn a == document ? -1 :\n\t\t\t\tb == document ? 1 :\n\t\t\t\t/* eslint-enable eqeqeq */\n\t\t\t\taup ? -1 :\n\t\t\t\tbup ? 1 :\n\t\t\t\tsortInput ?\n\t\t\t\t( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :\n\t\t\t\t0;\n\n\t\t// If the nodes are siblings, we can do a quick check\n\t\t} else if ( aup === bup ) {\n\t\t\treturn siblingCheck( a, b );\n\t\t}\n\n\t\t// Otherwise we need full lists of their ancestors for comparison\n\t\tcur = a;\n\t\twhile ( ( cur = cur.parentNode ) ) {\n\t\t\tap.unshift( cur );\n\t\t}\n\t\tcur = b;\n\t\twhile ( ( cur = cur.parentNode ) ) {\n\t\t\tbp.unshift( cur );\n\t\t}\n\n\t\t// Walk down the tree looking for a discrepancy\n\t\twhile ( ap[ i ] === bp[ i ] ) {\n\t\t\ti++;\n\t\t}\n\n\t\treturn i ?\n\n\t\t\t// Do a sibling check if the nodes have a common ancestor\n\t\t\tsiblingCheck( ap[ i ], bp[ i ] ) :\n\n\t\t\t// Otherwise nodes in our document sort first\n\t\t\t// Support: IE 11+, Edge 17 - 18+\n\t\t\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t\t\t// two documents; shallow comparisons work.\n\t\t\t/* eslint-disable eqeqeq */\n\t\t\tap[ i ] == preferredDoc ? -1 :\n\t\t\tbp[ i ] == preferredDoc ? 1 :\n\t\t\t/* eslint-enable eqeqeq */\n\t\t\t0;\n\t};\n\n\treturn document;\n};\n\nSizzle.matches = function( expr, elements ) {\n\treturn Sizzle( expr, null, null, elements );\n};\n\nSizzle.matchesSelector = function( elem, expr ) {\n\tsetDocument( elem );\n\n\tif ( support.matchesSelector && documentIsHTML &&\n\t\t!nonnativeSelectorCache[ expr + \" \" ] &&\n\t\t( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&\n\t\t( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {\n\n\t\ttry {\n\t\t\tvar ret = matches.call( elem, expr );\n\n\t\t\t// IE 9's matchesSelector returns false on disconnected nodes\n\t\t\tif ( ret || support.disconnectedMatch ||\n\n\t\t\t\t// As well, disconnected nodes are said to be in a document\n\t\t\t\t// fragment in IE 9\n\t\t\t\telem.document && elem.document.nodeType !== 11 ) {\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t} catch ( e ) {\n\t\t\tnonnativeSelectorCache( expr, true );\n\t\t}\n\t}\n\n\treturn Sizzle( expr, document, null, [ elem ] ).length > 0;\n};\n\nSizzle.contains = function( context, elem ) {\n\n\t// Set document vars if needed\n\t// Support: IE 11+, Edge 17 - 18+\n\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t// two documents; shallow comparisons work.\n\t// eslint-disable-next-line eqeqeq\n\tif ( ( context.ownerDocument || context ) != document ) {\n\t\tsetDocument( context );\n\t}\n\treturn contains( context, elem );\n};\n\nSizzle.attr = function( elem, name ) {\n\n\t// Set document vars if needed\n\t// Support: IE 11+, Edge 17 - 18+\n\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t// two documents; shallow comparisons work.\n\t// eslint-disable-next-line eqeqeq\n\tif ( ( elem.ownerDocument || elem ) != document ) {\n\t\tsetDocument( elem );\n\t}\n\n\tvar fn = Expr.attrHandle[ name.toLowerCase() ],\n\n\t\t// Don't get fooled by Object.prototype properties (jQuery #13807)\n\t\tval = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?\n\t\t\tfn( elem, name, !documentIsHTML ) :\n\t\t\tundefined;\n\n\treturn val !== undefined ?\n\t\tval :\n\t\tsupport.attributes || !documentIsHTML ?\n\t\t\telem.getAttribute( name ) :\n\t\t\t( val = elem.getAttributeNode( name ) ) && val.specified ?\n\t\t\t\tval.value :\n\t\t\t\tnull;\n};\n\nSizzle.escape = function( sel ) {\n\treturn ( sel + \"\" ).replace( rcssescape, fcssescape );\n};\n\nSizzle.error = function( msg ) {\n\tthrow new Error( \"Syntax error, unrecognized expression: \" + msg );\n};\n\n/**\n * Document sorting and removing duplicates\n * @param {ArrayLike} results\n */\nSizzle.uniqueSort = function( results ) {\n\tvar elem,\n\t\tduplicates = [],\n\t\tj = 0,\n\t\ti = 0;\n\n\t// Unless we *know* we can detect duplicates, assume their presence\n\thasDuplicate = !support.detectDuplicates;\n\tsortInput = !support.sortStable && results.slice( 0 );\n\tresults.sort( sortOrder );\n\n\tif ( hasDuplicate ) {\n\t\twhile ( ( elem = results[ i++ ] ) ) {\n\t\t\tif ( elem === results[ i ] ) {\n\t\t\t\tj = duplicates.push( i );\n\t\t\t}\n\t\t}\n\t\twhile ( j-- ) {\n\t\t\tresults.splice( duplicates[ j ], 1 );\n\t\t}\n\t}\n\n\t// Clear input after sorting to release objects\n\t// See https://github.com/jquery/sizzle/pull/225\n\tsortInput = null;\n\n\treturn results;\n};\n\n/**\n * Utility function for retrieving the text value of an array of DOM nodes\n * @param {Array|Element} elem\n */\ngetText = Sizzle.getText = function( elem ) {\n\tvar node,\n\t\tret = \"\",\n\t\ti = 0,\n\t\tnodeType = elem.nodeType;\n\n\tif ( !nodeType ) {\n\n\t\t// If no nodeType, this is expected to be an array\n\t\twhile ( ( node = elem[ i++ ] ) ) {\n\n\t\t\t// Do not traverse comment nodes\n\t\t\tret += getText( node );\n\t\t}\n\t} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {\n\n\t\t// Use textContent for elements\n\t\t// innerText usage removed for consistency of new lines (jQuery #11153)\n\t\tif ( typeof elem.textContent === \"string\" ) {\n\t\t\treturn elem.textContent;\n\t\t} else {\n\n\t\t\t// Traverse its children\n\t\t\tfor ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {\n\t\t\t\tret += getText( elem );\n\t\t\t}\n\t\t}\n\t} else if ( nodeType === 3 || nodeType === 4 ) {\n\t\treturn elem.nodeValue;\n\t}\n\n\t// Do not include comment or processing instruction nodes\n\n\treturn ret;\n};\n\nExpr = Sizzle.selectors = {\n\n\t// Can be adjusted by the user\n\tcacheLength: 50,\n\n\tcreatePseudo: markFunction,\n\n\tmatch: matchExpr,\n\n\tattrHandle: {},\n\n\tfind: {},\n\n\trelative: {\n\t\t\">\": { dir: \"parentNode\", first: true },\n\t\t\" \": { dir: \"parentNode\" },\n\t\t\"+\": { dir: \"previousSibling\", first: true },\n\t\t\"~\": { dir: \"previousSibling\" }\n\t},\n\n\tpreFilter: {\n\t\t\"ATTR\": function( match ) {\n\t\t\tmatch[ 1 ] = match[ 1 ].replace( runescape, funescape );\n\n\t\t\t// Move the given value to match[3] whether quoted or unquoted\n\t\t\tmatch[ 3 ] = ( match[ 3 ] || match[ 4 ] ||\n\t\t\t\tmatch[ 5 ] || \"\" ).replace( runescape, funescape );\n\n\t\t\tif ( match[ 2 ] === \"~=\" ) {\n\t\t\t\tmatch[ 3 ] = \" \" + match[ 3 ] + \" \";\n\t\t\t}\n\n\t\t\treturn match.slice( 0, 4 );\n\t\t},\n\n\t\t\"CHILD\": function( match ) {\n\n\t\t\t/* matches from matchExpr[\"CHILD\"]\n\t\t\t\t1 type (only|nth|...)\n\t\t\t\t2 what (child|of-type)\n\t\t\t\t3 argument (even|odd|\\d*|\\d*n([+-]\\d+)?|...)\n\t\t\t\t4 xn-component of xn+y argument ([+-]?\\d*n|)\n\t\t\t\t5 sign of xn-component\n\t\t\t\t6 x of xn-component\n\t\t\t\t7 sign of y-component\n\t\t\t\t8 y of y-component\n\t\t\t*/\n\t\t\tmatch[ 1 ] = match[ 1 ].toLowerCase();\n\n\t\t\tif ( match[ 1 ].slice( 0, 3 ) === \"nth\" ) {\n\n\t\t\t\t// nth-* requires argument\n\t\t\t\tif ( !match[ 3 ] ) {\n\t\t\t\t\tSizzle.error( match[ 0 ] );\n\t\t\t\t}\n\n\t\t\t\t// numeric x and y parameters for Expr.filter.CHILD\n\t\t\t\t// remember that false/true cast respectively to 0/1\n\t\t\t\tmatch[ 4 ] = +( match[ 4 ] ?\n\t\t\t\t\tmatch[ 5 ] + ( match[ 6 ] || 1 ) :\n\t\t\t\t\t2 * ( match[ 3 ] === \"even\" || match[ 3 ] === \"odd\" ) );\n\t\t\t\tmatch[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === \"odd\" );\n\n\t\t\t\t// other types prohibit arguments\n\t\t\t} else if ( match[ 3 ] ) {\n\t\t\t\tSizzle.error( match[ 0 ] );\n\t\t\t}\n\n\t\t\treturn match;\n\t\t},\n\n\t\t\"PSEUDO\": function( match ) {\n\t\t\tvar excess,\n\t\t\t\tunquoted = !match[ 6 ] && match[ 2 ];\n\n\t\t\tif ( matchExpr[ \"CHILD\" ].test( match[ 0 ] ) ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Accept quoted arguments as-is\n\t\t\tif ( match[ 3 ] ) {\n\t\t\t\tmatch[ 2 ] = match[ 4 ] || match[ 5 ] || \"\";\n\n\t\t\t// Strip excess characters from unquoted arguments\n\t\t\t} else if ( unquoted && rpseudo.test( unquoted ) &&\n\n\t\t\t\t// Get excess from tokenize (recursively)\n\t\t\t\t( excess = tokenize( unquoted, true ) ) &&\n\n\t\t\t\t// advance to the next closing parenthesis\n\t\t\t\t( excess = unquoted.indexOf( \")\", unquoted.length - excess ) - unquoted.length ) ) {\n\n\t\t\t\t// excess is a negative index\n\t\t\t\tmatch[ 0 ] = match[ 0 ].slice( 0, excess );\n\t\t\t\tmatch[ 2 ] = unquoted.slice( 0, excess );\n\t\t\t}\n\n\t\t\t// Return only captures needed by the pseudo filter method (type and argument)\n\t\t\treturn match.slice( 0, 3 );\n\t\t}\n\t},\n\n\tfilter: {\n\n\t\t\"TAG\": function( nodeNameSelector ) {\n\t\t\tvar nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();\n\t\t\treturn nodeNameSelector === \"*\" ?\n\t\t\t\tfunction() {\n\t\t\t\t\treturn true;\n\t\t\t\t} :\n\t\t\t\tfunction( elem ) {\n\t\t\t\t\treturn elem.nodeName && elem.nodeName.toLowerCase() === nodeName;\n\t\t\t\t};\n\t\t},\n\n\t\t\"CLASS\": function( className ) {\n\t\t\tvar pattern = classCache[ className + \" \" ];\n\n\t\t\treturn pattern ||\n\t\t\t\t( pattern = new RegExp( \"(^|\" + whitespace +\n\t\t\t\t\t\")\" + className + \"(\" + whitespace + \"|$)\" ) ) && classCache(\n\t\t\t\t\t\tclassName, function( elem ) {\n\t\t\t\t\t\t\treturn pattern.test(\n\t\t\t\t\t\t\t\ttypeof elem.className === \"string\" && elem.className ||\n\t\t\t\t\t\t\t\ttypeof elem.getAttribute !== \"undefined\" &&\n\t\t\t\t\t\t\t\t\telem.getAttribute( \"class\" ) ||\n\t\t\t\t\t\t\t\t\"\"\n\t\t\t\t\t\t\t);\n\t\t\t\t} );\n\t\t},\n\n\t\t\"ATTR\": function( name, operator, check ) {\n\t\t\treturn function( elem ) {\n\t\t\t\tvar result = Sizzle.attr( elem, name );\n\n\t\t\t\tif ( result == null ) {\n\t\t\t\t\treturn operator === \"!=\";\n\t\t\t\t}\n\t\t\t\tif ( !operator ) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tresult += \"\";\n\n\t\t\t\t/* eslint-disable max-len */\n\n\t\t\t\treturn operator === \"=\" ? result === check :\n\t\t\t\t\toperator === \"!=\" ? result !== check :\n\t\t\t\t\toperator === \"^=\" ? check && result.indexOf( check ) === 0 :\n\t\t\t\t\toperator === \"*=\" ? check && result.indexOf( check ) > -1 :\n\t\t\t\t\toperator === \"$=\" ? check && result.slice( -check.length ) === check :\n\t\t\t\t\toperator === \"~=\" ? ( \" \" + result.replace( rwhitespace, \" \" ) + \" \" ).indexOf( check ) > -1 :\n\t\t\t\t\toperator === \"|=\" ? result === check || result.slice( 0, check.length + 1 ) === check + \"-\" :\n\t\t\t\t\tfalse;\n\t\t\t\t/* eslint-enable max-len */\n\n\t\t\t};\n\t\t},\n\n\t\t\"CHILD\": function( type, what, _argument, first, last ) {\n\t\t\tvar simple = type.slice( 0, 3 ) !== \"nth\",\n\t\t\t\tforward = type.slice( -4 ) !== \"last\",\n\t\t\t\tofType = what === \"of-type\";\n\n\t\t\treturn first === 1 && last === 0 ?\n\n\t\t\t\t// Shortcut for :nth-*(n)\n\t\t\t\tfunction( elem ) {\n\t\t\t\t\treturn !!elem.parentNode;\n\t\t\t\t} :\n\n\t\t\t\tfunction( elem, _context, xml ) {\n\t\t\t\t\tvar cache, uniqueCache, outerCache, node, nodeIndex, start,\n\t\t\t\t\t\tdir = simple !== forward ? \"nextSibling\" : \"previousSibling\",\n\t\t\t\t\t\tparent = elem.parentNode,\n\t\t\t\t\t\tname = ofType && elem.nodeName.toLowerCase(),\n\t\t\t\t\t\tuseCache = !xml && !ofType,\n\t\t\t\t\t\tdiff = false;\n\n\t\t\t\t\tif ( parent ) {\n\n\t\t\t\t\t\t// :(first|last|only)-(child|of-type)\n\t\t\t\t\t\tif ( simple ) {\n\t\t\t\t\t\t\twhile ( dir ) {\n\t\t\t\t\t\t\t\tnode = elem;\n\t\t\t\t\t\t\t\twhile ( ( node = node[ dir ] ) ) {\n\t\t\t\t\t\t\t\t\tif ( ofType ?\n\t\t\t\t\t\t\t\t\t\tnode.nodeName.toLowerCase() === name :\n\t\t\t\t\t\t\t\t\t\tnode.nodeType === 1 ) {\n\n\t\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Reverse direction for :only-* (if we haven't yet done so)\n\t\t\t\t\t\t\t\tstart = dir = type === \"only\" && !start && \"nextSibling\";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tstart = [ forward ? parent.firstChild : parent.lastChild ];\n\n\t\t\t\t\t\t// non-xml :nth-child(...) stores cache data on `parent`\n\t\t\t\t\t\tif ( forward && useCache ) {\n\n\t\t\t\t\t\t\t// Seek `elem` from a previously-cached index\n\n\t\t\t\t\t\t\t// ...in a gzip-friendly way\n\t\t\t\t\t\t\tnode = parent;\n\t\t\t\t\t\t\touterCache = node[ expando ] || ( node[ expando ] = {} );\n\n\t\t\t\t\t\t\t// Support: IE <9 only\n\t\t\t\t\t\t\t// Defend against cloned attroperties (jQuery gh-1709)\n\t\t\t\t\t\t\tuniqueCache = outerCache[ node.uniqueID ] ||\n\t\t\t\t\t\t\t\t( outerCache[ node.uniqueID ] = {} );\n\n\t\t\t\t\t\t\tcache = uniqueCache[ type ] || [];\n\t\t\t\t\t\t\tnodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];\n\t\t\t\t\t\t\tdiff = nodeIndex && cache[ 2 ];\n\t\t\t\t\t\t\tnode = nodeIndex && parent.childNodes[ nodeIndex ];\n\n\t\t\t\t\t\t\twhile ( ( node = ++nodeIndex && node && node[ dir ] ||\n\n\t\t\t\t\t\t\t\t// Fallback to seeking `elem` from the start\n\t\t\t\t\t\t\t\t( diff = nodeIndex = 0 ) || start.pop() ) ) {\n\n\t\t\t\t\t\t\t\t// When found, cache indexes on `parent` and break\n\t\t\t\t\t\t\t\tif ( node.nodeType === 1 && ++diff && node === elem ) {\n\t\t\t\t\t\t\t\t\tuniqueCache[ type ] = [ dirruns, nodeIndex, diff ];\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t// Use previously-cached element index if available\n\t\t\t\t\t\t\tif ( useCache ) {\n\n\t\t\t\t\t\t\t\t// ...in a gzip-friendly way\n\t\t\t\t\t\t\t\tnode = elem;\n\t\t\t\t\t\t\t\touterCache = node[ expando ] || ( node[ expando ] = {} );\n\n\t\t\t\t\t\t\t\t// Support: IE <9 only\n\t\t\t\t\t\t\t\t// Defend against cloned attroperties (jQuery gh-1709)\n\t\t\t\t\t\t\t\tuniqueCache = outerCache[ node.uniqueID ] ||\n\t\t\t\t\t\t\t\t\t( outerCache[ node.uniqueID ] = {} );\n\n\t\t\t\t\t\t\t\tcache = uniqueCache[ type ] || [];\n\t\t\t\t\t\t\t\tnodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];\n\t\t\t\t\t\t\t\tdiff = nodeIndex;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// xml :nth-child(...)\n\t\t\t\t\t\t\t// or :nth-last-child(...) or :nth(-last)?-of-type(...)\n\t\t\t\t\t\t\tif ( diff === false ) {\n\n\t\t\t\t\t\t\t\t// Use the same loop as above to seek `elem` from the start\n\t\t\t\t\t\t\t\twhile ( ( node = ++nodeIndex && node && node[ dir ] ||\n\t\t\t\t\t\t\t\t\t( diff = nodeIndex = 0 ) || start.pop() ) ) {\n\n\t\t\t\t\t\t\t\t\tif ( ( ofType ?\n\t\t\t\t\t\t\t\t\t\tnode.nodeName.toLowerCase() === name :\n\t\t\t\t\t\t\t\t\t\tnode.nodeType === 1 ) &&\n\t\t\t\t\t\t\t\t\t\t++diff ) {\n\n\t\t\t\t\t\t\t\t\t\t// Cache the index of each encountered element\n\t\t\t\t\t\t\t\t\t\tif ( useCache ) {\n\t\t\t\t\t\t\t\t\t\t\touterCache = node[ expando ] ||\n\t\t\t\t\t\t\t\t\t\t\t\t( node[ expando ] = {} );\n\n\t\t\t\t\t\t\t\t\t\t\t// Support: IE <9 only\n\t\t\t\t\t\t\t\t\t\t\t// Defend against cloned attroperties (jQuery gh-1709)\n\t\t\t\t\t\t\t\t\t\t\tuniqueCache = outerCache[ node.uniqueID ] ||\n\t\t\t\t\t\t\t\t\t\t\t\t( outerCache[ node.uniqueID ] = {} );\n\n\t\t\t\t\t\t\t\t\t\t\tuniqueCache[ type ] = [ dirruns, diff ];\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tif ( node === elem ) {\n\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Incorporate the offset, then check against cycle size\n\t\t\t\t\t\tdiff -= last;\n\t\t\t\t\t\treturn diff === first || ( diff % first === 0 && diff / first >= 0 );\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t},\n\n\t\t\"PSEUDO\": function( pseudo, argument ) {\n\n\t\t\t// pseudo-class names are case-insensitive\n\t\t\t// http://www.w3.org/TR/selectors/#pseudo-classes\n\t\t\t// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters\n\t\t\t// Remember that setFilters inherits from pseudos\n\t\t\tvar args,\n\t\t\t\tfn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||\n\t\t\t\t\tSizzle.error( \"unsupported pseudo: \" + pseudo );\n\n\t\t\t// The user may use createPseudo to indicate that\n\t\t\t// arguments are needed to create the filter function\n\t\t\t// just as Sizzle does\n\t\t\tif ( fn[ expando ] ) {\n\t\t\t\treturn fn( argument );\n\t\t\t}\n\n\t\t\t// But maintain support for old signatures\n\t\t\tif ( fn.length > 1 ) {\n\t\t\t\targs = [ pseudo, pseudo, \"\", argument ];\n\t\t\t\treturn Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?\n\t\t\t\t\tmarkFunction( function( seed, matches ) {\n\t\t\t\t\t\tvar idx,\n\t\t\t\t\t\t\tmatched = fn( seed, argument ),\n\t\t\t\t\t\t\ti = matched.length;\n\t\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\t\tidx = indexOf( seed, matched[ i ] );\n\t\t\t\t\t\t\tseed[ idx ] = !( matches[ idx ] = matched[ i ] );\n\t\t\t\t\t\t}\n\t\t\t\t\t} ) :\n\t\t\t\t\tfunction( elem ) {\n\t\t\t\t\t\treturn fn( elem, 0, args );\n\t\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn fn;\n\t\t}\n\t},\n\n\tpseudos: {\n\n\t\t// Potentially complex pseudos\n\t\t\"not\": markFunction( function( selector ) {\n\n\t\t\t// Trim the selector passed to compile\n\t\t\t// to avoid treating leading and trailing\n\t\t\t// spaces as combinators\n\t\t\tvar input = [],\n\t\t\t\tresults = [],\n\t\t\t\tmatcher = compile( selector.replace( rtrim, \"$1\" ) );\n\n\t\t\treturn matcher[ expando ] ?\n\t\t\t\tmarkFunction( function( seed, matches, _context, xml ) {\n\t\t\t\t\tvar elem,\n\t\t\t\t\t\tunmatched = matcher( seed, null, xml, [] ),\n\t\t\t\t\t\ti = seed.length;\n\n\t\t\t\t\t// Match elements unmatched by `matcher`\n\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\tif ( ( elem = unmatched[ i ] ) ) {\n\t\t\t\t\t\t\tseed[ i ] = !( matches[ i ] = elem );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} ) :\n\t\t\t\tfunction( elem, _context, xml ) {\n\t\t\t\t\tinput[ 0 ] = elem;\n\t\t\t\t\tmatcher( input, null, xml, results );\n\n\t\t\t\t\t// Don't keep the element (issue #299)\n\t\t\t\t\tinput[ 0 ] = null;\n\t\t\t\t\treturn !results.pop();\n\t\t\t\t};\n\t\t} ),\n\n\t\t\"has\": markFunction( function( selector ) {\n\t\t\treturn function( elem ) {\n\t\t\t\treturn Sizzle( selector, elem ).length > 0;\n\t\t\t};\n\t\t} ),\n\n\t\t\"contains\": markFunction( function( text ) {\n\t\t\ttext = text.replace( runescape, funescape );\n\t\t\treturn function( elem ) {\n\t\t\t\treturn ( elem.textContent || getText( elem ) ).indexOf( text ) > -1;\n\t\t\t};\n\t\t} ),\n\n\t\t// \"Whether an element is represented by a :lang() selector\n\t\t// is based solely on the element's language value\n\t\t// being equal to the identifier C,\n\t\t// or beginning with the identifier C immediately followed by \"-\".\n\t\t// The matching of C against the element's language value is performed case-insensitively.\n\t\t// The identifier C does not have to be a valid language name.\"\n\t\t// http://www.w3.org/TR/selectors/#lang-pseudo\n\t\t\"lang\": markFunction( function( lang ) {\n\n\t\t\t// lang value must be a valid identifier\n\t\t\tif ( !ridentifier.test( lang || \"\" ) ) {\n\t\t\t\tSizzle.error( \"unsupported lang: \" + lang );\n\t\t\t}\n\t\t\tlang = lang.replace( runescape, funescape ).toLowerCase();\n\t\t\treturn function( elem ) {\n\t\t\t\tvar elemLang;\n\t\t\t\tdo {\n\t\t\t\t\tif ( ( elemLang = documentIsHTML ?\n\t\t\t\t\t\telem.lang :\n\t\t\t\t\t\telem.getAttribute( \"xml:lang\" ) || elem.getAttribute( \"lang\" ) ) ) {\n\n\t\t\t\t\t\telemLang = elemLang.toLowerCase();\n\t\t\t\t\t\treturn elemLang === lang || elemLang.indexOf( lang + \"-\" ) === 0;\n\t\t\t\t\t}\n\t\t\t\t} while ( ( elem = elem.parentNode ) && elem.nodeType === 1 );\n\t\t\t\treturn false;\n\t\t\t};\n\t\t} ),\n\n\t\t// Miscellaneous\n\t\t\"target\": function( elem ) {\n\t\t\tvar hash = window.location && window.location.hash;\n\t\t\treturn hash && hash.slice( 1 ) === elem.id;\n\t\t},\n\n\t\t\"root\": function( elem ) {\n\t\t\treturn elem === docElem;\n\t\t},\n\n\t\t\"focus\": function( elem ) {\n\t\t\treturn elem === document.activeElement &&\n\t\t\t\t( !document.hasFocus || document.hasFocus() ) &&\n\t\t\t\t!!( elem.type || elem.href || ~elem.tabIndex );\n\t\t},\n\n\t\t// Boolean properties\n\t\t\"enabled\": createDisabledPseudo( false ),\n\t\t\"disabled\": createDisabledPseudo( true ),\n\n\t\t\"checked\": function( elem ) {\n\n\t\t\t// In CSS3, :checked should return both checked and selected elements\n\t\t\t// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked\n\t\t\tvar nodeName = elem.nodeName.toLowerCase();\n\t\t\treturn ( nodeName === \"input\" && !!elem.checked ) ||\n\t\t\t\t( nodeName === \"option\" && !!elem.selected );\n\t\t},\n\n\t\t\"selected\": function( elem ) {\n\n\t\t\t// Accessing this property makes selected-by-default\n\t\t\t// options in Safari work properly\n\t\t\tif ( elem.parentNode ) {\n\t\t\t\t// eslint-disable-next-line no-unused-expressions\n\t\t\t\telem.parentNode.selectedIndex;\n\t\t\t}\n\n\t\t\treturn elem.selected === true;\n\t\t},\n\n\t\t// Contents\n\t\t\"empty\": function( elem ) {\n\n\t\t\t// http://www.w3.org/TR/selectors/#empty-pseudo\n\t\t\t// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),\n\t\t\t// but not by others (comment: 8; processing instruction: 7; etc.)\n\t\t\t// nodeType < 6 works because attributes (2) do not appear as children\n\t\t\tfor ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {\n\t\t\t\tif ( elem.nodeType < 6 ) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\n\t\t\"parent\": function( elem ) {\n\t\t\treturn !Expr.pseudos[ \"empty\" ]( elem );\n\t\t},\n\n\t\t// Element/input types\n\t\t\"header\": function( elem ) {\n\t\t\treturn rheader.test( elem.nodeName );\n\t\t},\n\n\t\t\"input\": function( elem ) {\n\t\t\treturn rinputs.test( elem.nodeName );\n\t\t},\n\n\t\t\"button\": function( elem ) {\n\t\t\tvar name = elem.nodeName.toLowerCase();\n\t\t\treturn name === \"input\" && elem.type === \"button\" || name === \"button\";\n\t\t},\n\n\t\t\"text\": function( elem ) {\n\t\t\tvar attr;\n\t\t\treturn elem.nodeName.toLowerCase() === \"input\" &&\n\t\t\t\telem.type === \"text\" &&\n\n\t\t\t\t// Support: IE<8\n\t\t\t\t// New HTML5 attribute values (e.g., \"search\") appear with elem.type === \"text\"\n\t\t\t\t( ( attr = elem.getAttribute( \"type\" ) ) == null ||\n\t\t\t\t\tattr.toLowerCase() === \"text\" );\n\t\t},\n\n\t\t// Position-in-collection\n\t\t\"first\": createPositionalPseudo( function() {\n\t\t\treturn [ 0 ];\n\t\t} ),\n\n\t\t\"last\": createPositionalPseudo( function( _matchIndexes, length ) {\n\t\t\treturn [ length - 1 ];\n\t\t} ),\n\n\t\t\"eq\": createPositionalPseudo( function( _matchIndexes, length, argument ) {\n\t\t\treturn [ argument < 0 ? argument + length : argument ];\n\t\t} ),\n\n\t\t\"even\": createPositionalPseudo( function( matchIndexes, length ) {\n\t\t\tvar i = 0;\n\t\t\tfor ( ; i < length; i += 2 ) {\n\t\t\t\tmatchIndexes.push( i );\n\t\t\t}\n\t\t\treturn matchIndexes;\n\t\t} ),\n\n\t\t\"odd\": createPositionalPseudo( function( matchIndexes, length ) {\n\t\t\tvar i = 1;\n\t\t\tfor ( ; i < length; i += 2 ) {\n\t\t\t\tmatchIndexes.push( i );\n\t\t\t}\n\t\t\treturn matchIndexes;\n\t\t} ),\n\n\t\t\"lt\": createPositionalPseudo( function( matchIndexes, length, argument ) {\n\t\t\tvar i = argument < 0 ?\n\t\t\t\targument + length :\n\t\t\t\targument > length ?\n\t\t\t\t\tlength :\n\t\t\t\t\targument;\n\t\t\tfor ( ; --i >= 0; ) {\n\t\t\t\tmatchIndexes.push( i );\n\t\t\t}\n\t\t\treturn matchIndexes;\n\t\t} ),\n\n\t\t\"gt\": createPositionalPseudo( function( matchIndexes, length, argument ) {\n\t\t\tvar i = argument < 0 ? argument + length : argument;\n\t\t\tfor ( ; ++i < length; ) {\n\t\t\t\tmatchIndexes.push( i );\n\t\t\t}\n\t\t\treturn matchIndexes;\n\t\t} )\n\t}\n};\n\nExpr.pseudos[ \"nth\" ] = Expr.pseudos[ \"eq\" ];\n\n// Add button/input type pseudos\nfor ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {\n\tExpr.pseudos[ i ] = createInputPseudo( i );\n}\nfor ( i in { submit: true, reset: true } ) {\n\tExpr.pseudos[ i ] = createButtonPseudo( i );\n}\n\n// Easy API for creating new setFilters\nfunction setFilters() {}\nsetFilters.prototype = Expr.filters = Expr.pseudos;\nExpr.setFilters = new setFilters();\n\ntokenize = Sizzle.tokenize = function( selector, parseOnly ) {\n\tvar matched, match, tokens, type,\n\t\tsoFar, groups, preFilters,\n\t\tcached = tokenCache[ selector + \" \" ];\n\n\tif ( cached ) {\n\t\treturn parseOnly ? 0 : cached.slice( 0 );\n\t}\n\n\tsoFar = selector;\n\tgroups = [];\n\tpreFilters = Expr.preFilter;\n\n\twhile ( soFar ) {\n\n\t\t// Comma and first run\n\t\tif ( !matched || ( match = rcomma.exec( soFar ) ) ) {\n\t\t\tif ( match ) {\n\n\t\t\t\t// Don't consume trailing commas as valid\n\t\t\t\tsoFar = soFar.slice( match[ 0 ].length ) || soFar;\n\t\t\t}\n\t\t\tgroups.push( ( tokens = [] ) );\n\t\t}\n\n\t\tmatched = false;\n\n\t\t// Combinators\n\t\tif ( ( match = rcombinators.exec( soFar ) ) ) {\n\t\t\tmatched = match.shift();\n\t\t\ttokens.push( {\n\t\t\t\tvalue: matched,\n\n\t\t\t\t// Cast descendant combinators to space\n\t\t\t\ttype: match[ 0 ].replace( rtrim, \" \" )\n\t\t\t} );\n\t\t\tsoFar = soFar.slice( matched.length );\n\t\t}\n\n\t\t// Filters\n\t\tfor ( type in Expr.filter ) {\n\t\t\tif ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] ||\n\t\t\t\t( match = preFilters[ type ]( match ) ) ) ) {\n\t\t\t\tmatched = match.shift();\n\t\t\t\ttokens.push( {\n\t\t\t\t\tvalue: matched,\n\t\t\t\t\ttype: type,\n\t\t\t\t\tmatches: match\n\t\t\t\t} );\n\t\t\t\tsoFar = soFar.slice( matched.length );\n\t\t\t}\n\t\t}\n\n\t\tif ( !matched ) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// Return the length of the invalid excess\n\t// if we're just parsing\n\t// Otherwise, throw an error or return tokens\n\treturn parseOnly ?\n\t\tsoFar.length :\n\t\tsoFar ?\n\t\t\tSizzle.error( selector ) :\n\n\t\t\t// Cache the tokens\n\t\t\ttokenCache( selector, groups ).slice( 0 );\n};\n\nfunction toSelector( tokens ) {\n\tvar i = 0,\n\t\tlen = tokens.length,\n\t\tselector = \"\";\n\tfor ( ; i < len; i++ ) {\n\t\tselector += tokens[ i ].value;\n\t}\n\treturn selector;\n}\n\nfunction addCombinator( matcher, combinator, base ) {\n\tvar dir = combinator.dir,\n\t\tskip = combinator.next,\n\t\tkey = skip || dir,\n\t\tcheckNonElements = base && key === \"parentNode\",\n\t\tdoneName = done++;\n\n\treturn combinator.first ?\n\n\t\t// Check against closest ancestor/preceding element\n\t\tfunction( elem, context, xml ) {\n\t\t\twhile ( ( elem = elem[ dir ] ) ) {\n\t\t\t\tif ( elem.nodeType === 1 || checkNonElements ) {\n\t\t\t\t\treturn matcher( elem, context, xml );\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t} :\n\n\t\t// Check against all ancestor/preceding elements\n\t\tfunction( elem, context, xml ) {\n\t\t\tvar oldCache, uniqueCache, outerCache,\n\t\t\t\tnewCache = [ dirruns, doneName ];\n\n\t\t\t// We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching\n\t\t\tif ( xml ) {\n\t\t\t\twhile ( ( elem = elem[ dir ] ) ) {\n\t\t\t\t\tif ( elem.nodeType === 1 || checkNonElements ) {\n\t\t\t\t\t\tif ( matcher( elem, context, xml ) ) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\twhile ( ( elem = elem[ dir ] ) ) {\n\t\t\t\t\tif ( elem.nodeType === 1 || checkNonElements ) {\n\t\t\t\t\t\touterCache = elem[ expando ] || ( elem[ expando ] = {} );\n\n\t\t\t\t\t\t// Support: IE <9 only\n\t\t\t\t\t\t// Defend against cloned attroperties (jQuery gh-1709)\n\t\t\t\t\t\tuniqueCache = outerCache[ elem.uniqueID ] ||\n\t\t\t\t\t\t\t( outerCache[ elem.uniqueID ] = {} );\n\n\t\t\t\t\t\tif ( skip && skip === elem.nodeName.toLowerCase() ) {\n\t\t\t\t\t\t\telem = elem[ dir ] || elem;\n\t\t\t\t\t\t} else if ( ( oldCache = uniqueCache[ key ] ) &&\n\t\t\t\t\t\t\toldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {\n\n\t\t\t\t\t\t\t// Assign to newCache so results back-propagate to previous elements\n\t\t\t\t\t\t\treturn ( newCache[ 2 ] = oldCache[ 2 ] );\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t// Reuse newcache so results back-propagate to previous elements\n\t\t\t\t\t\t\tuniqueCache[ key ] = newCache;\n\n\t\t\t\t\t\t\t// A match means we're done; a fail means we have to keep checking\n\t\t\t\t\t\t\tif ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) {\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t};\n}\n\nfunction elementMatcher( matchers ) {\n\treturn matchers.length > 1 ?\n\t\tfunction( elem, context, xml ) {\n\t\t\tvar i = matchers.length;\n\t\t\twhile ( i-- ) {\n\t\t\t\tif ( !matchers[ i ]( elem, context, xml ) ) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t} :\n\t\tmatchers[ 0 ];\n}\n\nfunction multipleContexts( selector, contexts, results ) {\n\tvar i = 0,\n\t\tlen = contexts.length;\n\tfor ( ; i < len; i++ ) {\n\t\tSizzle( selector, contexts[ i ], results );\n\t}\n\treturn results;\n}\n\nfunction condense( unmatched, map, filter, context, xml ) {\n\tvar elem,\n\t\tnewUnmatched = [],\n\t\ti = 0,\n\t\tlen = unmatched.length,\n\t\tmapped = map != null;\n\n\tfor ( ; i < len; i++ ) {\n\t\tif ( ( elem = unmatched[ i ] ) ) {\n\t\t\tif ( !filter || filter( elem, context, xml ) ) {\n\t\t\t\tnewUnmatched.push( elem );\n\t\t\t\tif ( mapped ) {\n\t\t\t\t\tmap.push( i );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn newUnmatched;\n}\n\nfunction setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {\n\tif ( postFilter && !postFilter[ expando ] ) {\n\t\tpostFilter = setMatcher( postFilter );\n\t}\n\tif ( postFinder && !postFinder[ expando ] ) {\n\t\tpostFinder = setMatcher( postFinder, postSelector );\n\t}\n\treturn markFunction( function( seed, results, context, xml ) {\n\t\tvar temp, i, elem,\n\t\t\tpreMap = [],\n\t\t\tpostMap = [],\n\t\t\tpreexisting = results.length,\n\n\t\t\t// Get initial elements from seed or context\n\t\t\telems = seed || multipleContexts(\n\t\t\t\tselector || \"*\",\n\t\t\t\tcontext.nodeType ? [ context ] : context,\n\t\t\t\t[]\n\t\t\t),\n\n\t\t\t// Prefilter to get matcher input, preserving a map for seed-results synchronization\n\t\t\tmatcherIn = preFilter && ( seed || !selector ) ?\n\t\t\t\tcondense( elems, preMap, preFilter, context, xml ) :\n\t\t\t\telems,\n\n\t\t\tmatcherOut = matcher ?\n\n\t\t\t\t// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,\n\t\t\t\tpostFinder || ( seed ? preFilter : preexisting || postFilter ) ?\n\n\t\t\t\t\t// ...intermediate processing is necessary\n\t\t\t\t\t[] :\n\n\t\t\t\t\t// ...otherwise use results directly\n\t\t\t\t\tresults :\n\t\t\t\tmatcherIn;\n\n\t\t// Find primary matches\n\t\tif ( matcher ) {\n\t\t\tmatcher( matcherIn, matcherOut, context, xml );\n\t\t}\n\n\t\t// Apply postFilter\n\t\tif ( postFilter ) {\n\t\t\ttemp = condense( matcherOut, postMap );\n\t\t\tpostFilter( temp, [], context, xml );\n\n\t\t\t// Un-match failing elements by moving them back to matcherIn\n\t\t\ti = temp.length;\n\t\t\twhile ( i-- ) {\n\t\t\t\tif ( ( elem = temp[ i ] ) ) {\n\t\t\t\t\tmatcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ( seed ) {\n\t\t\tif ( postFinder || preFilter ) {\n\t\t\t\tif ( postFinder ) {\n\n\t\t\t\t\t// Get the final matcherOut by condensing this intermediate into postFinder contexts\n\t\t\t\t\ttemp = [];\n\t\t\t\t\ti = matcherOut.length;\n\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\tif ( ( elem = matcherOut[ i ] ) ) {\n\n\t\t\t\t\t\t\t// Restore matcherIn since elem is not yet a final match\n\t\t\t\t\t\t\ttemp.push( ( matcherIn[ i ] = elem ) );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tpostFinder( null, ( matcherOut = [] ), temp, xml );\n\t\t\t\t}\n\n\t\t\t\t// Move matched elements from seed to results to keep them synchronized\n\t\t\t\ti = matcherOut.length;\n\t\t\t\twhile ( i-- ) {\n\t\t\t\t\tif ( ( elem = matcherOut[ i ] ) &&\n\t\t\t\t\t\t( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) {\n\n\t\t\t\t\t\tseed[ temp ] = !( results[ temp ] = elem );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t// Add elements to results, through postFinder if defined\n\t\t} else {\n\t\t\tmatcherOut = condense(\n\t\t\t\tmatcherOut === results ?\n\t\t\t\t\tmatcherOut.splice( preexisting, matcherOut.length ) :\n\t\t\t\t\tmatcherOut\n\t\t\t);\n\t\t\tif ( postFinder ) {\n\t\t\t\tpostFinder( null, results, matcherOut, xml );\n\t\t\t} else {\n\t\t\t\tpush.apply( results, matcherOut );\n\t\t\t}\n\t\t}\n\t} );\n}\n\nfunction matcherFromTokens( tokens ) {\n\tvar checkContext, matcher, j,\n\t\tlen = tokens.length,\n\t\tleadingRelative = Expr.relative[ tokens[ 0 ].type ],\n\t\timplicitRelative = leadingRelative || Expr.relative[ \" \" ],\n\t\ti = leadingRelative ? 1 : 0,\n\n\t\t// The foundational matcher ensures that elements are reachable from top-level context(s)\n\t\tmatchContext = addCombinator( function( elem ) {\n\t\t\treturn elem === checkContext;\n\t\t}, implicitRelative, true ),\n\t\tmatchAnyContext = addCombinator( function( elem ) {\n\t\t\treturn indexOf( checkContext, elem ) > -1;\n\t\t}, implicitRelative, true ),\n\t\tmatchers = [ function( elem, context, xml ) {\n\t\t\tvar ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (\n\t\t\t\t( checkContext = context ).nodeType ?\n\t\t\t\t\tmatchContext( elem, context, xml ) :\n\t\t\t\t\tmatchAnyContext( elem, context, xml ) );\n\n\t\t\t// Avoid hanging onto element (issue #299)\n\t\t\tcheckContext = null;\n\t\t\treturn ret;\n\t\t} ];\n\n\tfor ( ; i < len; i++ ) {\n\t\tif ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) {\n\t\t\tmatchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];\n\t\t} else {\n\t\t\tmatcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches );\n\n\t\t\t// Return special upon seeing a positional matcher\n\t\t\tif ( matcher[ expando ] ) {\n\n\t\t\t\t// Find the next relative operator (if any) for proper handling\n\t\t\t\tj = ++i;\n\t\t\t\tfor ( ; j < len; j++ ) {\n\t\t\t\t\tif ( Expr.relative[ tokens[ j ].type ] ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn setMatcher(\n\t\t\t\t\ti > 1 && elementMatcher( matchers ),\n\t\t\t\t\ti > 1 && toSelector(\n\n\t\t\t\t\t// If the preceding token was a descendant combinator, insert an implicit any-element `*`\n\t\t\t\t\ttokens\n\t\t\t\t\t\t.slice( 0, i - 1 )\n\t\t\t\t\t\t.concat( { value: tokens[ i - 2 ].type === \" \" ? \"*\" : \"\" } )\n\t\t\t\t\t).replace( rtrim, \"$1\" ),\n\t\t\t\t\tmatcher,\n\t\t\t\t\ti < j && matcherFromTokens( tokens.slice( i, j ) ),\n\t\t\t\t\tj < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ),\n\t\t\t\t\tj < len && toSelector( tokens )\n\t\t\t\t);\n\t\t\t}\n\t\t\tmatchers.push( matcher );\n\t\t}\n\t}\n\n\treturn elementMatcher( matchers );\n}\n\nfunction matcherFromGroupMatchers( elementMatchers, setMatchers ) {\n\tvar bySet = setMatchers.length > 0,\n\t\tbyElement = elementMatchers.length > 0,\n\t\tsuperMatcher = function( seed, context, xml, results, outermost ) {\n\t\t\tvar elem, j, matcher,\n\t\t\t\tmatchedCount = 0,\n\t\t\t\ti = \"0\",\n\t\t\t\tunmatched = seed && [],\n\t\t\t\tsetMatched = [],\n\t\t\t\tcontextBackup = outermostContext,\n\n\t\t\t\t// We must always have either seed elements or outermost context\n\t\t\t\telems = seed || byElement && Expr.find[ \"TAG\" ]( \"*\", outermost ),\n\n\t\t\t\t// Use integer dirruns iff this is the outermost matcher\n\t\t\t\tdirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ),\n\t\t\t\tlen = elems.length;\n\n\t\t\tif ( outermost ) {\n\n\t\t\t\t// Support: IE 11+, Edge 17 - 18+\n\t\t\t\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t\t\t\t// two documents; shallow comparisons work.\n\t\t\t\t// eslint-disable-next-line eqeqeq\n\t\t\t\toutermostContext = context == document || context || outermost;\n\t\t\t}\n\n\t\t\t// Add elements passing elementMatchers directly to results\n\t\t\t// Support: IE<9, Safari\n\t\t\t// Tolerate NodeList properties (IE: \"length\"; Safari: ) matching elements by id\n\t\t\tfor ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) {\n\t\t\t\tif ( byElement && elem ) {\n\t\t\t\t\tj = 0;\n\n\t\t\t\t\t// Support: IE 11+, Edge 17 - 18+\n\t\t\t\t\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t\t\t\t\t// two documents; shallow comparisons work.\n\t\t\t\t\t// eslint-disable-next-line eqeqeq\n\t\t\t\t\tif ( !context && elem.ownerDocument != document ) {\n\t\t\t\t\t\tsetDocument( elem );\n\t\t\t\t\t\txml = !documentIsHTML;\n\t\t\t\t\t}\n\t\t\t\t\twhile ( ( matcher = elementMatchers[ j++ ] ) ) {\n\t\t\t\t\t\tif ( matcher( elem, context || document, xml ) ) {\n\t\t\t\t\t\t\tresults.push( elem );\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif ( outermost ) {\n\t\t\t\t\t\tdirruns = dirrunsUnique;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Track unmatched elements for set filters\n\t\t\t\tif ( bySet ) {\n\n\t\t\t\t\t// They will have gone through all possible matchers\n\t\t\t\t\tif ( ( elem = !matcher && elem ) ) {\n\t\t\t\t\t\tmatchedCount--;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Lengthen the array for every element, matched or not\n\t\t\t\t\tif ( seed ) {\n\t\t\t\t\t\tunmatched.push( elem );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// `i` is now the count of elements visited above, and adding it to `matchedCount`\n\t\t\t// makes the latter nonnegative.\n\t\t\tmatchedCount += i;\n\n\t\t\t// Apply set filters to unmatched elements\n\t\t\t// NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`\n\t\t\t// equals `i`), unless we didn't visit _any_ elements in the above loop because we have\n\t\t\t// no element matchers and no seed.\n\t\t\t// Incrementing an initially-string \"0\" `i` allows `i` to remain a string only in that\n\t\t\t// case, which will result in a \"00\" `matchedCount` that differs from `i` but is also\n\t\t\t// numerically zero.\n\t\t\tif ( bySet && i !== matchedCount ) {\n\t\t\t\tj = 0;\n\t\t\t\twhile ( ( matcher = setMatchers[ j++ ] ) ) {\n\t\t\t\t\tmatcher( unmatched, setMatched, context, xml );\n\t\t\t\t}\n\n\t\t\t\tif ( seed ) {\n\n\t\t\t\t\t// Reintegrate element matches to eliminate the need for sorting\n\t\t\t\t\tif ( matchedCount > 0 ) {\n\t\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\t\tif ( !( unmatched[ i ] || setMatched[ i ] ) ) {\n\t\t\t\t\t\t\t\tsetMatched[ i ] = pop.call( results );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Discard index placeholder values to get only actual matches\n\t\t\t\t\tsetMatched = condense( setMatched );\n\t\t\t\t}\n\n\t\t\t\t// Add matches to results\n\t\t\t\tpush.apply( results, setMatched );\n\n\t\t\t\t// Seedless set matches succeeding multiple successful matchers stipulate sorting\n\t\t\t\tif ( outermost && !seed && setMatched.length > 0 &&\n\t\t\t\t\t( matchedCount + setMatchers.length ) > 1 ) {\n\n\t\t\t\t\tSizzle.uniqueSort( results );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Override manipulation of globals by nested matchers\n\t\t\tif ( outermost ) {\n\t\t\t\tdirruns = dirrunsUnique;\n\t\t\t\toutermostContext = contextBackup;\n\t\t\t}\n\n\t\t\treturn unmatched;\n\t\t};\n\n\treturn bySet ?\n\t\tmarkFunction( superMatcher ) :\n\t\tsuperMatcher;\n}\n\ncompile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {\n\tvar i,\n\t\tsetMatchers = [],\n\t\telementMatchers = [],\n\t\tcached = compilerCache[ selector + \" \" ];\n\n\tif ( !cached ) {\n\n\t\t// Generate a function of recursive functions that can be used to check each element\n\t\tif ( !match ) {\n\t\t\tmatch = tokenize( selector );\n\t\t}\n\t\ti = match.length;\n\t\twhile ( i-- ) {\n\t\t\tcached = matcherFromTokens( match[ i ] );\n\t\t\tif ( cached[ expando ] ) {\n\t\t\t\tsetMatchers.push( cached );\n\t\t\t} else {\n\t\t\t\telementMatchers.push( cached );\n\t\t\t}\n\t\t}\n\n\t\t// Cache the compiled function\n\t\tcached = compilerCache(\n\t\t\tselector,\n\t\t\tmatcherFromGroupMatchers( elementMatchers, setMatchers )\n\t\t);\n\n\t\t// Save selector and tokenization\n\t\tcached.selector = selector;\n\t}\n\treturn cached;\n};\n\n/**\n * A low-level selection function that works with Sizzle's compiled\n * selector functions\n * @param {String|Function} selector A selector or a pre-compiled\n * selector function built with Sizzle.compile\n * @param {Element} context\n * @param {Array} [results]\n * @param {Array} [seed] A set of elements to match against\n */\nselect = Sizzle.select = function( selector, context, results, seed ) {\n\tvar i, tokens, token, type, find,\n\t\tcompiled = typeof selector === \"function\" && selector,\n\t\tmatch = !seed && tokenize( ( selector = compiled.selector || selector ) );\n\n\tresults = results || [];\n\n\t// Try to minimize operations if there is only one selector in the list and no seed\n\t// (the latter of which guarantees us context)\n\tif ( match.length === 1 ) {\n\n\t\t// Reduce context if the leading compound selector is an ID\n\t\ttokens = match[ 0 ] = match[ 0 ].slice( 0 );\n\t\tif ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === \"ID\" &&\n\t\t\tcontext.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) {\n\n\t\t\tcontext = ( Expr.find[ \"ID\" ]( token.matches[ 0 ]\n\t\t\t\t.replace( runescape, funescape ), context ) || [] )[ 0 ];\n\t\t\tif ( !context ) {\n\t\t\t\treturn results;\n\n\t\t\t// Precompiled matchers will still verify ancestry, so step up a level\n\t\t\t} else if ( compiled ) {\n\t\t\t\tcontext = context.parentNode;\n\t\t\t}\n\n\t\t\tselector = selector.slice( tokens.shift().value.length );\n\t\t}\n\n\t\t// Fetch a seed set for right-to-left matching\n\t\ti = matchExpr[ \"needsContext\" ].test( selector ) ? 0 : tokens.length;\n\t\twhile ( i-- ) {\n\t\t\ttoken = tokens[ i ];\n\n\t\t\t// Abort if we hit a combinator\n\t\t\tif ( Expr.relative[ ( type = token.type ) ] ) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif ( ( find = Expr.find[ type ] ) ) {\n\n\t\t\t\t// Search, expanding context for leading sibling combinators\n\t\t\t\tif ( ( seed = find(\n\t\t\t\t\ttoken.matches[ 0 ].replace( runescape, funescape ),\n\t\t\t\t\trsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) ||\n\t\t\t\t\t\tcontext\n\t\t\t\t) ) ) {\n\n\t\t\t\t\t// If seed is empty or no tokens remain, we can return early\n\t\t\t\t\ttokens.splice( i, 1 );\n\t\t\t\t\tselector = seed.length && toSelector( tokens );\n\t\t\t\t\tif ( !selector ) {\n\t\t\t\t\t\tpush.apply( results, seed );\n\t\t\t\t\t\treturn results;\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Compile and execute a filtering function if one is not provided\n\t// Provide `match` to avoid retokenization if we modified the selector above\n\t( compiled || compile( selector, match ) )(\n\t\tseed,\n\t\tcontext,\n\t\t!documentIsHTML,\n\t\tresults,\n\t\t!context || rsibling.test( selector ) && testContext( context.parentNode ) || context\n\t);\n\treturn results;\n};\n\n// One-time assignments\n\n// Sort stability\nsupport.sortStable = expando.split( \"\" ).sort( sortOrder ).join( \"\" ) === expando;\n\n// Support: Chrome 14-35+\n// Always assume duplicates if they aren't passed to the comparison function\nsupport.detectDuplicates = !!hasDuplicate;\n\n// Initialize against the default document\nsetDocument();\n\n// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)\n// Detached nodes confoundingly follow *each other*\nsupport.sortDetached = assert( function( el ) {\n\n\t// Should return 1, but returns 4 (following)\n\treturn el.compareDocumentPosition( document.createElement( \"fieldset\" ) ) & 1;\n} );\n\n// Support: IE<8\n// Prevent attribute/property \"interpolation\"\n// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx\nif ( !assert( function( el ) {\n\tel.innerHTML = \"\";\n\treturn el.firstChild.getAttribute( \"href\" ) === \"#\";\n} ) ) {\n\taddHandle( \"type|href|height|width\", function( elem, name, isXML ) {\n\t\tif ( !isXML ) {\n\t\t\treturn elem.getAttribute( name, name.toLowerCase() === \"type\" ? 1 : 2 );\n\t\t}\n\t} );\n}\n\n// Support: IE<9\n// Use defaultValue in place of getAttribute(\"value\")\nif ( !support.attributes || !assert( function( el ) {\n\tel.innerHTML = \"\";\n\tel.firstChild.setAttribute( \"value\", \"\" );\n\treturn el.firstChild.getAttribute( \"value\" ) === \"\";\n} ) ) {\n\taddHandle( \"value\", function( elem, _name, isXML ) {\n\t\tif ( !isXML && elem.nodeName.toLowerCase() === \"input\" ) {\n\t\t\treturn elem.defaultValue;\n\t\t}\n\t} );\n}\n\n// Support: IE<9\n// Use getAttributeNode to fetch booleans when getAttribute lies\nif ( !assert( function( el ) {\n\treturn el.getAttribute( \"disabled\" ) == null;\n} ) ) {\n\taddHandle( booleans, function( elem, name, isXML ) {\n\t\tvar val;\n\t\tif ( !isXML ) {\n\t\t\treturn elem[ name ] === true ? name.toLowerCase() :\n\t\t\t\t( val = elem.getAttributeNode( name ) ) && val.specified ?\n\t\t\t\t\tval.value :\n\t\t\t\t\tnull;\n\t\t}\n\t} );\n}\n\nreturn Sizzle;\n\n} )( window );\n\n\n\njQuery.find = Sizzle;\njQuery.expr = Sizzle.selectors;\n\n// Deprecated\njQuery.expr[ \":\" ] = jQuery.expr.pseudos;\njQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;\njQuery.text = Sizzle.getText;\njQuery.isXMLDoc = Sizzle.isXML;\njQuery.contains = Sizzle.contains;\njQuery.escapeSelector = Sizzle.escape;\n\n\n\n\nvar dir = function( elem, dir, until ) {\n\tvar matched = [],\n\t\ttruncate = until !== undefined;\n\n\twhile ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {\n\t\tif ( elem.nodeType === 1 ) {\n\t\t\tif ( truncate && jQuery( elem ).is( until ) ) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tmatched.push( elem );\n\t\t}\n\t}\n\treturn matched;\n};\n\n\nvar siblings = function( n, elem ) {\n\tvar matched = [];\n\n\tfor ( ; n; n = n.nextSibling ) {\n\t\tif ( n.nodeType === 1 && n !== elem ) {\n\t\t\tmatched.push( n );\n\t\t}\n\t}\n\n\treturn matched;\n};\n\n\nvar rneedsContext = jQuery.expr.match.needsContext;\n\n\n\nfunction nodeName( elem, name ) {\n\n return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();\n\n};\nvar rsingleTag = ( /^<([a-z][^\\/\\0>:\\x20\\t\\r\\n\\f]*)[\\x20\\t\\r\\n\\f]*\\/?>(?:<\\/\\1>|)$/i );\n\n\n\n// Implement the identical functionality for filter and not\nfunction winnow( elements, qualifier, not ) {\n\tif ( isFunction( qualifier ) ) {\n\t\treturn jQuery.grep( elements, function( elem, i ) {\n\t\t\treturn !!qualifier.call( elem, i, elem ) !== not;\n\t\t} );\n\t}\n\n\t// Single element\n\tif ( qualifier.nodeType ) {\n\t\treturn jQuery.grep( elements, function( elem ) {\n\t\t\treturn ( elem === qualifier ) !== not;\n\t\t} );\n\t}\n\n\t// Arraylike of elements (jQuery, arguments, Array)\n\tif ( typeof qualifier !== \"string\" ) {\n\t\treturn jQuery.grep( elements, function( elem ) {\n\t\t\treturn ( indexOf.call( qualifier, elem ) > -1 ) !== not;\n\t\t} );\n\t}\n\n\t// Filtered directly for both simple and complex selectors\n\treturn jQuery.filter( qualifier, elements, not );\n}\n\njQuery.filter = function( expr, elems, not ) {\n\tvar elem = elems[ 0 ];\n\n\tif ( not ) {\n\t\texpr = \":not(\" + expr + \")\";\n\t}\n\n\tif ( elems.length === 1 && elem.nodeType === 1 ) {\n\t\treturn jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [];\n\t}\n\n\treturn jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {\n\t\treturn elem.nodeType === 1;\n\t} ) );\n};\n\njQuery.fn.extend( {\n\tfind: function( selector ) {\n\t\tvar i, ret,\n\t\t\tlen = this.length,\n\t\t\tself = this;\n\n\t\tif ( typeof selector !== \"string\" ) {\n\t\t\treturn this.pushStack( jQuery( selector ).filter( function() {\n\t\t\t\tfor ( i = 0; i < len; i++ ) {\n\t\t\t\t\tif ( jQuery.contains( self[ i ], this ) ) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} ) );\n\t\t}\n\n\t\tret = this.pushStack( [] );\n\n\t\tfor ( i = 0; i < len; i++ ) {\n\t\t\tjQuery.find( selector, self[ i ], ret );\n\t\t}\n\n\t\treturn len > 1 ? jQuery.uniqueSort( ret ) : ret;\n\t},\n\tfilter: function( selector ) {\n\t\treturn this.pushStack( winnow( this, selector || [], false ) );\n\t},\n\tnot: function( selector ) {\n\t\treturn this.pushStack( winnow( this, selector || [], true ) );\n\t},\n\tis: function( selector ) {\n\t\treturn !!winnow(\n\t\t\tthis,\n\n\t\t\t// If this is a positional/relative selector, check membership in the returned set\n\t\t\t// so $(\"p:first\").is(\"p:last\") won't return true for a doc with two \"p\".\n\t\t\ttypeof selector === \"string\" && rneedsContext.test( selector ) ?\n\t\t\t\tjQuery( selector ) :\n\t\t\t\tselector || [],\n\t\t\tfalse\n\t\t).length;\n\t}\n} );\n\n\n// Initialize a jQuery object\n\n\n// A central reference to the root jQuery(document)\nvar rootjQuery,\n\n\t// A simple way to check for HTML strings\n\t// Prioritize #id over to avoid XSS via location.hash (#9521)\n\t// Strict HTML recognition (#11290: must start with <)\n\t// Shortcut simple #id case for speed\n\trquickExpr = /^(?:\\s*(<[\\w\\W]+>)[^>]*|#([\\w-]+))$/,\n\n\tinit = jQuery.fn.init = function( selector, context, root ) {\n\t\tvar match, elem;\n\n\t\t// HANDLE: $(\"\"), $(null), $(undefined), $(false)\n\t\tif ( !selector ) {\n\t\t\treturn this;\n\t\t}\n\n\t\t// Method init() accepts an alternate rootjQuery\n\t\t// so migrate can support jQuery.sub (gh-2101)\n\t\troot = root || rootjQuery;\n\n\t\t// Handle HTML strings\n\t\tif ( typeof selector === \"string\" ) {\n\t\t\tif ( selector[ 0 ] === \"<\" &&\n\t\t\t\tselector[ selector.length - 1 ] === \">\" &&\n\t\t\t\tselector.length >= 3 ) {\n\n\t\t\t\t// Assume that strings that start and end with <> are HTML and skip the regex check\n\t\t\t\tmatch = [ null, selector, null ];\n\n\t\t\t} else {\n\t\t\t\tmatch = rquickExpr.exec( selector );\n\t\t\t}\n\n\t\t\t// Match html or make sure no context is specified for #id\n\t\t\tif ( match && ( match[ 1 ] || !context ) ) {\n\n\t\t\t\t// HANDLE: $(html) -> $(array)\n\t\t\t\tif ( match[ 1 ] ) {\n\t\t\t\t\tcontext = context instanceof jQuery ? context[ 0 ] : context;\n\n\t\t\t\t\t// Option to run scripts is true for back-compat\n\t\t\t\t\t// Intentionally let the error be thrown if parseHTML is not present\n\t\t\t\t\tjQuery.merge( this, jQuery.parseHTML(\n\t\t\t\t\t\tmatch[ 1 ],\n\t\t\t\t\t\tcontext && context.nodeType ? context.ownerDocument || context : document,\n\t\t\t\t\t\ttrue\n\t\t\t\t\t) );\n\n\t\t\t\t\t// HANDLE: $(html, props)\n\t\t\t\t\tif ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {\n\t\t\t\t\t\tfor ( match in context ) {\n\n\t\t\t\t\t\t\t// Properties of context are called as methods if possible\n\t\t\t\t\t\t\tif ( isFunction( this[ match ] ) ) {\n\t\t\t\t\t\t\t\tthis[ match ]( context[ match ] );\n\n\t\t\t\t\t\t\t// ...and otherwise set as attributes\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthis.attr( match, context[ match ] );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn this;\n\n\t\t\t\t// HANDLE: $(#id)\n\t\t\t\t} else {\n\t\t\t\t\telem = document.getElementById( match[ 2 ] );\n\n\t\t\t\t\tif ( elem ) {\n\n\t\t\t\t\t\t// Inject the element directly into the jQuery object\n\t\t\t\t\t\tthis[ 0 ] = elem;\n\t\t\t\t\t\tthis.length = 1;\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\n\t\t\t// HANDLE: $(expr, $(...))\n\t\t\t} else if ( !context || context.jquery ) {\n\t\t\t\treturn ( context || root ).find( selector );\n\n\t\t\t// HANDLE: $(expr, context)\n\t\t\t// (which is just equivalent to: $(context).find(expr)\n\t\t\t} else {\n\t\t\t\treturn this.constructor( context ).find( selector );\n\t\t\t}\n\n\t\t// HANDLE: $(DOMElement)\n\t\t} else if ( selector.nodeType ) {\n\t\t\tthis[ 0 ] = selector;\n\t\t\tthis.length = 1;\n\t\t\treturn this;\n\n\t\t// HANDLE: $(function)\n\t\t// Shortcut for document ready\n\t\t} else if ( isFunction( selector ) ) {\n\t\t\treturn root.ready !== undefined ?\n\t\t\t\troot.ready( selector ) :\n\n\t\t\t\t// Execute immediately if ready is not present\n\t\t\t\tselector( jQuery );\n\t\t}\n\n\t\treturn jQuery.makeArray( selector, this );\n\t};\n\n// Give the init function the jQuery prototype for later instantiation\ninit.prototype = jQuery.fn;\n\n// Initialize central reference\nrootjQuery = jQuery( document );\n\n\nvar rparentsprev = /^(?:parents|prev(?:Until|All))/,\n\n\t// Methods guaranteed to produce a unique set when starting from a unique set\n\tguaranteedUnique = {\n\t\tchildren: true,\n\t\tcontents: true,\n\t\tnext: true,\n\t\tprev: true\n\t};\n\njQuery.fn.extend( {\n\thas: function( target ) {\n\t\tvar targets = jQuery( target, this ),\n\t\t\tl = targets.length;\n\n\t\treturn this.filter( function() {\n\t\t\tvar i = 0;\n\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\tif ( jQuery.contains( this, targets[ i ] ) ) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t},\n\n\tclosest: function( selectors, context ) {\n\t\tvar cur,\n\t\t\ti = 0,\n\t\t\tl = this.length,\n\t\t\tmatched = [],\n\t\t\ttargets = typeof selectors !== \"string\" && jQuery( selectors );\n\n\t\t// Positional selectors never match, since there's no _selection_ context\n\t\tif ( !rneedsContext.test( selectors ) ) {\n\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\tfor ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {\n\n\t\t\t\t\t// Always skip document fragments\n\t\t\t\t\tif ( cur.nodeType < 11 && ( targets ?\n\t\t\t\t\t\ttargets.index( cur ) > -1 :\n\n\t\t\t\t\t\t// Don't pass non-elements to Sizzle\n\t\t\t\t\t\tcur.nodeType === 1 &&\n\t\t\t\t\t\t\tjQuery.find.matchesSelector( cur, selectors ) ) ) {\n\n\t\t\t\t\t\tmatched.push( cur );\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );\n\t},\n\n\t// Determine the position of an element within the set\n\tindex: function( elem ) {\n\n\t\t// No argument, return index in parent\n\t\tif ( !elem ) {\n\t\t\treturn ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;\n\t\t}\n\n\t\t// Index in selector\n\t\tif ( typeof elem === \"string\" ) {\n\t\t\treturn indexOf.call( jQuery( elem ), this[ 0 ] );\n\t\t}\n\n\t\t// Locate the position of the desired element\n\t\treturn indexOf.call( this,\n\n\t\t\t// If it receives a jQuery object, the first element is used\n\t\t\telem.jquery ? elem[ 0 ] : elem\n\t\t);\n\t},\n\n\tadd: function( selector, context ) {\n\t\treturn this.pushStack(\n\t\t\tjQuery.uniqueSort(\n\t\t\t\tjQuery.merge( this.get(), jQuery( selector, context ) )\n\t\t\t)\n\t\t);\n\t},\n\n\taddBack: function( selector ) {\n\t\treturn this.add( selector == null ?\n\t\t\tthis.prevObject : this.prevObject.filter( selector )\n\t\t);\n\t}\n} );\n\nfunction sibling( cur, dir ) {\n\twhile ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}\n\treturn cur;\n}\n\njQuery.each( {\n\tparent: function( elem ) {\n\t\tvar parent = elem.parentNode;\n\t\treturn parent && parent.nodeType !== 11 ? parent : null;\n\t},\n\tparents: function( elem ) {\n\t\treturn dir( elem, \"parentNode\" );\n\t},\n\tparentsUntil: function( elem, _i, until ) {\n\t\treturn dir( elem, \"parentNode\", until );\n\t},\n\tnext: function( elem ) {\n\t\treturn sibling( elem, \"nextSibling\" );\n\t},\n\tprev: function( elem ) {\n\t\treturn sibling( elem, \"previousSibling\" );\n\t},\n\tnextAll: function( elem ) {\n\t\treturn dir( elem, \"nextSibling\" );\n\t},\n\tprevAll: function( elem ) {\n\t\treturn dir( elem, \"previousSibling\" );\n\t},\n\tnextUntil: function( elem, _i, until ) {\n\t\treturn dir( elem, \"nextSibling\", until );\n\t},\n\tprevUntil: function( elem, _i, until ) {\n\t\treturn dir( elem, \"previousSibling\", until );\n\t},\n\tsiblings: function( elem ) {\n\t\treturn siblings( ( elem.parentNode || {} ).firstChild, elem );\n\t},\n\tchildren: function( elem ) {\n\t\treturn siblings( elem.firstChild );\n\t},\n\tcontents: function( elem ) {\n\t\tif ( elem.contentDocument != null &&\n\n\t\t\t// Support: IE 11+\n\t\t\t// elements with no `data` attribute has an object\n\t\t\t// `contentDocument` with a `null` prototype.\n\t\t\tgetProto( elem.contentDocument ) ) {\n\n\t\t\treturn elem.contentDocument;\n\t\t}\n\n\t\t// Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only\n\t\t// Treat the template element as a regular one in browsers that\n\t\t// don't support it.\n\t\tif ( nodeName( elem, \"template\" ) ) {\n\t\t\telem = elem.content || elem;\n\t\t}\n\n\t\treturn jQuery.merge( [], elem.childNodes );\n\t}\n}, function( name, fn ) {\n\tjQuery.fn[ name ] = function( until, selector ) {\n\t\tvar matched = jQuery.map( this, fn, until );\n\n\t\tif ( name.slice( -5 ) !== \"Until\" ) {\n\t\t\tselector = until;\n\t\t}\n\n\t\tif ( selector && typeof selector === \"string\" ) {\n\t\t\tmatched = jQuery.filter( selector, matched );\n\t\t}\n\n\t\tif ( this.length > 1 ) {\n\n\t\t\t// Remove duplicates\n\t\t\tif ( !guaranteedUnique[ name ] ) {\n\t\t\t\tjQuery.uniqueSort( matched );\n\t\t\t}\n\n\t\t\t// Reverse order for parents* and prev-derivatives\n\t\t\tif ( rparentsprev.test( name ) ) {\n\t\t\t\tmatched.reverse();\n\t\t\t}\n\t\t}\n\n\t\treturn this.pushStack( matched );\n\t};\n} );\nvar rnothtmlwhite = ( /[^\\x20\\t\\r\\n\\f]+/g );\n\n\n\n// Convert String-formatted options into Object-formatted ones\nfunction createOptions( options ) {\n\tvar object = {};\n\tjQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) {\n\t\tobject[ flag ] = true;\n\t} );\n\treturn object;\n}\n\n/*\n * Create a callback list using the following parameters:\n *\n *\toptions: an optional list of space-separated options that will change how\n *\t\t\tthe callback list behaves or a more traditional option object\n *\n * By default a callback list will act like an event callback list and can be\n * \"fired\" multiple times.\n *\n * Possible options:\n *\n *\tonce:\t\t\twill ensure the callback list can only be fired once (like a Deferred)\n *\n *\tmemory:\t\t\twill keep track of previous values and will call any callback added\n *\t\t\t\t\tafter the list has been fired right away with the latest \"memorized\"\n *\t\t\t\t\tvalues (like a Deferred)\n *\n *\tunique:\t\t\twill ensure a callback can only be added once (no duplicate in the list)\n *\n *\tstopOnFalse:\tinterrupt callings when a callback returns false\n *\n */\njQuery.Callbacks = function( options ) {\n\n\t// Convert options from String-formatted to Object-formatted if needed\n\t// (we check in cache first)\n\toptions = typeof options === \"string\" ?\n\t\tcreateOptions( options ) :\n\t\tjQuery.extend( {}, options );\n\n\tvar // Flag to know if list is currently firing\n\t\tfiring,\n\n\t\t// Last fire value for non-forgettable lists\n\t\tmemory,\n\n\t\t// Flag to know if list was already fired\n\t\tfired,\n\n\t\t// Flag to prevent firing\n\t\tlocked,\n\n\t\t// Actual callback list\n\t\tlist = [],\n\n\t\t// Queue of execution data for repeatable lists\n\t\tqueue = [],\n\n\t\t// Index of currently firing callback (modified by add/remove as needed)\n\t\tfiringIndex = -1,\n\n\t\t// Fire callbacks\n\t\tfire = function() {\n\n\t\t\t// Enforce single-firing\n\t\t\tlocked = locked || options.once;\n\n\t\t\t// Execute callbacks for all pending executions,\n\t\t\t// respecting firingIndex overrides and runtime changes\n\t\t\tfired = firing = true;\n\t\t\tfor ( ; queue.length; firingIndex = -1 ) {\n\t\t\t\tmemory = queue.shift();\n\t\t\t\twhile ( ++firingIndex < list.length ) {\n\n\t\t\t\t\t// Run callback and check for early termination\n\t\t\t\t\tif ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&\n\t\t\t\t\t\toptions.stopOnFalse ) {\n\n\t\t\t\t\t\t// Jump to end and forget the data so .add doesn't re-fire\n\t\t\t\t\t\tfiringIndex = list.length;\n\t\t\t\t\t\tmemory = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Forget the data if we're done with it\n\t\t\tif ( !options.memory ) {\n\t\t\t\tmemory = false;\n\t\t\t}\n\n\t\t\tfiring = false;\n\n\t\t\t// Clean up if we're done firing for good\n\t\t\tif ( locked ) {\n\n\t\t\t\t// Keep an empty list if we have data for future add calls\n\t\t\t\tif ( memory ) {\n\t\t\t\t\tlist = [];\n\n\t\t\t\t// Otherwise, this object is spent\n\t\t\t\t} else {\n\t\t\t\t\tlist = \"\";\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t// Actual Callbacks object\n\t\tself = {\n\n\t\t\t// Add a callback or a collection of callbacks to the list\n\t\t\tadd: function() {\n\t\t\t\tif ( list ) {\n\n\t\t\t\t\t// If we have memory from a past run, we should fire after adding\n\t\t\t\t\tif ( memory && !firing ) {\n\t\t\t\t\t\tfiringIndex = list.length - 1;\n\t\t\t\t\t\tqueue.push( memory );\n\t\t\t\t\t}\n\n\t\t\t\t\t( function add( args ) {\n\t\t\t\t\t\tjQuery.each( args, function( _, arg ) {\n\t\t\t\t\t\t\tif ( isFunction( arg ) ) {\n\t\t\t\t\t\t\t\tif ( !options.unique || !self.has( arg ) ) {\n\t\t\t\t\t\t\t\t\tlist.push( arg );\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else if ( arg && arg.length && toType( arg ) !== \"string\" ) {\n\n\t\t\t\t\t\t\t\t// Inspect recursively\n\t\t\t\t\t\t\t\tadd( arg );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} );\n\t\t\t\t\t} )( arguments );\n\n\t\t\t\t\tif ( memory && !firing ) {\n\t\t\t\t\t\tfire();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// Remove a callback from the list\n\t\t\tremove: function() {\n\t\t\t\tjQuery.each( arguments, function( _, arg ) {\n\t\t\t\t\tvar index;\n\t\t\t\t\twhile ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {\n\t\t\t\t\t\tlist.splice( index, 1 );\n\n\t\t\t\t\t\t// Handle firing indexes\n\t\t\t\t\t\tif ( index <= firingIndex ) {\n\t\t\t\t\t\t\tfiringIndex--;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// Check if a given callback is in the list.\n\t\t\t// If no argument is given, return whether or not list has callbacks attached.\n\t\t\thas: function( fn ) {\n\t\t\t\treturn fn ?\n\t\t\t\t\tjQuery.inArray( fn, list ) > -1 :\n\t\t\t\t\tlist.length > 0;\n\t\t\t},\n\n\t\t\t// Remove all callbacks from the list\n\t\t\tempty: function() {\n\t\t\t\tif ( list ) {\n\t\t\t\t\tlist = [];\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// Disable .fire and .add\n\t\t\t// Abort any current/pending executions\n\t\t\t// Clear all callbacks and values\n\t\t\tdisable: function() {\n\t\t\t\tlocked = queue = [];\n\t\t\t\tlist = memory = \"\";\n\t\t\t\treturn this;\n\t\t\t},\n\t\t\tdisabled: function() {\n\t\t\t\treturn !list;\n\t\t\t},\n\n\t\t\t// Disable .fire\n\t\t\t// Also disable .add unless we have memory (since it would have no effect)\n\t\t\t// Abort any pending executions\n\t\t\tlock: function() {\n\t\t\t\tlocked = queue = [];\n\t\t\t\tif ( !memory && !firing ) {\n\t\t\t\t\tlist = memory = \"\";\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\t\t\tlocked: function() {\n\t\t\t\treturn !!locked;\n\t\t\t},\n\n\t\t\t// Call all callbacks with the given context and arguments\n\t\t\tfireWith: function( context, args ) {\n\t\t\t\tif ( !locked ) {\n\t\t\t\t\targs = args || [];\n\t\t\t\t\targs = [ context, args.slice ? args.slice() : args ];\n\t\t\t\t\tqueue.push( args );\n\t\t\t\t\tif ( !firing ) {\n\t\t\t\t\t\tfire();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// Call all the callbacks with the given arguments\n\t\t\tfire: function() {\n\t\t\t\tself.fireWith( this, arguments );\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// To know if the callbacks have already been called at least once\n\t\t\tfired: function() {\n\t\t\t\treturn !!fired;\n\t\t\t}\n\t\t};\n\n\treturn self;\n};\n\n\nfunction Identity( v ) {\n\treturn v;\n}\nfunction Thrower( ex ) {\n\tthrow ex;\n}\n\nfunction adoptValue( value, resolve, reject, noValue ) {\n\tvar method;\n\n\ttry {\n\n\t\t// Check for promise aspect first to privilege synchronous behavior\n\t\tif ( value && isFunction( ( method = value.promise ) ) ) {\n\t\t\tmethod.call( value ).done( resolve ).fail( reject );\n\n\t\t// Other thenables\n\t\t} else if ( value && isFunction( ( method = value.then ) ) ) {\n\t\t\tmethod.call( value, resolve, reject );\n\n\t\t// Other non-thenables\n\t\t} else {\n\n\t\t\t// Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer:\n\t\t\t// * false: [ value ].slice( 0 ) => resolve( value )\n\t\t\t// * true: [ value ].slice( 1 ) => resolve()\n\t\t\tresolve.apply( undefined, [ value ].slice( noValue ) );\n\t\t}\n\n\t// For Promises/A+, convert exceptions into rejections\n\t// Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in\n\t// Deferred#then to conditionally suppress rejection.\n\t} catch ( value ) {\n\n\t\t// Support: Android 4.0 only\n\t\t// Strict mode functions invoked without .call/.apply get global-object context\n\t\treject.apply( undefined, [ value ] );\n\t}\n}\n\njQuery.extend( {\n\n\tDeferred: function( func ) {\n\t\tvar tuples = [\n\n\t\t\t\t// action, add listener, callbacks,\n\t\t\t\t// ... .then handlers, argument index, [final state]\n\t\t\t\t[ \"notify\", \"progress\", jQuery.Callbacks( \"memory\" ),\n\t\t\t\t\tjQuery.Callbacks( \"memory\" ), 2 ],\n\t\t\t\t[ \"resolve\", \"done\", jQuery.Callbacks( \"once memory\" ),\n\t\t\t\t\tjQuery.Callbacks( \"once memory\" ), 0, \"resolved\" ],\n\t\t\t\t[ \"reject\", \"fail\", jQuery.Callbacks( \"once memory\" ),\n\t\t\t\t\tjQuery.Callbacks( \"once memory\" ), 1, \"rejected\" ]\n\t\t\t],\n\t\t\tstate = \"pending\",\n\t\t\tpromise = {\n\t\t\t\tstate: function() {\n\t\t\t\t\treturn state;\n\t\t\t\t},\n\t\t\t\talways: function() {\n\t\t\t\t\tdeferred.done( arguments ).fail( arguments );\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\t\t\t\t\"catch\": function( fn ) {\n\t\t\t\t\treturn promise.then( null, fn );\n\t\t\t\t},\n\n\t\t\t\t// Keep pipe for back-compat\n\t\t\t\tpipe: function( /* fnDone, fnFail, fnProgress */ ) {\n\t\t\t\t\tvar fns = arguments;\n\n\t\t\t\t\treturn jQuery.Deferred( function( newDefer ) {\n\t\t\t\t\t\tjQuery.each( tuples, function( _i, tuple ) {\n\n\t\t\t\t\t\t\t// Map tuples (progress, done, fail) to arguments (done, fail, progress)\n\t\t\t\t\t\t\tvar fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];\n\n\t\t\t\t\t\t\t// deferred.progress(function() { bind to newDefer or newDefer.notify })\n\t\t\t\t\t\t\t// deferred.done(function() { bind to newDefer or newDefer.resolve })\n\t\t\t\t\t\t\t// deferred.fail(function() { bind to newDefer or newDefer.reject })\n\t\t\t\t\t\t\tdeferred[ tuple[ 1 ] ]( function() {\n\t\t\t\t\t\t\t\tvar returned = fn && fn.apply( this, arguments );\n\t\t\t\t\t\t\t\tif ( returned && isFunction( returned.promise ) ) {\n\t\t\t\t\t\t\t\t\treturned.promise()\n\t\t\t\t\t\t\t\t\t\t.progress( newDefer.notify )\n\t\t\t\t\t\t\t\t\t\t.done( newDefer.resolve )\n\t\t\t\t\t\t\t\t\t\t.fail( newDefer.reject );\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tnewDefer[ tuple[ 0 ] + \"With\" ](\n\t\t\t\t\t\t\t\t\t\tthis,\n\t\t\t\t\t\t\t\t\t\tfn ? [ returned ] : arguments\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t} );\n\t\t\t\t\t\tfns = null;\n\t\t\t\t\t} ).promise();\n\t\t\t\t},\n\t\t\t\tthen: function( onFulfilled, onRejected, onProgress ) {\n\t\t\t\t\tvar maxDepth = 0;\n\t\t\t\t\tfunction resolve( depth, deferred, handler, special ) {\n\t\t\t\t\t\treturn function() {\n\t\t\t\t\t\t\tvar that = this,\n\t\t\t\t\t\t\t\targs = arguments,\n\t\t\t\t\t\t\t\tmightThrow = function() {\n\t\t\t\t\t\t\t\t\tvar returned, then;\n\n\t\t\t\t\t\t\t\t\t// Support: Promises/A+ section 2.3.3.3.3\n\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-59\n\t\t\t\t\t\t\t\t\t// Ignore double-resolution attempts\n\t\t\t\t\t\t\t\t\tif ( depth < maxDepth ) {\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\treturned = handler.apply( that, args );\n\n\t\t\t\t\t\t\t\t\t// Support: Promises/A+ section 2.3.1\n\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-48\n\t\t\t\t\t\t\t\t\tif ( returned === deferred.promise() ) {\n\t\t\t\t\t\t\t\t\t\tthrow new TypeError( \"Thenable self-resolution\" );\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// Support: Promises/A+ sections 2.3.3.1, 3.5\n\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-54\n\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-75\n\t\t\t\t\t\t\t\t\t// Retrieve `then` only once\n\t\t\t\t\t\t\t\t\tthen = returned &&\n\n\t\t\t\t\t\t\t\t\t\t// Support: Promises/A+ section 2.3.4\n\t\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-64\n\t\t\t\t\t\t\t\t\t\t// Only check objects and functions for thenability\n\t\t\t\t\t\t\t\t\t\t( typeof returned === \"object\" ||\n\t\t\t\t\t\t\t\t\t\t\ttypeof returned === \"function\" ) &&\n\t\t\t\t\t\t\t\t\t\treturned.then;\n\n\t\t\t\t\t\t\t\t\t// Handle a returned thenable\n\t\t\t\t\t\t\t\t\tif ( isFunction( then ) ) {\n\n\t\t\t\t\t\t\t\t\t\t// Special processors (notify) just wait for resolution\n\t\t\t\t\t\t\t\t\t\tif ( special ) {\n\t\t\t\t\t\t\t\t\t\t\tthen.call(\n\t\t\t\t\t\t\t\t\t\t\t\treturned,\n\t\t\t\t\t\t\t\t\t\t\t\tresolve( maxDepth, deferred, Identity, special ),\n\t\t\t\t\t\t\t\t\t\t\t\tresolve( maxDepth, deferred, Thrower, special )\n\t\t\t\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t\t\t// Normal processors (resolve) also hook into progress\n\t\t\t\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t\t\t\t// ...and disregard older resolution values\n\t\t\t\t\t\t\t\t\t\t\tmaxDepth++;\n\n\t\t\t\t\t\t\t\t\t\t\tthen.call(\n\t\t\t\t\t\t\t\t\t\t\t\treturned,\n\t\t\t\t\t\t\t\t\t\t\t\tresolve( maxDepth, deferred, Identity, special ),\n\t\t\t\t\t\t\t\t\t\t\t\tresolve( maxDepth, deferred, Thrower, special ),\n\t\t\t\t\t\t\t\t\t\t\t\tresolve( maxDepth, deferred, Identity,\n\t\t\t\t\t\t\t\t\t\t\t\t\tdeferred.notifyWith )\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// Handle all other returned values\n\t\t\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t\t\t// Only substitute handlers pass on context\n\t\t\t\t\t\t\t\t\t\t// and multiple values (non-spec behavior)\n\t\t\t\t\t\t\t\t\t\tif ( handler !== Identity ) {\n\t\t\t\t\t\t\t\t\t\t\tthat = undefined;\n\t\t\t\t\t\t\t\t\t\t\targs = [ returned ];\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t// Process the value(s)\n\t\t\t\t\t\t\t\t\t\t// Default process is resolve\n\t\t\t\t\t\t\t\t\t\t( special || deferred.resolveWith )( that, args );\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t},\n\n\t\t\t\t\t\t\t\t// Only normal processors (resolve) catch and reject exceptions\n\t\t\t\t\t\t\t\tprocess = special ?\n\t\t\t\t\t\t\t\t\tmightThrow :\n\t\t\t\t\t\t\t\t\tfunction() {\n\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\tmightThrow();\n\t\t\t\t\t\t\t\t\t\t} catch ( e ) {\n\n\t\t\t\t\t\t\t\t\t\t\tif ( jQuery.Deferred.exceptionHook ) {\n\t\t\t\t\t\t\t\t\t\t\t\tjQuery.Deferred.exceptionHook( e,\n\t\t\t\t\t\t\t\t\t\t\t\t\tprocess.stackTrace );\n\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t// Support: Promises/A+ section 2.3.3.3.4.1\n\t\t\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-61\n\t\t\t\t\t\t\t\t\t\t\t// Ignore post-resolution exceptions\n\t\t\t\t\t\t\t\t\t\t\tif ( depth + 1 >= maxDepth ) {\n\n\t\t\t\t\t\t\t\t\t\t\t\t// Only substitute handlers pass on context\n\t\t\t\t\t\t\t\t\t\t\t\t// and multiple values (non-spec behavior)\n\t\t\t\t\t\t\t\t\t\t\t\tif ( handler !== Thrower ) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tthat = undefined;\n\t\t\t\t\t\t\t\t\t\t\t\t\targs = [ e ];\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t\tdeferred.rejectWith( that, args );\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t// Support: Promises/A+ section 2.3.3.3.1\n\t\t\t\t\t\t\t// https://promisesaplus.com/#point-57\n\t\t\t\t\t\t\t// Re-resolve promises immediately to dodge false rejection from\n\t\t\t\t\t\t\t// subsequent errors\n\t\t\t\t\t\t\tif ( depth ) {\n\t\t\t\t\t\t\t\tprocess();\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t// Call an optional hook to record the stack, in case of exception\n\t\t\t\t\t\t\t\t// since it's otherwise lost when execution goes async\n\t\t\t\t\t\t\t\tif ( jQuery.Deferred.getStackHook ) {\n\t\t\t\t\t\t\t\t\tprocess.stackTrace = jQuery.Deferred.getStackHook();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\twindow.setTimeout( process );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\treturn jQuery.Deferred( function( newDefer ) {\n\n\t\t\t\t\t\t// progress_handlers.add( ... )\n\t\t\t\t\t\ttuples[ 0 ][ 3 ].add(\n\t\t\t\t\t\t\tresolve(\n\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t\tnewDefer,\n\t\t\t\t\t\t\t\tisFunction( onProgress ) ?\n\t\t\t\t\t\t\t\t\tonProgress :\n\t\t\t\t\t\t\t\t\tIdentity,\n\t\t\t\t\t\t\t\tnewDefer.notifyWith\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// fulfilled_handlers.add( ... )\n\t\t\t\t\t\ttuples[ 1 ][ 3 ].add(\n\t\t\t\t\t\t\tresolve(\n\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t\tnewDefer,\n\t\t\t\t\t\t\t\tisFunction( onFulfilled ) ?\n\t\t\t\t\t\t\t\t\tonFulfilled :\n\t\t\t\t\t\t\t\t\tIdentity\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// rejected_handlers.add( ... )\n\t\t\t\t\t\ttuples[ 2 ][ 3 ].add(\n\t\t\t\t\t\t\tresolve(\n\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t\tnewDefer,\n\t\t\t\t\t\t\t\tisFunction( onRejected ) ?\n\t\t\t\t\t\t\t\t\tonRejected :\n\t\t\t\t\t\t\t\t\tThrower\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t);\n\t\t\t\t\t} ).promise();\n\t\t\t\t},\n\n\t\t\t\t// Get a promise for this deferred\n\t\t\t\t// If obj is provided, the promise aspect is added to the object\n\t\t\t\tpromise: function( obj ) {\n\t\t\t\t\treturn obj != null ? jQuery.extend( obj, promise ) : promise;\n\t\t\t\t}\n\t\t\t},\n\t\t\tdeferred = {};\n\n\t\t// Add list-specific methods\n\t\tjQuery.each( tuples, function( i, tuple ) {\n\t\t\tvar list = tuple[ 2 ],\n\t\t\t\tstateString = tuple[ 5 ];\n\n\t\t\t// promise.progress = list.add\n\t\t\t// promise.done = list.add\n\t\t\t// promise.fail = list.add\n\t\t\tpromise[ tuple[ 1 ] ] = list.add;\n\n\t\t\t// Handle state\n\t\t\tif ( stateString ) {\n\t\t\t\tlist.add(\n\t\t\t\t\tfunction() {\n\n\t\t\t\t\t\t// state = \"resolved\" (i.e., fulfilled)\n\t\t\t\t\t\t// state = \"rejected\"\n\t\t\t\t\t\tstate = stateString;\n\t\t\t\t\t},\n\n\t\t\t\t\t// rejected_callbacks.disable\n\t\t\t\t\t// fulfilled_callbacks.disable\n\t\t\t\t\ttuples[ 3 - i ][ 2 ].disable,\n\n\t\t\t\t\t// rejected_handlers.disable\n\t\t\t\t\t// fulfilled_handlers.disable\n\t\t\t\t\ttuples[ 3 - i ][ 3 ].disable,\n\n\t\t\t\t\t// progress_callbacks.lock\n\t\t\t\t\ttuples[ 0 ][ 2 ].lock,\n\n\t\t\t\t\t// progress_handlers.lock\n\t\t\t\t\ttuples[ 0 ][ 3 ].lock\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// progress_handlers.fire\n\t\t\t// fulfilled_handlers.fire\n\t\t\t// rejected_handlers.fire\n\t\t\tlist.add( tuple[ 3 ].fire );\n\n\t\t\t// deferred.notify = function() { deferred.notifyWith(...) }\n\t\t\t// deferred.resolve = function() { deferred.resolveWith(...) }\n\t\t\t// deferred.reject = function() { deferred.rejectWith(...) }\n\t\t\tdeferred[ tuple[ 0 ] ] = function() {\n\t\t\t\tdeferred[ tuple[ 0 ] + \"With\" ]( this === deferred ? undefined : this, arguments );\n\t\t\t\treturn this;\n\t\t\t};\n\n\t\t\t// deferred.notifyWith = list.fireWith\n\t\t\t// deferred.resolveWith = list.fireWith\n\t\t\t// deferred.rejectWith = list.fireWith\n\t\t\tdeferred[ tuple[ 0 ] + \"With\" ] = list.fireWith;\n\t\t} );\n\n\t\t// Make the deferred a promise\n\t\tpromise.promise( deferred );\n\n\t\t// Call given func if any\n\t\tif ( func ) {\n\t\t\tfunc.call( deferred, deferred );\n\t\t}\n\n\t\t// All done!\n\t\treturn deferred;\n\t},\n\n\t// Deferred helper\n\twhen: function( singleValue ) {\n\t\tvar\n\n\t\t\t// count of uncompleted subordinates\n\t\t\tremaining = arguments.length,\n\n\t\t\t// count of unprocessed arguments\n\t\t\ti = remaining,\n\n\t\t\t// subordinate fulfillment data\n\t\t\tresolveContexts = Array( i ),\n\t\t\tresolveValues = slice.call( arguments ),\n\n\t\t\t// the master Deferred\n\t\t\tmaster = jQuery.Deferred(),\n\n\t\t\t// subordinate callback factory\n\t\t\tupdateFunc = function( i ) {\n\t\t\t\treturn function( value ) {\n\t\t\t\t\tresolveContexts[ i ] = this;\n\t\t\t\t\tresolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;\n\t\t\t\t\tif ( !( --remaining ) ) {\n\t\t\t\t\t\tmaster.resolveWith( resolveContexts, resolveValues );\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t};\n\n\t\t// Single- and empty arguments are adopted like Promise.resolve\n\t\tif ( remaining <= 1 ) {\n\t\t\tadoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject,\n\t\t\t\t!remaining );\n\n\t\t\t// Use .then() to unwrap secondary thenables (cf. gh-3000)\n\t\t\tif ( master.state() === \"pending\" ||\n\t\t\t\tisFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {\n\n\t\t\t\treturn master.then();\n\t\t\t}\n\t\t}\n\n\t\t// Multiple arguments are aggregated like Promise.all array elements\n\t\twhile ( i-- ) {\n\t\t\tadoptValue( resolveValues[ i ], updateFunc( i ), master.reject );\n\t\t}\n\n\t\treturn master.promise();\n\t}\n} );\n\n\n// These usually indicate a programmer mistake during development,\n// warn about them ASAP rather than swallowing them by default.\nvar rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;\n\njQuery.Deferred.exceptionHook = function( error, stack ) {\n\n\t// Support: IE 8 - 9 only\n\t// Console exists when dev tools are open, which can happen at any time\n\tif ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) {\n\t\twindow.console.warn( \"jQuery.Deferred exception: \" + error.message, error.stack, stack );\n\t}\n};\n\n\n\n\njQuery.readyException = function( error ) {\n\twindow.setTimeout( function() {\n\t\tthrow error;\n\t} );\n};\n\n\n\n\n// The deferred used on DOM ready\nvar readyList = jQuery.Deferred();\n\njQuery.fn.ready = function( fn ) {\n\n\treadyList\n\t\t.then( fn )\n\n\t\t// Wrap jQuery.readyException in a function so that the lookup\n\t\t// happens at the time of error handling instead of callback\n\t\t// registration.\n\t\t.catch( function( error ) {\n\t\t\tjQuery.readyException( error );\n\t\t} );\n\n\treturn this;\n};\n\njQuery.extend( {\n\n\t// Is the DOM ready to be used? Set to true once it occurs.\n\tisReady: false,\n\n\t// A counter to track how many items to wait for before\n\t// the ready event fires. See #6781\n\treadyWait: 1,\n\n\t// Handle when the DOM is ready\n\tready: function( wait ) {\n\n\t\t// Abort if there are pending holds or we're already ready\n\t\tif ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Remember that the DOM is ready\n\t\tjQuery.isReady = true;\n\n\t\t// If a normal DOM Ready event fired, decrement, and wait if need be\n\t\tif ( wait !== true && --jQuery.readyWait > 0 ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// If there are functions bound, to execute\n\t\treadyList.resolveWith( document, [ jQuery ] );\n\t}\n} );\n\njQuery.ready.then = readyList.then;\n\n// The ready event handler and self cleanup method\nfunction completed() {\n\tdocument.removeEventListener( \"DOMContentLoaded\", completed );\n\twindow.removeEventListener( \"load\", completed );\n\tjQuery.ready();\n}\n\n// Catch cases where $(document).ready() is called\n// after the browser event has already occurred.\n// Support: IE <=9 - 10 only\n// Older IE sometimes signals \"interactive\" too soon\nif ( document.readyState === \"complete\" ||\n\t( document.readyState !== \"loading\" && !document.documentElement.doScroll ) ) {\n\n\t// Handle it asynchronously to allow scripts the opportunity to delay ready\n\twindow.setTimeout( jQuery.ready );\n\n} else {\n\n\t// Use the handy event callback\n\tdocument.addEventListener( \"DOMContentLoaded\", completed );\n\n\t// A fallback to window.onload, that will always work\n\twindow.addEventListener( \"load\", completed );\n}\n\n\n\n\n// Multifunctional method to get and set values of a collection\n// The value/s can optionally be executed if it's a function\nvar access = function( elems, fn, key, value, chainable, emptyGet, raw ) {\n\tvar i = 0,\n\t\tlen = elems.length,\n\t\tbulk = key == null;\n\n\t// Sets many values\n\tif ( toType( key ) === \"object\" ) {\n\t\tchainable = true;\n\t\tfor ( i in key ) {\n\t\t\taccess( elems, fn, i, key[ i ], true, emptyGet, raw );\n\t\t}\n\n\t// Sets one value\n\t} else if ( value !== undefined ) {\n\t\tchainable = true;\n\n\t\tif ( !isFunction( value ) ) {\n\t\t\traw = true;\n\t\t}\n\n\t\tif ( bulk ) {\n\n\t\t\t// Bulk operations run against the entire set\n\t\t\tif ( raw ) {\n\t\t\t\tfn.call( elems, value );\n\t\t\t\tfn = null;\n\n\t\t\t// ...except when executing function values\n\t\t\t} else {\n\t\t\t\tbulk = fn;\n\t\t\t\tfn = function( elem, _key, value ) {\n\t\t\t\t\treturn bulk.call( jQuery( elem ), value );\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tif ( fn ) {\n\t\t\tfor ( ; i < len; i++ ) {\n\t\t\t\tfn(\n\t\t\t\t\telems[ i ], key, raw ?\n\t\t\t\t\tvalue :\n\t\t\t\t\tvalue.call( elems[ i ], i, fn( elems[ i ], key ) )\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tif ( chainable ) {\n\t\treturn elems;\n\t}\n\n\t// Gets\n\tif ( bulk ) {\n\t\treturn fn.call( elems );\n\t}\n\n\treturn len ? fn( elems[ 0 ], key ) : emptyGet;\n};\n\n\n// Matches dashed string for camelizing\nvar rmsPrefix = /^-ms-/,\n\trdashAlpha = /-([a-z])/g;\n\n// Used by camelCase as callback to replace()\nfunction fcamelCase( _all, letter ) {\n\treturn letter.toUpperCase();\n}\n\n// Convert dashed to camelCase; used by the css and data modules\n// Support: IE <=9 - 11, Edge 12 - 15\n// Microsoft forgot to hump their vendor prefix (#9572)\nfunction camelCase( string ) {\n\treturn string.replace( rmsPrefix, \"ms-\" ).replace( rdashAlpha, fcamelCase );\n}\nvar acceptData = function( owner ) {\n\n\t// Accepts only:\n\t// - Node\n\t// - Node.ELEMENT_NODE\n\t// - Node.DOCUMENT_NODE\n\t// - Object\n\t// - Any\n\treturn owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );\n};\n\n\n\n\nfunction Data() {\n\tthis.expando = jQuery.expando + Data.uid++;\n}\n\nData.uid = 1;\n\nData.prototype = {\n\n\tcache: function( owner ) {\n\n\t\t// Check if the owner object already has a cache\n\t\tvar value = owner[ this.expando ];\n\n\t\t// If not, create one\n\t\tif ( !value ) {\n\t\t\tvalue = {};\n\n\t\t\t// We can accept data for non-element nodes in modern browsers,\n\t\t\t// but we should not, see #8335.\n\t\t\t// Always return an empty object.\n\t\t\tif ( acceptData( owner ) ) {\n\n\t\t\t\t// If it is a node unlikely to be stringify-ed or looped over\n\t\t\t\t// use plain assignment\n\t\t\t\tif ( owner.nodeType ) {\n\t\t\t\t\towner[ this.expando ] = value;\n\n\t\t\t\t// Otherwise secure it in a non-enumerable property\n\t\t\t\t// configurable must be true to allow the property to be\n\t\t\t\t// deleted when data is removed\n\t\t\t\t} else {\n\t\t\t\t\tObject.defineProperty( owner, this.expando, {\n\t\t\t\t\t\tvalue: value,\n\t\t\t\t\t\tconfigurable: true\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn value;\n\t},\n\tset: function( owner, data, value ) {\n\t\tvar prop,\n\t\t\tcache = this.cache( owner );\n\n\t\t// Handle: [ owner, key, value ] args\n\t\t// Always use camelCase key (gh-2257)\n\t\tif ( typeof data === \"string\" ) {\n\t\t\tcache[ camelCase( data ) ] = value;\n\n\t\t// Handle: [ owner, { properties } ] args\n\t\t} else {\n\n\t\t\t// Copy the properties one-by-one to the cache object\n\t\t\tfor ( prop in data ) {\n\t\t\t\tcache[ camelCase( prop ) ] = data[ prop ];\n\t\t\t}\n\t\t}\n\t\treturn cache;\n\t},\n\tget: function( owner, key ) {\n\t\treturn key === undefined ?\n\t\t\tthis.cache( owner ) :\n\n\t\t\t// Always use camelCase key (gh-2257)\n\t\t\towner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ];\n\t},\n\taccess: function( owner, key, value ) {\n\n\t\t// In cases where either:\n\t\t//\n\t\t// 1. No key was specified\n\t\t// 2. A string key was specified, but no value provided\n\t\t//\n\t\t// Take the \"read\" path and allow the get method to determine\n\t\t// which value to return, respectively either:\n\t\t//\n\t\t// 1. The entire cache object\n\t\t// 2. The data stored at the key\n\t\t//\n\t\tif ( key === undefined ||\n\t\t\t\t( ( key && typeof key === \"string\" ) && value === undefined ) ) {\n\n\t\t\treturn this.get( owner, key );\n\t\t}\n\n\t\t// When the key is not a string, or both a key and value\n\t\t// are specified, set or extend (existing objects) with either:\n\t\t//\n\t\t// 1. An object of properties\n\t\t// 2. A key and value\n\t\t//\n\t\tthis.set( owner, key, value );\n\n\t\t// Since the \"set\" path can have two possible entry points\n\t\t// return the expected data based on which path was taken[*]\n\t\treturn value !== undefined ? value : key;\n\t},\n\tremove: function( owner, key ) {\n\t\tvar i,\n\t\t\tcache = owner[ this.expando ];\n\n\t\tif ( cache === undefined ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( key !== undefined ) {\n\n\t\t\t// Support array or space separated string of keys\n\t\t\tif ( Array.isArray( key ) ) {\n\n\t\t\t\t// If key is an array of keys...\n\t\t\t\t// We always set camelCase keys, so remove that.\n\t\t\t\tkey = key.map( camelCase );\n\t\t\t} else {\n\t\t\t\tkey = camelCase( key );\n\n\t\t\t\t// If a key with the spaces exists, use it.\n\t\t\t\t// Otherwise, create an array by matching non-whitespace\n\t\t\t\tkey = key in cache ?\n\t\t\t\t\t[ key ] :\n\t\t\t\t\t( key.match( rnothtmlwhite ) || [] );\n\t\t\t}\n\n\t\t\ti = key.length;\n\n\t\t\twhile ( i-- ) {\n\t\t\t\tdelete cache[ key[ i ] ];\n\t\t\t}\n\t\t}\n\n\t\t// Remove the expando if there's no more data\n\t\tif ( key === undefined || jQuery.isEmptyObject( cache ) ) {\n\n\t\t\t// Support: Chrome <=35 - 45\n\t\t\t// Webkit & Blink performance suffers when deleting properties\n\t\t\t// from DOM nodes, so set to undefined instead\n\t\t\t// https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)\n\t\t\tif ( owner.nodeType ) {\n\t\t\t\towner[ this.expando ] = undefined;\n\t\t\t} else {\n\t\t\t\tdelete owner[ this.expando ];\n\t\t\t}\n\t\t}\n\t},\n\thasData: function( owner ) {\n\t\tvar cache = owner[ this.expando ];\n\t\treturn cache !== undefined && !jQuery.isEmptyObject( cache );\n\t}\n};\nvar dataPriv = new Data();\n\nvar dataUser = new Data();\n\n\n\n//\tImplementation Summary\n//\n//\t1. Enforce API surface and semantic compatibility with 1.9.x branch\n//\t2. Improve the module's maintainability by reducing the storage\n//\t\tpaths to a single mechanism.\n//\t3. Use the same single mechanism to support \"private\" and \"user\" data.\n//\t4. _Never_ expose \"private\" data to user code (TODO: Drop _data, _removeData)\n//\t5. Avoid exposing implementation details on user objects (eg. expando properties)\n//\t6. Provide a clear path for implementation upgrade to WeakMap in 2014\n\nvar rbrace = /^(?:\\{[\\w\\W]*\\}|\\[[\\w\\W]*\\])$/,\n\trmultiDash = /[A-Z]/g;\n\nfunction getData( data ) {\n\tif ( data === \"true\" ) {\n\t\treturn true;\n\t}\n\n\tif ( data === \"false\" ) {\n\t\treturn false;\n\t}\n\n\tif ( data === \"null\" ) {\n\t\treturn null;\n\t}\n\n\t// Only convert to a number if it doesn't change the string\n\tif ( data === +data + \"\" ) {\n\t\treturn +data;\n\t}\n\n\tif ( rbrace.test( data ) ) {\n\t\treturn JSON.parse( data );\n\t}\n\n\treturn data;\n}\n\nfunction dataAttr( elem, key, data ) {\n\tvar name;\n\n\t// If nothing was found internally, try to fetch any\n\t// data from the HTML5 data-* attribute\n\tif ( data === undefined && elem.nodeType === 1 ) {\n\t\tname = \"data-\" + key.replace( rmultiDash, \"-$&\" ).toLowerCase();\n\t\tdata = elem.getAttribute( name );\n\n\t\tif ( typeof data === \"string\" ) {\n\t\t\ttry {\n\t\t\t\tdata = getData( data );\n\t\t\t} catch ( e ) {}\n\n\t\t\t// Make sure we set the data so it isn't changed later\n\t\t\tdataUser.set( elem, key, data );\n\t\t} else {\n\t\t\tdata = undefined;\n\t\t}\n\t}\n\treturn data;\n}\n\njQuery.extend( {\n\thasData: function( elem ) {\n\t\treturn dataUser.hasData( elem ) || dataPriv.hasData( elem );\n\t},\n\n\tdata: function( elem, name, data ) {\n\t\treturn dataUser.access( elem, name, data );\n\t},\n\n\tremoveData: function( elem, name ) {\n\t\tdataUser.remove( elem, name );\n\t},\n\n\t// TODO: Now that all calls to _data and _removeData have been replaced\n\t// with direct calls to dataPriv methods, these can be deprecated.\n\t_data: function( elem, name, data ) {\n\t\treturn dataPriv.access( elem, name, data );\n\t},\n\n\t_removeData: function( elem, name ) {\n\t\tdataPriv.remove( elem, name );\n\t}\n} );\n\njQuery.fn.extend( {\n\tdata: function( key, value ) {\n\t\tvar i, name, data,\n\t\t\telem = this[ 0 ],\n\t\t\tattrs = elem && elem.attributes;\n\n\t\t// Gets all values\n\t\tif ( key === undefined ) {\n\t\t\tif ( this.length ) {\n\t\t\t\tdata = dataUser.get( elem );\n\n\t\t\t\tif ( elem.nodeType === 1 && !dataPriv.get( elem, \"hasDataAttrs\" ) ) {\n\t\t\t\t\ti = attrs.length;\n\t\t\t\t\twhile ( i-- ) {\n\n\t\t\t\t\t\t// Support: IE 11 only\n\t\t\t\t\t\t// The attrs elements can be null (#14894)\n\t\t\t\t\t\tif ( attrs[ i ] ) {\n\t\t\t\t\t\t\tname = attrs[ i ].name;\n\t\t\t\t\t\t\tif ( name.indexOf( \"data-\" ) === 0 ) {\n\t\t\t\t\t\t\t\tname = camelCase( name.slice( 5 ) );\n\t\t\t\t\t\t\t\tdataAttr( elem, name, data[ name ] );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdataPriv.set( elem, \"hasDataAttrs\", true );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn data;\n\t\t}\n\n\t\t// Sets multiple values\n\t\tif ( typeof key === \"object\" ) {\n\t\t\treturn this.each( function() {\n\t\t\t\tdataUser.set( this, key );\n\t\t\t} );\n\t\t}\n\n\t\treturn access( this, function( value ) {\n\t\t\tvar data;\n\n\t\t\t// The calling jQuery object (element matches) is not empty\n\t\t\t// (and therefore has an element appears at this[ 0 ]) and the\n\t\t\t// `value` parameter was not undefined. An empty jQuery object\n\t\t\t// will result in `undefined` for elem = this[ 0 ] which will\n\t\t\t// throw an exception if an attempt to read a data cache is made.\n\t\t\tif ( elem && value === undefined ) {\n\n\t\t\t\t// Attempt to get data from the cache\n\t\t\t\t// The key will always be camelCased in Data\n\t\t\t\tdata = dataUser.get( elem, key );\n\t\t\t\tif ( data !== undefined ) {\n\t\t\t\t\treturn data;\n\t\t\t\t}\n\n\t\t\t\t// Attempt to \"discover\" the data in\n\t\t\t\t// HTML5 custom data-* attrs\n\t\t\t\tdata = dataAttr( elem, key );\n\t\t\t\tif ( data !== undefined ) {\n\t\t\t\t\treturn data;\n\t\t\t\t}\n\n\t\t\t\t// We tried really hard, but the data doesn't exist.\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Set the data...\n\t\t\tthis.each( function() {\n\n\t\t\t\t// We always store the camelCased key\n\t\t\t\tdataUser.set( this, key, value );\n\t\t\t} );\n\t\t}, null, value, arguments.length > 1, null, true );\n\t},\n\n\tremoveData: function( key ) {\n\t\treturn this.each( function() {\n\t\t\tdataUser.remove( this, key );\n\t\t} );\n\t}\n} );\n\n\njQuery.extend( {\n\tqueue: function( elem, type, data ) {\n\t\tvar queue;\n\n\t\tif ( elem ) {\n\t\t\ttype = ( type || \"fx\" ) + \"queue\";\n\t\t\tqueue = dataPriv.get( elem, type );\n\n\t\t\t// Speed up dequeue by getting out quickly if this is just a lookup\n\t\t\tif ( data ) {\n\t\t\t\tif ( !queue || Array.isArray( data ) ) {\n\t\t\t\t\tqueue = dataPriv.access( elem, type, jQuery.makeArray( data ) );\n\t\t\t\t} else {\n\t\t\t\t\tqueue.push( data );\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn queue || [];\n\t\t}\n\t},\n\n\tdequeue: function( elem, type ) {\n\t\ttype = type || \"fx\";\n\n\t\tvar queue = jQuery.queue( elem, type ),\n\t\t\tstartLength = queue.length,\n\t\t\tfn = queue.shift(),\n\t\t\thooks = jQuery._queueHooks( elem, type ),\n\t\t\tnext = function() {\n\t\t\t\tjQuery.dequeue( elem, type );\n\t\t\t};\n\n\t\t// If the fx queue is dequeued, always remove the progress sentinel\n\t\tif ( fn === \"inprogress\" ) {\n\t\t\tfn = queue.shift();\n\t\t\tstartLength--;\n\t\t}\n\n\t\tif ( fn ) {\n\n\t\t\t// Add a progress sentinel to prevent the fx queue from being\n\t\t\t// automatically dequeued\n\t\t\tif ( type === \"fx\" ) {\n\t\t\t\tqueue.unshift( \"inprogress\" );\n\t\t\t}\n\n\t\t\t// Clear up the last queue stop function\n\t\t\tdelete hooks.stop;\n\t\t\tfn.call( elem, next, hooks );\n\t\t}\n\n\t\tif ( !startLength && hooks ) {\n\t\t\thooks.empty.fire();\n\t\t}\n\t},\n\n\t// Not public - generate a queueHooks object, or return the current one\n\t_queueHooks: function( elem, type ) {\n\t\tvar key = type + \"queueHooks\";\n\t\treturn dataPriv.get( elem, key ) || dataPriv.access( elem, key, {\n\t\t\tempty: jQuery.Callbacks( \"once memory\" ).add( function() {\n\t\t\t\tdataPriv.remove( elem, [ type + \"queue\", key ] );\n\t\t\t} )\n\t\t} );\n\t}\n} );\n\njQuery.fn.extend( {\n\tqueue: function( type, data ) {\n\t\tvar setter = 2;\n\n\t\tif ( typeof type !== \"string\" ) {\n\t\t\tdata = type;\n\t\t\ttype = \"fx\";\n\t\t\tsetter--;\n\t\t}\n\n\t\tif ( arguments.length < setter ) {\n\t\t\treturn jQuery.queue( this[ 0 ], type );\n\t\t}\n\n\t\treturn data === undefined ?\n\t\t\tthis :\n\t\t\tthis.each( function() {\n\t\t\t\tvar queue = jQuery.queue( this, type, data );\n\n\t\t\t\t// Ensure a hooks for this queue\n\t\t\t\tjQuery._queueHooks( this, type );\n\n\t\t\t\tif ( type === \"fx\" && queue[ 0 ] !== \"inprogress\" ) {\n\t\t\t\t\tjQuery.dequeue( this, type );\n\t\t\t\t}\n\t\t\t} );\n\t},\n\tdequeue: function( type ) {\n\t\treturn this.each( function() {\n\t\t\tjQuery.dequeue( this, type );\n\t\t} );\n\t},\n\tclearQueue: function( type ) {\n\t\treturn this.queue( type || \"fx\", [] );\n\t},\n\n\t// Get a promise resolved when queues of a certain type\n\t// are emptied (fx is the type by default)\n\tpromise: function( type, obj ) {\n\t\tvar tmp,\n\t\t\tcount = 1,\n\t\t\tdefer = jQuery.Deferred(),\n\t\t\telements = this,\n\t\t\ti = this.length,\n\t\t\tresolve = function() {\n\t\t\t\tif ( !( --count ) ) {\n\t\t\t\t\tdefer.resolveWith( elements, [ elements ] );\n\t\t\t\t}\n\t\t\t};\n\n\t\tif ( typeof type !== \"string\" ) {\n\t\t\tobj = type;\n\t\t\ttype = undefined;\n\t\t}\n\t\ttype = type || \"fx\";\n\n\t\twhile ( i-- ) {\n\t\t\ttmp = dataPriv.get( elements[ i ], type + \"queueHooks\" );\n\t\t\tif ( tmp && tmp.empty ) {\n\t\t\t\tcount++;\n\t\t\t\ttmp.empty.add( resolve );\n\t\t\t}\n\t\t}\n\t\tresolve();\n\t\treturn defer.promise( obj );\n\t}\n} );\nvar pnum = ( /[+-]?(?:\\d*\\.|)\\d+(?:[eE][+-]?\\d+|)/ ).source;\n\nvar rcssNum = new RegExp( \"^(?:([+-])=|)(\" + pnum + \")([a-z%]*)$\", \"i\" );\n\n\nvar cssExpand = [ \"Top\", \"Right\", \"Bottom\", \"Left\" ];\n\nvar documentElement = document.documentElement;\n\n\n\n\tvar isAttached = function( elem ) {\n\t\t\treturn jQuery.contains( elem.ownerDocument, elem );\n\t\t},\n\t\tcomposed = { composed: true };\n\n\t// Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only\n\t// Check attachment across shadow DOM boundaries when possible (gh-3504)\n\t// Support: iOS 10.0-10.2 only\n\t// Early iOS 10 versions support `attachShadow` but not `getRootNode`,\n\t// leading to errors. We need to check for `getRootNode`.\n\tif ( documentElement.getRootNode ) {\n\t\tisAttached = function( elem ) {\n\t\t\treturn jQuery.contains( elem.ownerDocument, elem ) ||\n\t\t\t\telem.getRootNode( composed ) === elem.ownerDocument;\n\t\t};\n\t}\nvar isHiddenWithinTree = function( elem, el ) {\n\n\t\t// isHiddenWithinTree might be called from jQuery#filter function;\n\t\t// in that case, element will be second argument\n\t\telem = el || elem;\n\n\t\t// Inline style trumps all\n\t\treturn elem.style.display === \"none\" ||\n\t\t\telem.style.display === \"\" &&\n\n\t\t\t// Otherwise, check computed style\n\t\t\t// Support: Firefox <=43 - 45\n\t\t\t// Disconnected elements can have computed display: none, so first confirm that elem is\n\t\t\t// in the document.\n\t\t\tisAttached( elem ) &&\n\n\t\t\tjQuery.css( elem, \"display\" ) === \"none\";\n\t};\n\n\n\nfunction adjustCSS( elem, prop, valueParts, tween ) {\n\tvar adjusted, scale,\n\t\tmaxIterations = 20,\n\t\tcurrentValue = tween ?\n\t\t\tfunction() {\n\t\t\t\treturn tween.cur();\n\t\t\t} :\n\t\t\tfunction() {\n\t\t\t\treturn jQuery.css( elem, prop, \"\" );\n\t\t\t},\n\t\tinitial = currentValue(),\n\t\tunit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? \"\" : \"px\" ),\n\n\t\t// Starting value computation is required for potential unit mismatches\n\t\tinitialInUnit = elem.nodeType &&\n\t\t\t( jQuery.cssNumber[ prop ] || unit !== \"px\" && +initial ) &&\n\t\t\trcssNum.exec( jQuery.css( elem, prop ) );\n\n\tif ( initialInUnit && initialInUnit[ 3 ] !== unit ) {\n\n\t\t// Support: Firefox <=54\n\t\t// Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144)\n\t\tinitial = initial / 2;\n\n\t\t// Trust units reported by jQuery.css\n\t\tunit = unit || initialInUnit[ 3 ];\n\n\t\t// Iteratively approximate from a nonzero starting point\n\t\tinitialInUnit = +initial || 1;\n\n\t\twhile ( maxIterations-- ) {\n\n\t\t\t// Evaluate and update our best guess (doubling guesses that zero out).\n\t\t\t// Finish if the scale equals or crosses 1 (making the old*new product non-positive).\n\t\t\tjQuery.style( elem, prop, initialInUnit + unit );\n\t\t\tif ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) {\n\t\t\t\tmaxIterations = 0;\n\t\t\t}\n\t\t\tinitialInUnit = initialInUnit / scale;\n\n\t\t}\n\n\t\tinitialInUnit = initialInUnit * 2;\n\t\tjQuery.style( elem, prop, initialInUnit + unit );\n\n\t\t// Make sure we update the tween properties later on\n\t\tvalueParts = valueParts || [];\n\t}\n\n\tif ( valueParts ) {\n\t\tinitialInUnit = +initialInUnit || +initial || 0;\n\n\t\t// Apply relative offset (+=/-=) if specified\n\t\tadjusted = valueParts[ 1 ] ?\n\t\t\tinitialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :\n\t\t\t+valueParts[ 2 ];\n\t\tif ( tween ) {\n\t\t\ttween.unit = unit;\n\t\t\ttween.start = initialInUnit;\n\t\t\ttween.end = adjusted;\n\t\t}\n\t}\n\treturn adjusted;\n}\n\n\nvar defaultDisplayMap = {};\n\nfunction getDefaultDisplay( elem ) {\n\tvar temp,\n\t\tdoc = elem.ownerDocument,\n\t\tnodeName = elem.nodeName,\n\t\tdisplay = defaultDisplayMap[ nodeName ];\n\n\tif ( display ) {\n\t\treturn display;\n\t}\n\n\ttemp = doc.body.appendChild( doc.createElement( nodeName ) );\n\tdisplay = jQuery.css( temp, \"display\" );\n\n\ttemp.parentNode.removeChild( temp );\n\n\tif ( display === \"none\" ) {\n\t\tdisplay = \"block\";\n\t}\n\tdefaultDisplayMap[ nodeName ] = display;\n\n\treturn display;\n}\n\nfunction showHide( elements, show ) {\n\tvar display, elem,\n\t\tvalues = [],\n\t\tindex = 0,\n\t\tlength = elements.length;\n\n\t// Determine new display value for elements that need to change\n\tfor ( ; index < length; index++ ) {\n\t\telem = elements[ index ];\n\t\tif ( !elem.style ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tdisplay = elem.style.display;\n\t\tif ( show ) {\n\n\t\t\t// Since we force visibility upon cascade-hidden elements, an immediate (and slow)\n\t\t\t// check is required in this first loop unless we have a nonempty display value (either\n\t\t\t// inline or about-to-be-restored)\n\t\t\tif ( display === \"none\" ) {\n\t\t\t\tvalues[ index ] = dataPriv.get( elem, \"display\" ) || null;\n\t\t\t\tif ( !values[ index ] ) {\n\t\t\t\t\telem.style.display = \"\";\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ( elem.style.display === \"\" && isHiddenWithinTree( elem ) ) {\n\t\t\t\tvalues[ index ] = getDefaultDisplay( elem );\n\t\t\t}\n\t\t} else {\n\t\t\tif ( display !== \"none\" ) {\n\t\t\t\tvalues[ index ] = \"none\";\n\n\t\t\t\t// Remember what we're overwriting\n\t\t\t\tdataPriv.set( elem, \"display\", display );\n\t\t\t}\n\t\t}\n\t}\n\n\t// Set the display of the elements in a second loop to avoid constant reflow\n\tfor ( index = 0; index < length; index++ ) {\n\t\tif ( values[ index ] != null ) {\n\t\t\telements[ index ].style.display = values[ index ];\n\t\t}\n\t}\n\n\treturn elements;\n}\n\njQuery.fn.extend( {\n\tshow: function() {\n\t\treturn showHide( this, true );\n\t},\n\thide: function() {\n\t\treturn showHide( this );\n\t},\n\ttoggle: function( state ) {\n\t\tif ( typeof state === \"boolean\" ) {\n\t\t\treturn state ? this.show() : this.hide();\n\t\t}\n\n\t\treturn this.each( function() {\n\t\t\tif ( isHiddenWithinTree( this ) ) {\n\t\t\t\tjQuery( this ).show();\n\t\t\t} else {\n\t\t\t\tjQuery( this ).hide();\n\t\t\t}\n\t\t} );\n\t}\n} );\nvar rcheckableType = ( /^(?:checkbox|radio)$/i );\n\nvar rtagName = ( /<([a-z][^\\/\\0>\\x20\\t\\r\\n\\f]*)/i );\n\nvar rscriptType = ( /^$|^module$|\\/(?:java|ecma)script/i );\n\n\n\n( function() {\n\tvar fragment = document.createDocumentFragment(),\n\t\tdiv = fragment.appendChild( document.createElement( \"div\" ) ),\n\t\tinput = document.createElement( \"input\" );\n\n\t// Support: Android 4.0 - 4.3 only\n\t// Check state lost if the name is set (#11217)\n\t// Support: Windows Web Apps (WWA)\n\t// `name` and `type` must use .setAttribute for WWA (#14901)\n\tinput.setAttribute( \"type\", \"radio\" );\n\tinput.setAttribute( \"checked\", \"checked\" );\n\tinput.setAttribute( \"name\", \"t\" );\n\n\tdiv.appendChild( input );\n\n\t// Support: Android <=4.1 only\n\t// Older WebKit doesn't clone checked state correctly in fragments\n\tsupport.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;\n\n\t// Support: IE <=11 only\n\t// Make sure textarea (and checkbox) defaultValue is properly cloned\n\tdiv.innerHTML = \"\";\n\tsupport.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;\n\n\t// Support: IE <=9 only\n\t// IE <=9 replaces \";\n\tsupport.option = !!div.lastChild;\n} )();\n\n\n// We have to close these tags to support XHTML (#13200)\nvar wrapMap = {\n\n\t// XHTML parsers do not magically insert elements in the\n\t// same way that tag soup parsers do. So we cannot shorten\n\t// this by omitting
\", \"
\" ],\n\tcol: [ 2, \"\", \"
\" ],\n\ttr: [ 2, \"\", \"
\" ],\n\ttd: [ 3, \"\", \"
\" ],\n\n\t_default: [ 0, \"\", \"\" ]\n};\n\nwrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;\nwrapMap.th = wrapMap.td;\n\n// Support: IE <=9 only\nif ( !support.option ) {\n\twrapMap.optgroup = wrapMap.option = [ 1, \"\" ];\n}\n\n\nfunction getAll( context, tag ) {\n\n\t// Support: IE <=9 - 11 only\n\t// Use typeof to avoid zero-argument method invocation on host objects (#15151)\n\tvar ret;\n\n\tif ( typeof context.getElementsByTagName !== \"undefined\" ) {\n\t\tret = context.getElementsByTagName( tag || \"*\" );\n\n\t} else if ( typeof context.querySelectorAll !== \"undefined\" ) {\n\t\tret = context.querySelectorAll( tag || \"*\" );\n\n\t} else {\n\t\tret = [];\n\t}\n\n\tif ( tag === undefined || tag && nodeName( context, tag ) ) {\n\t\treturn jQuery.merge( [ context ], ret );\n\t}\n\n\treturn ret;\n}\n\n\n// Mark scripts as having already been evaluated\nfunction setGlobalEval( elems, refElements ) {\n\tvar i = 0,\n\t\tl = elems.length;\n\n\tfor ( ; i < l; i++ ) {\n\t\tdataPriv.set(\n\t\t\telems[ i ],\n\t\t\t\"globalEval\",\n\t\t\t!refElements || dataPriv.get( refElements[ i ], \"globalEval\" )\n\t\t);\n\t}\n}\n\n\nvar rhtml = /<|&#?\\w+;/;\n\nfunction buildFragment( elems, context, scripts, selection, ignored ) {\n\tvar elem, tmp, tag, wrap, attached, j,\n\t\tfragment = context.createDocumentFragment(),\n\t\tnodes = [],\n\t\ti = 0,\n\t\tl = elems.length;\n\n\tfor ( ; i < l; i++ ) {\n\t\telem = elems[ i ];\n\n\t\tif ( elem || elem === 0 ) {\n\n\t\t\t// Add nodes directly\n\t\t\tif ( toType( elem ) === \"object\" ) {\n\n\t\t\t\t// Support: Android <=4.0 only, PhantomJS 1 only\n\t\t\t\t// push.apply(_, arraylike) throws on ancient WebKit\n\t\t\t\tjQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );\n\n\t\t\t// Convert non-html into a text node\n\t\t\t} else if ( !rhtml.test( elem ) ) {\n\t\t\t\tnodes.push( context.createTextNode( elem ) );\n\n\t\t\t// Convert html into DOM nodes\n\t\t\t} else {\n\t\t\t\ttmp = tmp || fragment.appendChild( context.createElement( \"div\" ) );\n\n\t\t\t\t// Deserialize a standard representation\n\t\t\t\ttag = ( rtagName.exec( elem ) || [ \"\", \"\" ] )[ 1 ].toLowerCase();\n\t\t\t\twrap = wrapMap[ tag ] || wrapMap._default;\n\t\t\t\ttmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];\n\n\t\t\t\t// Descend through wrappers to the right content\n\t\t\t\tj = wrap[ 0 ];\n\t\t\t\twhile ( j-- ) {\n\t\t\t\t\ttmp = tmp.lastChild;\n\t\t\t\t}\n\n\t\t\t\t// Support: Android <=4.0 only, PhantomJS 1 only\n\t\t\t\t// push.apply(_, arraylike) throws on ancient WebKit\n\t\t\t\tjQuery.merge( nodes, tmp.childNodes );\n\n\t\t\t\t// Remember the top-level container\n\t\t\t\ttmp = fragment.firstChild;\n\n\t\t\t\t// Ensure the created nodes are orphaned (#12392)\n\t\t\t\ttmp.textContent = \"\";\n\t\t\t}\n\t\t}\n\t}\n\n\t// Remove wrapper from fragment\n\tfragment.textContent = \"\";\n\n\ti = 0;\n\twhile ( ( elem = nodes[ i++ ] ) ) {\n\n\t\t// Skip elements already in the context collection (trac-4087)\n\t\tif ( selection && jQuery.inArray( elem, selection ) > -1 ) {\n\t\t\tif ( ignored ) {\n\t\t\t\tignored.push( elem );\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tattached = isAttached( elem );\n\n\t\t// Append to fragment\n\t\ttmp = getAll( fragment.appendChild( elem ), \"script\" );\n\n\t\t// Preserve script evaluation history\n\t\tif ( attached ) {\n\t\t\tsetGlobalEval( tmp );\n\t\t}\n\n\t\t// Capture executables\n\t\tif ( scripts ) {\n\t\t\tj = 0;\n\t\t\twhile ( ( elem = tmp[ j++ ] ) ) {\n\t\t\t\tif ( rscriptType.test( elem.type || \"\" ) ) {\n\t\t\t\t\tscripts.push( elem );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn fragment;\n}\n\n\nvar\n\trkeyEvent = /^key/,\n\trmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,\n\trtypenamespace = /^([^.]*)(?:\\.(.+)|)/;\n\nfunction returnTrue() {\n\treturn true;\n}\n\nfunction returnFalse() {\n\treturn false;\n}\n\n// Support: IE <=9 - 11+\n// focus() and blur() are asynchronous, except when they are no-op.\n// So expect focus to be synchronous when the element is already active,\n// and blur to be synchronous when the element is not already active.\n// (focus and blur are always synchronous in other supported browsers,\n// this just defines when we can count on it).\nfunction expectSync( elem, type ) {\n\treturn ( elem === safeActiveElement() ) === ( type === \"focus\" );\n}\n\n// Support: IE <=9 only\n// Accessing document.activeElement can throw unexpectedly\n// https://bugs.jquery.com/ticket/13393\nfunction safeActiveElement() {\n\ttry {\n\t\treturn document.activeElement;\n\t} catch ( err ) { }\n}\n\nfunction on( elem, types, selector, data, fn, one ) {\n\tvar origFn, type;\n\n\t// Types can be a map of types/handlers\n\tif ( typeof types === \"object\" ) {\n\n\t\t// ( types-Object, selector, data )\n\t\tif ( typeof selector !== \"string\" ) {\n\n\t\t\t// ( types-Object, data )\n\t\t\tdata = data || selector;\n\t\t\tselector = undefined;\n\t\t}\n\t\tfor ( type in types ) {\n\t\t\ton( elem, type, selector, data, types[ type ], one );\n\t\t}\n\t\treturn elem;\n\t}\n\n\tif ( data == null && fn == null ) {\n\n\t\t// ( types, fn )\n\t\tfn = selector;\n\t\tdata = selector = undefined;\n\t} else if ( fn == null ) {\n\t\tif ( typeof selector === \"string\" ) {\n\n\t\t\t// ( types, selector, fn )\n\t\t\tfn = data;\n\t\t\tdata = undefined;\n\t\t} else {\n\n\t\t\t// ( types, data, fn )\n\t\t\tfn = data;\n\t\t\tdata = selector;\n\t\t\tselector = undefined;\n\t\t}\n\t}\n\tif ( fn === false ) {\n\t\tfn = returnFalse;\n\t} else if ( !fn ) {\n\t\treturn elem;\n\t}\n\n\tif ( one === 1 ) {\n\t\torigFn = fn;\n\t\tfn = function( event ) {\n\n\t\t\t// Can use an empty set, since event contains the info\n\t\t\tjQuery().off( event );\n\t\t\treturn origFn.apply( this, arguments );\n\t\t};\n\n\t\t// Use same guid so caller can remove using origFn\n\t\tfn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );\n\t}\n\treturn elem.each( function() {\n\t\tjQuery.event.add( this, types, fn, data, selector );\n\t} );\n}\n\n/*\n * Helper functions for managing events -- not part of the public interface.\n * Props to Dean Edwards' addEvent library for many of the ideas.\n */\njQuery.event = {\n\n\tglobal: {},\n\n\tadd: function( elem, types, handler, data, selector ) {\n\n\t\tvar handleObjIn, eventHandle, tmp,\n\t\t\tevents, t, handleObj,\n\t\t\tspecial, handlers, type, namespaces, origType,\n\t\t\telemData = dataPriv.get( elem );\n\n\t\t// Only attach events to objects that accept data\n\t\tif ( !acceptData( elem ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Caller can pass in an object of custom data in lieu of the handler\n\t\tif ( handler.handler ) {\n\t\t\thandleObjIn = handler;\n\t\t\thandler = handleObjIn.handler;\n\t\t\tselector = handleObjIn.selector;\n\t\t}\n\n\t\t// Ensure that invalid selectors throw exceptions at attach time\n\t\t// Evaluate against documentElement in case elem is a non-element node (e.g., document)\n\t\tif ( selector ) {\n\t\t\tjQuery.find.matchesSelector( documentElement, selector );\n\t\t}\n\n\t\t// Make sure that the handler has a unique ID, used to find/remove it later\n\t\tif ( !handler.guid ) {\n\t\t\thandler.guid = jQuery.guid++;\n\t\t}\n\n\t\t// Init the element's event structure and main handler, if this is the first\n\t\tif ( !( events = elemData.events ) ) {\n\t\t\tevents = elemData.events = Object.create( null );\n\t\t}\n\t\tif ( !( eventHandle = elemData.handle ) ) {\n\t\t\teventHandle = elemData.handle = function( e ) {\n\n\t\t\t\t// Discard the second event of a jQuery.event.trigger() and\n\t\t\t\t// when an event is called after a page has unloaded\n\t\t\t\treturn typeof jQuery !== \"undefined\" && jQuery.event.triggered !== e.type ?\n\t\t\t\t\tjQuery.event.dispatch.apply( elem, arguments ) : undefined;\n\t\t\t};\n\t\t}\n\n\t\t// Handle multiple events separated by a space\n\t\ttypes = ( types || \"\" ).match( rnothtmlwhite ) || [ \"\" ];\n\t\tt = types.length;\n\t\twhile ( t-- ) {\n\t\t\ttmp = rtypenamespace.exec( types[ t ] ) || [];\n\t\t\ttype = origType = tmp[ 1 ];\n\t\t\tnamespaces = ( tmp[ 2 ] || \"\" ).split( \".\" ).sort();\n\n\t\t\t// There *must* be a type, no attaching namespace-only handlers\n\t\t\tif ( !type ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// If event changes its type, use the special event handlers for the changed type\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\n\t\t\t// If selector defined, determine special event api type, otherwise given type\n\t\t\ttype = ( selector ? special.delegateType : special.bindType ) || type;\n\n\t\t\t// Update special based on newly reset type\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\n\t\t\t// handleObj is passed to all event handlers\n\t\t\thandleObj = jQuery.extend( {\n\t\t\t\ttype: type,\n\t\t\t\torigType: origType,\n\t\t\t\tdata: data,\n\t\t\t\thandler: handler,\n\t\t\t\tguid: handler.guid,\n\t\t\t\tselector: selector,\n\t\t\t\tneedsContext: selector && jQuery.expr.match.needsContext.test( selector ),\n\t\t\t\tnamespace: namespaces.join( \".\" )\n\t\t\t}, handleObjIn );\n\n\t\t\t// Init the event handler queue if we're the first\n\t\t\tif ( !( handlers = events[ type ] ) ) {\n\t\t\t\thandlers = events[ type ] = [];\n\t\t\t\thandlers.delegateCount = 0;\n\n\t\t\t\t// Only use addEventListener if the special events handler returns false\n\t\t\t\tif ( !special.setup ||\n\t\t\t\t\tspecial.setup.call( elem, data, namespaces, eventHandle ) === false ) {\n\n\t\t\t\t\tif ( elem.addEventListener ) {\n\t\t\t\t\t\telem.addEventListener( type, eventHandle );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( special.add ) {\n\t\t\t\tspecial.add.call( elem, handleObj );\n\n\t\t\t\tif ( !handleObj.handler.guid ) {\n\t\t\t\t\thandleObj.handler.guid = handler.guid;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Add to the element's handler list, delegates in front\n\t\t\tif ( selector ) {\n\t\t\t\thandlers.splice( handlers.delegateCount++, 0, handleObj );\n\t\t\t} else {\n\t\t\t\thandlers.push( handleObj );\n\t\t\t}\n\n\t\t\t// Keep track of which events have ever been used, for event optimization\n\t\t\tjQuery.event.global[ type ] = true;\n\t\t}\n\n\t},\n\n\t// Detach an event or set of events from an element\n\tremove: function( elem, types, handler, selector, mappedTypes ) {\n\n\t\tvar j, origCount, tmp,\n\t\t\tevents, t, handleObj,\n\t\t\tspecial, handlers, type, namespaces, origType,\n\t\t\telemData = dataPriv.hasData( elem ) && dataPriv.get( elem );\n\n\t\tif ( !elemData || !( events = elemData.events ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Once for each type.namespace in types; type may be omitted\n\t\ttypes = ( types || \"\" ).match( rnothtmlwhite ) || [ \"\" ];\n\t\tt = types.length;\n\t\twhile ( t-- ) {\n\t\t\ttmp = rtypenamespace.exec( types[ t ] ) || [];\n\t\t\ttype = origType = tmp[ 1 ];\n\t\t\tnamespaces = ( tmp[ 2 ] || \"\" ).split( \".\" ).sort();\n\n\t\t\t// Unbind all events (on this namespace, if provided) for the element\n\t\t\tif ( !type ) {\n\t\t\t\tfor ( type in events ) {\n\t\t\t\t\tjQuery.event.remove( elem, type + types[ t ], handler, selector, true );\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\t\t\ttype = ( selector ? special.delegateType : special.bindType ) || type;\n\t\t\thandlers = events[ type ] || [];\n\t\t\ttmp = tmp[ 2 ] &&\n\t\t\t\tnew RegExp( \"(^|\\\\.)\" + namespaces.join( \"\\\\.(?:.*\\\\.|)\" ) + \"(\\\\.|$)\" );\n\n\t\t\t// Remove matching events\n\t\t\torigCount = j = handlers.length;\n\t\t\twhile ( j-- ) {\n\t\t\t\thandleObj = handlers[ j ];\n\n\t\t\t\tif ( ( mappedTypes || origType === handleObj.origType ) &&\n\t\t\t\t\t( !handler || handler.guid === handleObj.guid ) &&\n\t\t\t\t\t( !tmp || tmp.test( handleObj.namespace ) ) &&\n\t\t\t\t\t( !selector || selector === handleObj.selector ||\n\t\t\t\t\t\tselector === \"**\" && handleObj.selector ) ) {\n\t\t\t\t\thandlers.splice( j, 1 );\n\n\t\t\t\t\tif ( handleObj.selector ) {\n\t\t\t\t\t\thandlers.delegateCount--;\n\t\t\t\t\t}\n\t\t\t\t\tif ( special.remove ) {\n\t\t\t\t\t\tspecial.remove.call( elem, handleObj );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Remove generic event handler if we removed something and no more handlers exist\n\t\t\t// (avoids potential for endless recursion during removal of special event handlers)\n\t\t\tif ( origCount && !handlers.length ) {\n\t\t\t\tif ( !special.teardown ||\n\t\t\t\t\tspecial.teardown.call( elem, namespaces, elemData.handle ) === false ) {\n\n\t\t\t\t\tjQuery.removeEvent( elem, type, elemData.handle );\n\t\t\t\t}\n\n\t\t\t\tdelete events[ type ];\n\t\t\t}\n\t\t}\n\n\t\t// Remove data and the expando if it's no longer used\n\t\tif ( jQuery.isEmptyObject( events ) ) {\n\t\t\tdataPriv.remove( elem, \"handle events\" );\n\t\t}\n\t},\n\n\tdispatch: function( nativeEvent ) {\n\n\t\tvar i, j, ret, matched, handleObj, handlerQueue,\n\t\t\targs = new Array( arguments.length ),\n\n\t\t\t// Make a writable jQuery.Event from the native event object\n\t\t\tevent = jQuery.event.fix( nativeEvent ),\n\n\t\t\thandlers = (\n\t\t\t\t\tdataPriv.get( this, \"events\" ) || Object.create( null )\n\t\t\t\t)[ event.type ] || [],\n\t\t\tspecial = jQuery.event.special[ event.type ] || {};\n\n\t\t// Use the fix-ed jQuery.Event rather than the (read-only) native event\n\t\targs[ 0 ] = event;\n\n\t\tfor ( i = 1; i < arguments.length; i++ ) {\n\t\t\targs[ i ] = arguments[ i ];\n\t\t}\n\n\t\tevent.delegateTarget = this;\n\n\t\t// Call the preDispatch hook for the mapped type, and let it bail if desired\n\t\tif ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Determine handlers\n\t\thandlerQueue = jQuery.event.handlers.call( this, event, handlers );\n\n\t\t// Run delegates first; they may want to stop propagation beneath us\n\t\ti = 0;\n\t\twhile ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {\n\t\t\tevent.currentTarget = matched.elem;\n\n\t\t\tj = 0;\n\t\t\twhile ( ( handleObj = matched.handlers[ j++ ] ) &&\n\t\t\t\t!event.isImmediatePropagationStopped() ) {\n\n\t\t\t\t// If the event is namespaced, then each handler is only invoked if it is\n\t\t\t\t// specially universal or its namespaces are a superset of the event's.\n\t\t\t\tif ( !event.rnamespace || handleObj.namespace === false ||\n\t\t\t\t\tevent.rnamespace.test( handleObj.namespace ) ) {\n\n\t\t\t\t\tevent.handleObj = handleObj;\n\t\t\t\t\tevent.data = handleObj.data;\n\n\t\t\t\t\tret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||\n\t\t\t\t\t\thandleObj.handler ).apply( matched.elem, args );\n\n\t\t\t\t\tif ( ret !== undefined ) {\n\t\t\t\t\t\tif ( ( event.result = ret ) === false ) {\n\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Call the postDispatch hook for the mapped type\n\t\tif ( special.postDispatch ) {\n\t\t\tspecial.postDispatch.call( this, event );\n\t\t}\n\n\t\treturn event.result;\n\t},\n\n\thandlers: function( event, handlers ) {\n\t\tvar i, handleObj, sel, matchedHandlers, matchedSelectors,\n\t\t\thandlerQueue = [],\n\t\t\tdelegateCount = handlers.delegateCount,\n\t\t\tcur = event.target;\n\n\t\t// Find delegate handlers\n\t\tif ( delegateCount &&\n\n\t\t\t// Support: IE <=9\n\t\t\t// Black-hole SVG instance trees (trac-13180)\n\t\t\tcur.nodeType &&\n\n\t\t\t// Support: Firefox <=42\n\t\t\t// Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861)\n\t\t\t// https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click\n\t\t\t// Support: IE 11 only\n\t\t\t// ...but not arrow key \"clicks\" of radio inputs, which can have `button` -1 (gh-2343)\n\t\t\t!( event.type === \"click\" && event.button >= 1 ) ) {\n\n\t\t\tfor ( ; cur !== this; cur = cur.parentNode || this ) {\n\n\t\t\t\t// Don't check non-elements (#13208)\n\t\t\t\t// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)\n\t\t\t\tif ( cur.nodeType === 1 && !( event.type === \"click\" && cur.disabled === true ) ) {\n\t\t\t\t\tmatchedHandlers = [];\n\t\t\t\t\tmatchedSelectors = {};\n\t\t\t\t\tfor ( i = 0; i < delegateCount; i++ ) {\n\t\t\t\t\t\thandleObj = handlers[ i ];\n\n\t\t\t\t\t\t// Don't conflict with Object.prototype properties (#13203)\n\t\t\t\t\t\tsel = handleObj.selector + \" \";\n\n\t\t\t\t\t\tif ( matchedSelectors[ sel ] === undefined ) {\n\t\t\t\t\t\t\tmatchedSelectors[ sel ] = handleObj.needsContext ?\n\t\t\t\t\t\t\t\tjQuery( sel, this ).index( cur ) > -1 :\n\t\t\t\t\t\t\t\tjQuery.find( sel, this, null, [ cur ] ).length;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( matchedSelectors[ sel ] ) {\n\t\t\t\t\t\t\tmatchedHandlers.push( handleObj );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif ( matchedHandlers.length ) {\n\t\t\t\t\t\thandlerQueue.push( { elem: cur, handlers: matchedHandlers } );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Add the remaining (directly-bound) handlers\n\t\tcur = this;\n\t\tif ( delegateCount < handlers.length ) {\n\t\t\thandlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } );\n\t\t}\n\n\t\treturn handlerQueue;\n\t},\n\n\taddProp: function( name, hook ) {\n\t\tObject.defineProperty( jQuery.Event.prototype, name, {\n\t\t\tenumerable: true,\n\t\t\tconfigurable: true,\n\n\t\t\tget: isFunction( hook ) ?\n\t\t\t\tfunction() {\n\t\t\t\t\tif ( this.originalEvent ) {\n\t\t\t\t\t\t\treturn hook( this.originalEvent );\n\t\t\t\t\t}\n\t\t\t\t} :\n\t\t\t\tfunction() {\n\t\t\t\t\tif ( this.originalEvent ) {\n\t\t\t\t\t\t\treturn this.originalEvent[ name ];\n\t\t\t\t\t}\n\t\t\t\t},\n\n\t\t\tset: function( value ) {\n\t\t\t\tObject.defineProperty( this, name, {\n\t\t\t\t\tenumerable: true,\n\t\t\t\t\tconfigurable: true,\n\t\t\t\t\twritable: true,\n\t\t\t\t\tvalue: value\n\t\t\t\t} );\n\t\t\t}\n\t\t} );\n\t},\n\n\tfix: function( originalEvent ) {\n\t\treturn originalEvent[ jQuery.expando ] ?\n\t\t\toriginalEvent :\n\t\t\tnew jQuery.Event( originalEvent );\n\t},\n\n\tspecial: {\n\t\tload: {\n\n\t\t\t// Prevent triggered image.load events from bubbling to window.load\n\t\t\tnoBubble: true\n\t\t},\n\t\tclick: {\n\n\t\t\t// Utilize native event to ensure correct state for checkable inputs\n\t\t\tsetup: function( data ) {\n\n\t\t\t\t// For mutual compressibility with _default, replace `this` access with a local var.\n\t\t\t\t// `|| data` is dead code meant only to preserve the variable through minification.\n\t\t\t\tvar el = this || data;\n\n\t\t\t\t// Claim the first handler\n\t\t\t\tif ( rcheckableType.test( el.type ) &&\n\t\t\t\t\tel.click && nodeName( el, \"input\" ) ) {\n\n\t\t\t\t\t// dataPriv.set( el, \"click\", ... )\n\t\t\t\t\tleverageNative( el, \"click\", returnTrue );\n\t\t\t\t}\n\n\t\t\t\t// Return false to allow normal processing in the caller\n\t\t\t\treturn false;\n\t\t\t},\n\t\t\ttrigger: function( data ) {\n\n\t\t\t\t// For mutual compressibility with _default, replace `this` access with a local var.\n\t\t\t\t// `|| data` is dead code meant only to preserve the variable through minification.\n\t\t\t\tvar el = this || data;\n\n\t\t\t\t// Force setup before triggering a click\n\t\t\t\tif ( rcheckableType.test( el.type ) &&\n\t\t\t\t\tel.click && nodeName( el, \"input\" ) ) {\n\n\t\t\t\t\tleverageNative( el, \"click\" );\n\t\t\t\t}\n\n\t\t\t\t// Return non-false to allow normal event-path propagation\n\t\t\t\treturn true;\n\t\t\t},\n\n\t\t\t// For cross-browser consistency, suppress native .click() on links\n\t\t\t// Also prevent it if we're currently inside a leveraged native-event stack\n\t\t\t_default: function( event ) {\n\t\t\t\tvar target = event.target;\n\t\t\t\treturn rcheckableType.test( target.type ) &&\n\t\t\t\t\ttarget.click && nodeName( target, \"input\" ) &&\n\t\t\t\t\tdataPriv.get( target, \"click\" ) ||\n\t\t\t\t\tnodeName( target, \"a\" );\n\t\t\t}\n\t\t},\n\n\t\tbeforeunload: {\n\t\t\tpostDispatch: function( event ) {\n\n\t\t\t\t// Support: Firefox 20+\n\t\t\t\t// Firefox doesn't alert if the returnValue field is not set.\n\t\t\t\tif ( event.result !== undefined && event.originalEvent ) {\n\t\t\t\t\tevent.originalEvent.returnValue = event.result;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n};\n\n// Ensure the presence of an event listener that handles manually-triggered\n// synthetic events by interrupting progress until reinvoked in response to\n// *native* events that it fires directly, ensuring that state changes have\n// already occurred before other listeners are invoked.\nfunction leverageNative( el, type, expectSync ) {\n\n\t// Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add\n\tif ( !expectSync ) {\n\t\tif ( dataPriv.get( el, type ) === undefined ) {\n\t\t\tjQuery.event.add( el, type, returnTrue );\n\t\t}\n\t\treturn;\n\t}\n\n\t// Register the controller as a special universal handler for all event namespaces\n\tdataPriv.set( el, type, false );\n\tjQuery.event.add( el, type, {\n\t\tnamespace: false,\n\t\thandler: function( event ) {\n\t\t\tvar notAsync, result,\n\t\t\t\tsaved = dataPriv.get( this, type );\n\n\t\t\tif ( ( event.isTrigger & 1 ) && this[ type ] ) {\n\n\t\t\t\t// Interrupt processing of the outer synthetic .trigger()ed event\n\t\t\t\t// Saved data should be false in such cases, but might be a leftover capture object\n\t\t\t\t// from an async native handler (gh-4350)\n\t\t\t\tif ( !saved.length ) {\n\n\t\t\t\t\t// Store arguments for use when handling the inner native event\n\t\t\t\t\t// There will always be at least one argument (an event object), so this array\n\t\t\t\t\t// will not be confused with a leftover capture object.\n\t\t\t\t\tsaved = slice.call( arguments );\n\t\t\t\t\tdataPriv.set( this, type, saved );\n\n\t\t\t\t\t// Trigger the native event and capture its result\n\t\t\t\t\t// Support: IE <=9 - 11+\n\t\t\t\t\t// focus() and blur() are asynchronous\n\t\t\t\t\tnotAsync = expectSync( this, type );\n\t\t\t\t\tthis[ type ]();\n\t\t\t\t\tresult = dataPriv.get( this, type );\n\t\t\t\t\tif ( saved !== result || notAsync ) {\n\t\t\t\t\t\tdataPriv.set( this, type, false );\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult = {};\n\t\t\t\t\t}\n\t\t\t\t\tif ( saved !== result ) {\n\n\t\t\t\t\t\t// Cancel the outer synthetic event\n\t\t\t\t\t\tevent.stopImmediatePropagation();\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\treturn result.value;\n\t\t\t\t\t}\n\n\t\t\t\t// If this is an inner synthetic event for an event with a bubbling surrogate\n\t\t\t\t// (focus or blur), assume that the surrogate already propagated from triggering the\n\t\t\t\t// native event and prevent that from happening again here.\n\t\t\t\t// This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the\n\t\t\t\t// bubbling surrogate propagates *after* the non-bubbling base), but that seems\n\t\t\t\t// less bad than duplication.\n\t\t\t\t} else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) {\n\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t}\n\n\t\t\t// If this is a native event triggered above, everything is now in order\n\t\t\t// Fire an inner synthetic event with the original arguments\n\t\t\t} else if ( saved.length ) {\n\n\t\t\t\t// ...and capture the result\n\t\t\t\tdataPriv.set( this, type, {\n\t\t\t\t\tvalue: jQuery.event.trigger(\n\n\t\t\t\t\t\t// Support: IE <=9 - 11+\n\t\t\t\t\t\t// Extend with the prototype to reset the above stopImmediatePropagation()\n\t\t\t\t\t\tjQuery.extend( saved[ 0 ], jQuery.Event.prototype ),\n\t\t\t\t\t\tsaved.slice( 1 ),\n\t\t\t\t\t\tthis\n\t\t\t\t\t)\n\t\t\t\t} );\n\n\t\t\t\t// Abort handling of the native event\n\t\t\t\tevent.stopImmediatePropagation();\n\t\t\t}\n\t\t}\n\t} );\n}\n\njQuery.removeEvent = function( elem, type, handle ) {\n\n\t// This \"if\" is needed for plain objects\n\tif ( elem.removeEventListener ) {\n\t\telem.removeEventListener( type, handle );\n\t}\n};\n\njQuery.Event = function( src, props ) {\n\n\t// Allow instantiation without the 'new' keyword\n\tif ( !( this instanceof jQuery.Event ) ) {\n\t\treturn new jQuery.Event( src, props );\n\t}\n\n\t// Event object\n\tif ( src && src.type ) {\n\t\tthis.originalEvent = src;\n\t\tthis.type = src.type;\n\n\t\t// Events bubbling up the document may have been marked as prevented\n\t\t// by a handler lower down the tree; reflect the correct value.\n\t\tthis.isDefaultPrevented = src.defaultPrevented ||\n\t\t\t\tsrc.defaultPrevented === undefined &&\n\n\t\t\t\t// Support: Android <=2.3 only\n\t\t\t\tsrc.returnValue === false ?\n\t\t\treturnTrue :\n\t\t\treturnFalse;\n\n\t\t// Create target properties\n\t\t// Support: Safari <=6 - 7 only\n\t\t// Target should not be a text node (#504, #13143)\n\t\tthis.target = ( src.target && src.target.nodeType === 3 ) ?\n\t\t\tsrc.target.parentNode :\n\t\t\tsrc.target;\n\n\t\tthis.currentTarget = src.currentTarget;\n\t\tthis.relatedTarget = src.relatedTarget;\n\n\t// Event type\n\t} else {\n\t\tthis.type = src;\n\t}\n\n\t// Put explicitly provided properties onto the event object\n\tif ( props ) {\n\t\tjQuery.extend( this, props );\n\t}\n\n\t// Create a timestamp if incoming event doesn't have one\n\tthis.timeStamp = src && src.timeStamp || Date.now();\n\n\t// Mark it as fixed\n\tthis[ jQuery.expando ] = true;\n};\n\n// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding\n// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html\njQuery.Event.prototype = {\n\tconstructor: jQuery.Event,\n\tisDefaultPrevented: returnFalse,\n\tisPropagationStopped: returnFalse,\n\tisImmediatePropagationStopped: returnFalse,\n\tisSimulated: false,\n\n\tpreventDefault: function() {\n\t\tvar e = this.originalEvent;\n\n\t\tthis.isDefaultPrevented = returnTrue;\n\n\t\tif ( e && !this.isSimulated ) {\n\t\t\te.preventDefault();\n\t\t}\n\t},\n\tstopPropagation: function() {\n\t\tvar e = this.originalEvent;\n\n\t\tthis.isPropagationStopped = returnTrue;\n\n\t\tif ( e && !this.isSimulated ) {\n\t\t\te.stopPropagation();\n\t\t}\n\t},\n\tstopImmediatePropagation: function() {\n\t\tvar e = this.originalEvent;\n\n\t\tthis.isImmediatePropagationStopped = returnTrue;\n\n\t\tif ( e && !this.isSimulated ) {\n\t\t\te.stopImmediatePropagation();\n\t\t}\n\n\t\tthis.stopPropagation();\n\t}\n};\n\n// Includes all common event props including KeyEvent and MouseEvent specific props\njQuery.each( {\n\taltKey: true,\n\tbubbles: true,\n\tcancelable: true,\n\tchangedTouches: true,\n\tctrlKey: true,\n\tdetail: true,\n\teventPhase: true,\n\tmetaKey: true,\n\tpageX: true,\n\tpageY: true,\n\tshiftKey: true,\n\tview: true,\n\t\"char\": true,\n\tcode: true,\n\tcharCode: true,\n\tkey: true,\n\tkeyCode: true,\n\tbutton: true,\n\tbuttons: true,\n\tclientX: true,\n\tclientY: true,\n\toffsetX: true,\n\toffsetY: true,\n\tpointerId: true,\n\tpointerType: true,\n\tscreenX: true,\n\tscreenY: true,\n\ttargetTouches: true,\n\ttoElement: true,\n\ttouches: true,\n\n\twhich: function( event ) {\n\t\tvar button = event.button;\n\n\t\t// Add which for key events\n\t\tif ( event.which == null && rkeyEvent.test( event.type ) ) {\n\t\t\treturn event.charCode != null ? event.charCode : event.keyCode;\n\t\t}\n\n\t\t// Add which for click: 1 === left; 2 === middle; 3 === right\n\t\tif ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) {\n\t\t\tif ( button & 1 ) {\n\t\t\t\treturn 1;\n\t\t\t}\n\n\t\t\tif ( button & 2 ) {\n\t\t\t\treturn 3;\n\t\t\t}\n\n\t\t\tif ( button & 4 ) {\n\t\t\t\treturn 2;\n\t\t\t}\n\n\t\t\treturn 0;\n\t\t}\n\n\t\treturn event.which;\n\t}\n}, jQuery.event.addProp );\n\njQuery.each( { focus: \"focusin\", blur: \"focusout\" }, function( type, delegateType ) {\n\tjQuery.event.special[ type ] = {\n\n\t\t// Utilize native event if possible so blur/focus sequence is correct\n\t\tsetup: function() {\n\n\t\t\t// Claim the first handler\n\t\t\t// dataPriv.set( this, \"focus\", ... )\n\t\t\t// dataPriv.set( this, \"blur\", ... )\n\t\t\tleverageNative( this, type, expectSync );\n\n\t\t\t// Return false to allow normal processing in the caller\n\t\t\treturn false;\n\t\t},\n\t\ttrigger: function() {\n\n\t\t\t// Force setup before trigger\n\t\t\tleverageNative( this, type );\n\n\t\t\t// Return non-false to allow normal event-path propagation\n\t\t\treturn true;\n\t\t},\n\n\t\tdelegateType: delegateType\n\t};\n} );\n\n// Create mouseenter/leave events using mouseover/out and event-time checks\n// so that event delegation works in jQuery.\n// Do the same for pointerenter/pointerleave and pointerover/pointerout\n//\n// Support: Safari 7 only\n// Safari sends mouseenter too often; see:\n// https://bugs.chromium.org/p/chromium/issues/detail?id=470258\n// for the description of the bug (it existed in older Chrome versions as well).\njQuery.each( {\n\tmouseenter: \"mouseover\",\n\tmouseleave: \"mouseout\",\n\tpointerenter: \"pointerover\",\n\tpointerleave: \"pointerout\"\n}, function( orig, fix ) {\n\tjQuery.event.special[ orig ] = {\n\t\tdelegateType: fix,\n\t\tbindType: fix,\n\n\t\thandle: function( event ) {\n\t\t\tvar ret,\n\t\t\t\ttarget = this,\n\t\t\t\trelated = event.relatedTarget,\n\t\t\t\thandleObj = event.handleObj;\n\n\t\t\t// For mouseenter/leave call the handler if related is outside the target.\n\t\t\t// NB: No relatedTarget if the mouse left/entered the browser window\n\t\t\tif ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) {\n\t\t\t\tevent.type = handleObj.origType;\n\t\t\t\tret = handleObj.handler.apply( this, arguments );\n\t\t\t\tevent.type = fix;\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t};\n} );\n\njQuery.fn.extend( {\n\n\ton: function( types, selector, data, fn ) {\n\t\treturn on( this, types, selector, data, fn );\n\t},\n\tone: function( types, selector, data, fn ) {\n\t\treturn on( this, types, selector, data, fn, 1 );\n\t},\n\toff: function( types, selector, fn ) {\n\t\tvar handleObj, type;\n\t\tif ( types && types.preventDefault && types.handleObj ) {\n\n\t\t\t// ( event ) dispatched jQuery.Event\n\t\t\thandleObj = types.handleObj;\n\t\t\tjQuery( types.delegateTarget ).off(\n\t\t\t\thandleObj.namespace ?\n\t\t\t\t\thandleObj.origType + \".\" + handleObj.namespace :\n\t\t\t\t\thandleObj.origType,\n\t\t\t\thandleObj.selector,\n\t\t\t\thandleObj.handler\n\t\t\t);\n\t\t\treturn this;\n\t\t}\n\t\tif ( typeof types === \"object\" ) {\n\n\t\t\t// ( types-object [, selector] )\n\t\t\tfor ( type in types ) {\n\t\t\t\tthis.off( type, selector, types[ type ] );\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\t\tif ( selector === false || typeof selector === \"function\" ) {\n\n\t\t\t// ( types [, fn] )\n\t\t\tfn = selector;\n\t\t\tselector = undefined;\n\t\t}\n\t\tif ( fn === false ) {\n\t\t\tfn = returnFalse;\n\t\t}\n\t\treturn this.each( function() {\n\t\t\tjQuery.event.remove( this, types, fn, selector );\n\t\t} );\n\t}\n} );\n\n\nvar\n\n\t// Support: IE <=10 - 11, Edge 12 - 13 only\n\t// In IE/Edge using regex groups here causes severe slowdowns.\n\t// See https://connect.microsoft.com/IE/feedback/details/1736512/\n\trnoInnerhtml = /\\s*$/g;\n\n// Prefer a tbody over its parent table for containing new rows\nfunction manipulationTarget( elem, content ) {\n\tif ( nodeName( elem, \"table\" ) &&\n\t\tnodeName( content.nodeType !== 11 ? content : content.firstChild, \"tr\" ) ) {\n\n\t\treturn jQuery( elem ).children( \"tbody\" )[ 0 ] || elem;\n\t}\n\n\treturn elem;\n}\n\n// Replace/restore the type attribute of script elements for safe DOM manipulation\nfunction disableScript( elem ) {\n\telem.type = ( elem.getAttribute( \"type\" ) !== null ) + \"/\" + elem.type;\n\treturn elem;\n}\nfunction restoreScript( elem ) {\n\tif ( ( elem.type || \"\" ).slice( 0, 5 ) === \"true/\" ) {\n\t\telem.type = elem.type.slice( 5 );\n\t} else {\n\t\telem.removeAttribute( \"type\" );\n\t}\n\n\treturn elem;\n}\n\nfunction cloneCopyEvent( src, dest ) {\n\tvar i, l, type, pdataOld, udataOld, udataCur, events;\n\n\tif ( dest.nodeType !== 1 ) {\n\t\treturn;\n\t}\n\n\t// 1. Copy private data: events, handlers, etc.\n\tif ( dataPriv.hasData( src ) ) {\n\t\tpdataOld = dataPriv.get( src );\n\t\tevents = pdataOld.events;\n\n\t\tif ( events ) {\n\t\t\tdataPriv.remove( dest, \"handle events\" );\n\n\t\t\tfor ( type in events ) {\n\t\t\t\tfor ( i = 0, l = events[ type ].length; i < l; i++ ) {\n\t\t\t\t\tjQuery.event.add( dest, type, events[ type ][ i ] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// 2. Copy user data\n\tif ( dataUser.hasData( src ) ) {\n\t\tudataOld = dataUser.access( src );\n\t\tudataCur = jQuery.extend( {}, udataOld );\n\n\t\tdataUser.set( dest, udataCur );\n\t}\n}\n\n// Fix IE bugs, see support tests\nfunction fixInput( src, dest ) {\n\tvar nodeName = dest.nodeName.toLowerCase();\n\n\t// Fails to persist the checked state of a cloned checkbox or radio button.\n\tif ( nodeName === \"input\" && rcheckableType.test( src.type ) ) {\n\t\tdest.checked = src.checked;\n\n\t// Fails to return the selected option to the default selected state when cloning options\n\t} else if ( nodeName === \"input\" || nodeName === \"textarea\" ) {\n\t\tdest.defaultValue = src.defaultValue;\n\t}\n}\n\nfunction domManip( collection, args, callback, ignored ) {\n\n\t// Flatten any nested arrays\n\targs = flat( args );\n\n\tvar fragment, first, scripts, hasScripts, node, doc,\n\t\ti = 0,\n\t\tl = collection.length,\n\t\tiNoClone = l - 1,\n\t\tvalue = args[ 0 ],\n\t\tvalueIsFunction = isFunction( value );\n\n\t// We can't cloneNode fragments that contain checked, in WebKit\n\tif ( valueIsFunction ||\n\t\t\t( l > 1 && typeof value === \"string\" &&\n\t\t\t\t!support.checkClone && rchecked.test( value ) ) ) {\n\t\treturn collection.each( function( index ) {\n\t\t\tvar self = collection.eq( index );\n\t\t\tif ( valueIsFunction ) {\n\t\t\t\targs[ 0 ] = value.call( this, index, self.html() );\n\t\t\t}\n\t\t\tdomManip( self, args, callback, ignored );\n\t\t} );\n\t}\n\n\tif ( l ) {\n\t\tfragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );\n\t\tfirst = fragment.firstChild;\n\n\t\tif ( fragment.childNodes.length === 1 ) {\n\t\t\tfragment = first;\n\t\t}\n\n\t\t// Require either new content or an interest in ignored elements to invoke the callback\n\t\tif ( first || ignored ) {\n\t\t\tscripts = jQuery.map( getAll( fragment, \"script\" ), disableScript );\n\t\t\thasScripts = scripts.length;\n\n\t\t\t// Use the original fragment for the last item\n\t\t\t// instead of the first because it can end up\n\t\t\t// being emptied incorrectly in certain situations (#8070).\n\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\tnode = fragment;\n\n\t\t\t\tif ( i !== iNoClone ) {\n\t\t\t\t\tnode = jQuery.clone( node, true, true );\n\n\t\t\t\t\t// Keep references to cloned scripts for later restoration\n\t\t\t\t\tif ( hasScripts ) {\n\n\t\t\t\t\t\t// Support: Android <=4.0 only, PhantomJS 1 only\n\t\t\t\t\t\t// push.apply(_, arraylike) throws on ancient WebKit\n\t\t\t\t\t\tjQuery.merge( scripts, getAll( node, \"script\" ) );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcallback.call( collection[ i ], node, i );\n\t\t\t}\n\n\t\t\tif ( hasScripts ) {\n\t\t\t\tdoc = scripts[ scripts.length - 1 ].ownerDocument;\n\n\t\t\t\t// Reenable scripts\n\t\t\t\tjQuery.map( scripts, restoreScript );\n\n\t\t\t\t// Evaluate executable scripts on first document insertion\n\t\t\t\tfor ( i = 0; i < hasScripts; i++ ) {\n\t\t\t\t\tnode = scripts[ i ];\n\t\t\t\t\tif ( rscriptType.test( node.type || \"\" ) &&\n\t\t\t\t\t\t!dataPriv.access( node, \"globalEval\" ) &&\n\t\t\t\t\t\tjQuery.contains( doc, node ) ) {\n\n\t\t\t\t\t\tif ( node.src && ( node.type || \"\" ).toLowerCase() !== \"module\" ) {\n\n\t\t\t\t\t\t\t// Optional AJAX dependency, but won't run scripts if not present\n\t\t\t\t\t\t\tif ( jQuery._evalUrl && !node.noModule ) {\n\t\t\t\t\t\t\t\tjQuery._evalUrl( node.src, {\n\t\t\t\t\t\t\t\t\tnonce: node.nonce || node.getAttribute( \"nonce\" )\n\t\t\t\t\t\t\t\t}, doc );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tDOMEval( node.textContent.replace( rcleanScript, \"\" ), node, doc );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn collection;\n}\n\nfunction remove( elem, selector, keepData ) {\n\tvar node,\n\t\tnodes = selector ? jQuery.filter( selector, elem ) : elem,\n\t\ti = 0;\n\n\tfor ( ; ( node = nodes[ i ] ) != null; i++ ) {\n\t\tif ( !keepData && node.nodeType === 1 ) {\n\t\t\tjQuery.cleanData( getAll( node ) );\n\t\t}\n\n\t\tif ( node.parentNode ) {\n\t\t\tif ( keepData && isAttached( node ) ) {\n\t\t\t\tsetGlobalEval( getAll( node, \"script\" ) );\n\t\t\t}\n\t\t\tnode.parentNode.removeChild( node );\n\t\t}\n\t}\n\n\treturn elem;\n}\n\njQuery.extend( {\n\thtmlPrefilter: function( html ) {\n\t\treturn html;\n\t},\n\n\tclone: function( elem, dataAndEvents, deepDataAndEvents ) {\n\t\tvar i, l, srcElements, destElements,\n\t\t\tclone = elem.cloneNode( true ),\n\t\t\tinPage = isAttached( elem );\n\n\t\t// Fix IE cloning issues\n\t\tif ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&\n\t\t\t\t!jQuery.isXMLDoc( elem ) ) {\n\n\t\t\t// We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2\n\t\t\tdestElements = getAll( clone );\n\t\t\tsrcElements = getAll( elem );\n\n\t\t\tfor ( i = 0, l = srcElements.length; i < l; i++ ) {\n\t\t\t\tfixInput( srcElements[ i ], destElements[ i ] );\n\t\t\t}\n\t\t}\n\n\t\t// Copy the events from the original to the clone\n\t\tif ( dataAndEvents ) {\n\t\t\tif ( deepDataAndEvents ) {\n\t\t\t\tsrcElements = srcElements || getAll( elem );\n\t\t\t\tdestElements = destElements || getAll( clone );\n\n\t\t\t\tfor ( i = 0, l = srcElements.length; i < l; i++ ) {\n\t\t\t\t\tcloneCopyEvent( srcElements[ i ], destElements[ i ] );\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcloneCopyEvent( elem, clone );\n\t\t\t}\n\t\t}\n\n\t\t// Preserve script evaluation history\n\t\tdestElements = getAll( clone, \"script\" );\n\t\tif ( destElements.length > 0 ) {\n\t\t\tsetGlobalEval( destElements, !inPage && getAll( elem, \"script\" ) );\n\t\t}\n\n\t\t// Return the cloned set\n\t\treturn clone;\n\t},\n\n\tcleanData: function( elems ) {\n\t\tvar data, elem, type,\n\t\t\tspecial = jQuery.event.special,\n\t\t\ti = 0;\n\n\t\tfor ( ; ( elem = elems[ i ] ) !== undefined; i++ ) {\n\t\t\tif ( acceptData( elem ) ) {\n\t\t\t\tif ( ( data = elem[ dataPriv.expando ] ) ) {\n\t\t\t\t\tif ( data.events ) {\n\t\t\t\t\t\tfor ( type in data.events ) {\n\t\t\t\t\t\t\tif ( special[ type ] ) {\n\t\t\t\t\t\t\t\tjQuery.event.remove( elem, type );\n\n\t\t\t\t\t\t\t// This is a shortcut to avoid jQuery.event.remove's overhead\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tjQuery.removeEvent( elem, type, data.handle );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Support: Chrome <=35 - 45+\n\t\t\t\t\t// Assign undefined instead of using delete, see Data#remove\n\t\t\t\t\telem[ dataPriv.expando ] = undefined;\n\t\t\t\t}\n\t\t\t\tif ( elem[ dataUser.expando ] ) {\n\n\t\t\t\t\t// Support: Chrome <=35 - 45+\n\t\t\t\t\t// Assign undefined instead of using delete, see Data#remove\n\t\t\t\t\telem[ dataUser.expando ] = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n} );\n\njQuery.fn.extend( {\n\tdetach: function( selector ) {\n\t\treturn remove( this, selector, true );\n\t},\n\n\tremove: function( selector ) {\n\t\treturn remove( this, selector );\n\t},\n\n\ttext: function( value ) {\n\t\treturn access( this, function( value ) {\n\t\t\treturn value === undefined ?\n\t\t\t\tjQuery.text( this ) :\n\t\t\t\tthis.empty().each( function() {\n\t\t\t\t\tif ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {\n\t\t\t\t\t\tthis.textContent = value;\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t}, null, value, arguments.length );\n\t},\n\n\tappend: function() {\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tif ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {\n\t\t\t\tvar target = manipulationTarget( this, elem );\n\t\t\t\ttarget.appendChild( elem );\n\t\t\t}\n\t\t} );\n\t},\n\n\tprepend: function() {\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tif ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {\n\t\t\t\tvar target = manipulationTarget( this, elem );\n\t\t\t\ttarget.insertBefore( elem, target.firstChild );\n\t\t\t}\n\t\t} );\n\t},\n\n\tbefore: function() {\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tif ( this.parentNode ) {\n\t\t\t\tthis.parentNode.insertBefore( elem, this );\n\t\t\t}\n\t\t} );\n\t},\n\n\tafter: function() {\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tif ( this.parentNode ) {\n\t\t\t\tthis.parentNode.insertBefore( elem, this.nextSibling );\n\t\t\t}\n\t\t} );\n\t},\n\n\tempty: function() {\n\t\tvar elem,\n\t\t\ti = 0;\n\n\t\tfor ( ; ( elem = this[ i ] ) != null; i++ ) {\n\t\t\tif ( elem.nodeType === 1 ) {\n\n\t\t\t\t// Prevent memory leaks\n\t\t\t\tjQuery.cleanData( getAll( elem, false ) );\n\n\t\t\t\t// Remove any remaining nodes\n\t\t\t\telem.textContent = \"\";\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tclone: function( dataAndEvents, deepDataAndEvents ) {\n\t\tdataAndEvents = dataAndEvents == null ? false : dataAndEvents;\n\t\tdeepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;\n\n\t\treturn this.map( function() {\n\t\t\treturn jQuery.clone( this, dataAndEvents, deepDataAndEvents );\n\t\t} );\n\t},\n\n\thtml: function( value ) {\n\t\treturn access( this, function( value ) {\n\t\t\tvar elem = this[ 0 ] || {},\n\t\t\t\ti = 0,\n\t\t\t\tl = this.length;\n\n\t\t\tif ( value === undefined && elem.nodeType === 1 ) {\n\t\t\t\treturn elem.innerHTML;\n\t\t\t}\n\n\t\t\t// See if we can take a shortcut and just use innerHTML\n\t\t\tif ( typeof value === \"string\" && !rnoInnerhtml.test( value ) &&\n\t\t\t\t!wrapMap[ ( rtagName.exec( value ) || [ \"\", \"\" ] )[ 1 ].toLowerCase() ] ) {\n\n\t\t\t\tvalue = jQuery.htmlPrefilter( value );\n\n\t\t\t\ttry {\n\t\t\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\t\t\telem = this[ i ] || {};\n\n\t\t\t\t\t\t// Remove element nodes and prevent memory leaks\n\t\t\t\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\t\t\t\tjQuery.cleanData( getAll( elem, false ) );\n\t\t\t\t\t\t\telem.innerHTML = value;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\telem = 0;\n\n\t\t\t\t// If using innerHTML throws an exception, use the fallback method\n\t\t\t\t} catch ( e ) {}\n\t\t\t}\n\n\t\t\tif ( elem ) {\n\t\t\t\tthis.empty().append( value );\n\t\t\t}\n\t\t}, null, value, arguments.length );\n\t},\n\n\treplaceWith: function() {\n\t\tvar ignored = [];\n\n\t\t// Make the changes, replacing each non-ignored context element with the new content\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tvar parent = this.parentNode;\n\n\t\t\tif ( jQuery.inArray( this, ignored ) < 0 ) {\n\t\t\t\tjQuery.cleanData( getAll( this ) );\n\t\t\t\tif ( parent ) {\n\t\t\t\t\tparent.replaceChild( elem, this );\n\t\t\t\t}\n\t\t\t}\n\n\t\t// Force callback invocation\n\t\t}, ignored );\n\t}\n} );\n\njQuery.each( {\n\tappendTo: \"append\",\n\tprependTo: \"prepend\",\n\tinsertBefore: \"before\",\n\tinsertAfter: \"after\",\n\treplaceAll: \"replaceWith\"\n}, function( name, original ) {\n\tjQuery.fn[ name ] = function( selector ) {\n\t\tvar elems,\n\t\t\tret = [],\n\t\t\tinsert = jQuery( selector ),\n\t\t\tlast = insert.length - 1,\n\t\t\ti = 0;\n\n\t\tfor ( ; i <= last; i++ ) {\n\t\t\telems = i === last ? this : this.clone( true );\n\t\t\tjQuery( insert[ i ] )[ original ]( elems );\n\n\t\t\t// Support: Android <=4.0 only, PhantomJS 1 only\n\t\t\t// .get() because push.apply(_, arraylike) throws on ancient WebKit\n\t\t\tpush.apply( ret, elems.get() );\n\t\t}\n\n\t\treturn this.pushStack( ret );\n\t};\n} );\nvar rnumnonpx = new RegExp( \"^(\" + pnum + \")(?!px)[a-z%]+$\", \"i\" );\n\nvar getStyles = function( elem ) {\n\n\t\t// Support: IE <=11 only, Firefox <=30 (#15098, #14150)\n\t\t// IE throws on elements created in popups\n\t\t// FF meanwhile throws on frame elements through \"defaultView.getComputedStyle\"\n\t\tvar view = elem.ownerDocument.defaultView;\n\n\t\tif ( !view || !view.opener ) {\n\t\t\tview = window;\n\t\t}\n\n\t\treturn view.getComputedStyle( elem );\n\t};\n\nvar swap = function( elem, options, callback ) {\n\tvar ret, name,\n\t\told = {};\n\n\t// Remember the old values, and insert the new ones\n\tfor ( name in options ) {\n\t\told[ name ] = elem.style[ name ];\n\t\telem.style[ name ] = options[ name ];\n\t}\n\n\tret = callback.call( elem );\n\n\t// Revert the old values\n\tfor ( name in options ) {\n\t\telem.style[ name ] = old[ name ];\n\t}\n\n\treturn ret;\n};\n\n\nvar rboxStyle = new RegExp( cssExpand.join( \"|\" ), \"i\" );\n\n\n\n( function() {\n\n\t// Executing both pixelPosition & boxSizingReliable tests require only one layout\n\t// so they're executed at the same time to save the second computation.\n\tfunction computeStyleTests() {\n\n\t\t// This is a singleton, we need to execute it only once\n\t\tif ( !div ) {\n\t\t\treturn;\n\t\t}\n\n\t\tcontainer.style.cssText = \"position:absolute;left:-11111px;width:60px;\" +\n\t\t\t\"margin-top:1px;padding:0;border:0\";\n\t\tdiv.style.cssText =\n\t\t\t\"position:relative;display:block;box-sizing:border-box;overflow:scroll;\" +\n\t\t\t\"margin:auto;border:1px;padding:1px;\" +\n\t\t\t\"width:60%;top:1%\";\n\t\tdocumentElement.appendChild( container ).appendChild( div );\n\n\t\tvar divStyle = window.getComputedStyle( div );\n\t\tpixelPositionVal = divStyle.top !== \"1%\";\n\n\t\t// Support: Android 4.0 - 4.3 only, Firefox <=3 - 44\n\t\treliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12;\n\n\t\t// Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3\n\t\t// Some styles come back with percentage values, even though they shouldn't\n\t\tdiv.style.right = \"60%\";\n\t\tpixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36;\n\n\t\t// Support: IE 9 - 11 only\n\t\t// Detect misreporting of content dimensions for box-sizing:border-box elements\n\t\tboxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36;\n\n\t\t// Support: IE 9 only\n\t\t// Detect overflow:scroll screwiness (gh-3699)\n\t\t// Support: Chrome <=64\n\t\t// Don't get tricked when zoom affects offsetWidth (gh-4029)\n\t\tdiv.style.position = \"absolute\";\n\t\tscrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12;\n\n\t\tdocumentElement.removeChild( container );\n\n\t\t// Nullify the div so it wouldn't be stored in the memory and\n\t\t// it will also be a sign that checks already performed\n\t\tdiv = null;\n\t}\n\n\tfunction roundPixelMeasures( measure ) {\n\t\treturn Math.round( parseFloat( measure ) );\n\t}\n\n\tvar pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal,\n\t\treliableTrDimensionsVal, reliableMarginLeftVal,\n\t\tcontainer = document.createElement( \"div\" ),\n\t\tdiv = document.createElement( \"div\" );\n\n\t// Finish early in limited (non-browser) environments\n\tif ( !div.style ) {\n\t\treturn;\n\t}\n\n\t// Support: IE <=9 - 11 only\n\t// Style of cloned element affects source element cloned (#8908)\n\tdiv.style.backgroundClip = \"content-box\";\n\tdiv.cloneNode( true ).style.backgroundClip = \"\";\n\tsupport.clearCloneStyle = div.style.backgroundClip === \"content-box\";\n\n\tjQuery.extend( support, {\n\t\tboxSizingReliable: function() {\n\t\t\tcomputeStyleTests();\n\t\t\treturn boxSizingReliableVal;\n\t\t},\n\t\tpixelBoxStyles: function() {\n\t\t\tcomputeStyleTests();\n\t\t\treturn pixelBoxStylesVal;\n\t\t},\n\t\tpixelPosition: function() {\n\t\t\tcomputeStyleTests();\n\t\t\treturn pixelPositionVal;\n\t\t},\n\t\treliableMarginLeft: function() {\n\t\t\tcomputeStyleTests();\n\t\t\treturn reliableMarginLeftVal;\n\t\t},\n\t\tscrollboxSize: function() {\n\t\t\tcomputeStyleTests();\n\t\t\treturn scrollboxSizeVal;\n\t\t},\n\n\t\t// Support: IE 9 - 11+, Edge 15 - 18+\n\t\t// IE/Edge misreport `getComputedStyle` of table rows with width/height\n\t\t// set in CSS while `offset*` properties report correct values.\n\t\t// Behavior in IE 9 is more subtle than in newer versions & it passes\n\t\t// some versions of this test; make sure not to make it pass there!\n\t\treliableTrDimensions: function() {\n\t\t\tvar table, tr, trChild, trStyle;\n\t\t\tif ( reliableTrDimensionsVal == null ) {\n\t\t\t\ttable = document.createElement( \"table\" );\n\t\t\t\ttr = document.createElement( \"tr\" );\n\t\t\t\ttrChild = document.createElement( \"div\" );\n\n\t\t\t\ttable.style.cssText = \"position:absolute;left:-11111px\";\n\t\t\t\ttr.style.height = \"1px\";\n\t\t\t\ttrChild.style.height = \"9px\";\n\n\t\t\t\tdocumentElement\n\t\t\t\t\t.appendChild( table )\n\t\t\t\t\t.appendChild( tr )\n\t\t\t\t\t.appendChild( trChild );\n\n\t\t\t\ttrStyle = window.getComputedStyle( tr );\n\t\t\t\treliableTrDimensionsVal = parseInt( trStyle.height ) > 3;\n\n\t\t\t\tdocumentElement.removeChild( table );\n\t\t\t}\n\t\t\treturn reliableTrDimensionsVal;\n\t\t}\n\t} );\n} )();\n\n\nfunction curCSS( elem, name, computed ) {\n\tvar width, minWidth, maxWidth, ret,\n\n\t\t// Support: Firefox 51+\n\t\t// Retrieving style before computed somehow\n\t\t// fixes an issue with getting wrong values\n\t\t// on detached elements\n\t\tstyle = elem.style;\n\n\tcomputed = computed || getStyles( elem );\n\n\t// getPropertyValue is needed for:\n\t// .css('filter') (IE 9 only, #12537)\n\t// .css('--customProperty) (#3144)\n\tif ( computed ) {\n\t\tret = computed.getPropertyValue( name ) || computed[ name ];\n\n\t\tif ( ret === \"\" && !isAttached( elem ) ) {\n\t\t\tret = jQuery.style( elem, name );\n\t\t}\n\n\t\t// A tribute to the \"awesome hack by Dean Edwards\"\n\t\t// Android Browser returns percentage for some values,\n\t\t// but width seems to be reliably pixels.\n\t\t// This is against the CSSOM draft spec:\n\t\t// https://drafts.csswg.org/cssom/#resolved-values\n\t\tif ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) {\n\n\t\t\t// Remember the original values\n\t\t\twidth = style.width;\n\t\t\tminWidth = style.minWidth;\n\t\t\tmaxWidth = style.maxWidth;\n\n\t\t\t// Put in the new values to get a computed value out\n\t\t\tstyle.minWidth = style.maxWidth = style.width = ret;\n\t\t\tret = computed.width;\n\n\t\t\t// Revert the changed values\n\t\t\tstyle.width = width;\n\t\t\tstyle.minWidth = minWidth;\n\t\t\tstyle.maxWidth = maxWidth;\n\t\t}\n\t}\n\n\treturn ret !== undefined ?\n\n\t\t// Support: IE <=9 - 11 only\n\t\t// IE returns zIndex value as an integer.\n\t\tret + \"\" :\n\t\tret;\n}\n\n\nfunction addGetHookIf( conditionFn, hookFn ) {\n\n\t// Define the hook, we'll check on the first run if it's really needed.\n\treturn {\n\t\tget: function() {\n\t\t\tif ( conditionFn() ) {\n\n\t\t\t\t// Hook not needed (or it's not possible to use it due\n\t\t\t\t// to missing dependency), remove it.\n\t\t\t\tdelete this.get;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Hook needed; redefine it so that the support test is not executed again.\n\t\t\treturn ( this.get = hookFn ).apply( this, arguments );\n\t\t}\n\t};\n}\n\n\nvar cssPrefixes = [ \"Webkit\", \"Moz\", \"ms\" ],\n\temptyStyle = document.createElement( \"div\" ).style,\n\tvendorProps = {};\n\n// Return a vendor-prefixed property or undefined\nfunction vendorPropName( name ) {\n\n\t// Check for vendor prefixed names\n\tvar capName = name[ 0 ].toUpperCase() + name.slice( 1 ),\n\t\ti = cssPrefixes.length;\n\n\twhile ( i-- ) {\n\t\tname = cssPrefixes[ i ] + capName;\n\t\tif ( name in emptyStyle ) {\n\t\t\treturn name;\n\t\t}\n\t}\n}\n\n// Return a potentially-mapped jQuery.cssProps or vendor prefixed property\nfunction finalPropName( name ) {\n\tvar final = jQuery.cssProps[ name ] || vendorProps[ name ];\n\n\tif ( final ) {\n\t\treturn final;\n\t}\n\tif ( name in emptyStyle ) {\n\t\treturn name;\n\t}\n\treturn vendorProps[ name ] = vendorPropName( name ) || name;\n}\n\n\nvar\n\n\t// Swappable if display is none or starts with table\n\t// except \"table\", \"table-cell\", or \"table-caption\"\n\t// See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display\n\trdisplayswap = /^(none|table(?!-c[ea]).+)/,\n\trcustomProp = /^--/,\n\tcssShow = { position: \"absolute\", visibility: \"hidden\", display: \"block\" },\n\tcssNormalTransform = {\n\t\tletterSpacing: \"0\",\n\t\tfontWeight: \"400\"\n\t};\n\nfunction setPositiveNumber( _elem, value, subtract ) {\n\n\t// Any relative (+/-) values have already been\n\t// normalized at this point\n\tvar matches = rcssNum.exec( value );\n\treturn matches ?\n\n\t\t// Guard against undefined \"subtract\", e.g., when used as in cssHooks\n\t\tMath.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || \"px\" ) :\n\t\tvalue;\n}\n\nfunction boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) {\n\tvar i = dimension === \"width\" ? 1 : 0,\n\t\textra = 0,\n\t\tdelta = 0;\n\n\t// Adjustment may not be necessary\n\tif ( box === ( isBorderBox ? \"border\" : \"content\" ) ) {\n\t\treturn 0;\n\t}\n\n\tfor ( ; i < 4; i += 2 ) {\n\n\t\t// Both box models exclude margin\n\t\tif ( box === \"margin\" ) {\n\t\t\tdelta += jQuery.css( elem, box + cssExpand[ i ], true, styles );\n\t\t}\n\n\t\t// If we get here with a content-box, we're seeking \"padding\" or \"border\" or \"margin\"\n\t\tif ( !isBorderBox ) {\n\n\t\t\t// Add padding\n\t\t\tdelta += jQuery.css( elem, \"padding\" + cssExpand[ i ], true, styles );\n\n\t\t\t// For \"border\" or \"margin\", add border\n\t\t\tif ( box !== \"padding\" ) {\n\t\t\t\tdelta += jQuery.css( elem, \"border\" + cssExpand[ i ] + \"Width\", true, styles );\n\n\t\t\t// But still keep track of it otherwise\n\t\t\t} else {\n\t\t\t\textra += jQuery.css( elem, \"border\" + cssExpand[ i ] + \"Width\", true, styles );\n\t\t\t}\n\n\t\t// If we get here with a border-box (content + padding + border), we're seeking \"content\" or\n\t\t// \"padding\" or \"margin\"\n\t\t} else {\n\n\t\t\t// For \"content\", subtract padding\n\t\t\tif ( box === \"content\" ) {\n\t\t\t\tdelta -= jQuery.css( elem, \"padding\" + cssExpand[ i ], true, styles );\n\t\t\t}\n\n\t\t\t// For \"content\" or \"padding\", subtract border\n\t\t\tif ( box !== \"margin\" ) {\n\t\t\t\tdelta -= jQuery.css( elem, \"border\" + cssExpand[ i ] + \"Width\", true, styles );\n\t\t\t}\n\t\t}\n\t}\n\n\t// Account for positive content-box scroll gutter when requested by providing computedVal\n\tif ( !isBorderBox && computedVal >= 0 ) {\n\n\t\t// offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border\n\t\t// Assuming integer scroll gutter, subtract the rest and round down\n\t\tdelta += Math.max( 0, Math.ceil(\n\t\t\telem[ \"offset\" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -\n\t\t\tcomputedVal -\n\t\t\tdelta -\n\t\t\textra -\n\t\t\t0.5\n\n\t\t// If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter\n\t\t// Use an explicit zero to avoid NaN (gh-3964)\n\t\t) ) || 0;\n\t}\n\n\treturn delta;\n}\n\nfunction getWidthOrHeight( elem, dimension, extra ) {\n\n\t// Start with computed style\n\tvar styles = getStyles( elem ),\n\n\t\t// To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322).\n\t\t// Fake content-box until we know it's needed to know the true value.\n\t\tboxSizingNeeded = !support.boxSizingReliable() || extra,\n\t\tisBorderBox = boxSizingNeeded &&\n\t\t\tjQuery.css( elem, \"boxSizing\", false, styles ) === \"border-box\",\n\t\tvalueIsBorderBox = isBorderBox,\n\n\t\tval = curCSS( elem, dimension, styles ),\n\t\toffsetProp = \"offset\" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 );\n\n\t// Support: Firefox <=54\n\t// Return a confounding non-pixel value or feign ignorance, as appropriate.\n\tif ( rnumnonpx.test( val ) ) {\n\t\tif ( !extra ) {\n\t\t\treturn val;\n\t\t}\n\t\tval = \"auto\";\n\t}\n\n\n\t// Support: IE 9 - 11 only\n\t// Use offsetWidth/offsetHeight for when box sizing is unreliable.\n\t// In those cases, the computed value can be trusted to be border-box.\n\tif ( ( !support.boxSizingReliable() && isBorderBox ||\n\n\t\t// Support: IE 10 - 11+, Edge 15 - 18+\n\t\t// IE/Edge misreport `getComputedStyle` of table rows with width/height\n\t\t// set in CSS while `offset*` properties report correct values.\n\t\t// Interestingly, in some cases IE 9 doesn't suffer from this issue.\n\t\t!support.reliableTrDimensions() && nodeName( elem, \"tr\" ) ||\n\n\t\t// Fall back to offsetWidth/offsetHeight when value is \"auto\"\n\t\t// This happens for inline elements with no explicit setting (gh-3571)\n\t\tval === \"auto\" ||\n\n\t\t// Support: Android <=4.1 - 4.3 only\n\t\t// Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602)\n\t\t!parseFloat( val ) && jQuery.css( elem, \"display\", false, styles ) === \"inline\" ) &&\n\n\t\t// Make sure the element is visible & connected\n\t\telem.getClientRects().length ) {\n\n\t\tisBorderBox = jQuery.css( elem, \"boxSizing\", false, styles ) === \"border-box\";\n\n\t\t// Where available, offsetWidth/offsetHeight approximate border box dimensions.\n\t\t// Where not available (e.g., SVG), assume unreliable box-sizing and interpret the\n\t\t// retrieved value as a content box dimension.\n\t\tvalueIsBorderBox = offsetProp in elem;\n\t\tif ( valueIsBorderBox ) {\n\t\t\tval = elem[ offsetProp ];\n\t\t}\n\t}\n\n\t// Normalize \"\" and auto\n\tval = parseFloat( val ) || 0;\n\n\t// Adjust for the element's box model\n\treturn ( val +\n\t\tboxModelAdjustment(\n\t\t\telem,\n\t\t\tdimension,\n\t\t\textra || ( isBorderBox ? \"border\" : \"content\" ),\n\t\t\tvalueIsBorderBox,\n\t\t\tstyles,\n\n\t\t\t// Provide the current computed size to request scroll gutter calculation (gh-3589)\n\t\t\tval\n\t\t)\n\t) + \"px\";\n}\n\njQuery.extend( {\n\n\t// Add in style property hooks for overriding the default\n\t// behavior of getting and setting a style property\n\tcssHooks: {\n\t\topacity: {\n\t\t\tget: function( elem, computed ) {\n\t\t\t\tif ( computed ) {\n\n\t\t\t\t\t// We should always get a number back from opacity\n\t\t\t\t\tvar ret = curCSS( elem, \"opacity\" );\n\t\t\t\t\treturn ret === \"\" ? \"1\" : ret;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\t// Don't automatically add \"px\" to these possibly-unitless properties\n\tcssNumber: {\n\t\t\"animationIterationCount\": true,\n\t\t\"columnCount\": true,\n\t\t\"fillOpacity\": true,\n\t\t\"flexGrow\": true,\n\t\t\"flexShrink\": true,\n\t\t\"fontWeight\": true,\n\t\t\"gridArea\": true,\n\t\t\"gridColumn\": true,\n\t\t\"gridColumnEnd\": true,\n\t\t\"gridColumnStart\": true,\n\t\t\"gridRow\": true,\n\t\t\"gridRowEnd\": true,\n\t\t\"gridRowStart\": true,\n\t\t\"lineHeight\": true,\n\t\t\"opacity\": true,\n\t\t\"order\": true,\n\t\t\"orphans\": true,\n\t\t\"widows\": true,\n\t\t\"zIndex\": true,\n\t\t\"zoom\": true\n\t},\n\n\t// Add in properties whose names you wish to fix before\n\t// setting or getting the value\n\tcssProps: {},\n\n\t// Get and set the style property on a DOM Node\n\tstyle: function( elem, name, value, extra ) {\n\n\t\t// Don't set styles on text and comment nodes\n\t\tif ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Make sure that we're working with the right name\n\t\tvar ret, type, hooks,\n\t\t\torigName = camelCase( name ),\n\t\t\tisCustomProp = rcustomProp.test( name ),\n\t\t\tstyle = elem.style;\n\n\t\t// Make sure that we're working with the right name. We don't\n\t\t// want to query the value if it is a CSS custom property\n\t\t// since they are user-defined.\n\t\tif ( !isCustomProp ) {\n\t\t\tname = finalPropName( origName );\n\t\t}\n\n\t\t// Gets hook for the prefixed version, then unprefixed version\n\t\thooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];\n\n\t\t// Check if we're setting a value\n\t\tif ( value !== undefined ) {\n\t\t\ttype = typeof value;\n\n\t\t\t// Convert \"+=\" or \"-=\" to relative numbers (#7345)\n\t\t\tif ( type === \"string\" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {\n\t\t\t\tvalue = adjustCSS( elem, name, ret );\n\n\t\t\t\t// Fixes bug #9237\n\t\t\t\ttype = \"number\";\n\t\t\t}\n\n\t\t\t// Make sure that null and NaN values aren't set (#7116)\n\t\t\tif ( value == null || value !== value ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// If a number was passed in, add the unit (except for certain CSS properties)\n\t\t\t// The isCustomProp check can be removed in jQuery 4.0 when we only auto-append\n\t\t\t// \"px\" to a few hardcoded values.\n\t\t\tif ( type === \"number\" && !isCustomProp ) {\n\t\t\t\tvalue += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? \"\" : \"px\" );\n\t\t\t}\n\n\t\t\t// background-* props affect original clone's values\n\t\t\tif ( !support.clearCloneStyle && value === \"\" && name.indexOf( \"background\" ) === 0 ) {\n\t\t\t\tstyle[ name ] = \"inherit\";\n\t\t\t}\n\n\t\t\t// If a hook was provided, use that value, otherwise just set the specified value\n\t\t\tif ( !hooks || !( \"set\" in hooks ) ||\n\t\t\t\t( value = hooks.set( elem, value, extra ) ) !== undefined ) {\n\n\t\t\t\tif ( isCustomProp ) {\n\t\t\t\t\tstyle.setProperty( name, value );\n\t\t\t\t} else {\n\t\t\t\t\tstyle[ name ] = value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t} else {\n\n\t\t\t// If a hook was provided get the non-computed value from there\n\t\t\tif ( hooks && \"get\" in hooks &&\n\t\t\t\t( ret = hooks.get( elem, false, extra ) ) !== undefined ) {\n\n\t\t\t\treturn ret;\n\t\t\t}\n\n\t\t\t// Otherwise just get the value from the style object\n\t\t\treturn style[ name ];\n\t\t}\n\t},\n\n\tcss: function( elem, name, extra, styles ) {\n\t\tvar val, num, hooks,\n\t\t\torigName = camelCase( name ),\n\t\t\tisCustomProp = rcustomProp.test( name );\n\n\t\t// Make sure that we're working with the right name. We don't\n\t\t// want to modify the value if it is a CSS custom property\n\t\t// since they are user-defined.\n\t\tif ( !isCustomProp ) {\n\t\t\tname = finalPropName( origName );\n\t\t}\n\n\t\t// Try prefixed name followed by the unprefixed name\n\t\thooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];\n\n\t\t// If a hook was provided get the computed value from there\n\t\tif ( hooks && \"get\" in hooks ) {\n\t\t\tval = hooks.get( elem, true, extra );\n\t\t}\n\n\t\t// Otherwise, if a way to get the computed value exists, use that\n\t\tif ( val === undefined ) {\n\t\t\tval = curCSS( elem, name, styles );\n\t\t}\n\n\t\t// Convert \"normal\" to computed value\n\t\tif ( val === \"normal\" && name in cssNormalTransform ) {\n\t\t\tval = cssNormalTransform[ name ];\n\t\t}\n\n\t\t// Make numeric if forced or a qualifier was provided and val looks numeric\n\t\tif ( extra === \"\" || extra ) {\n\t\t\tnum = parseFloat( val );\n\t\t\treturn extra === true || isFinite( num ) ? num || 0 : val;\n\t\t}\n\n\t\treturn val;\n\t}\n} );\n\njQuery.each( [ \"height\", \"width\" ], function( _i, dimension ) {\n\tjQuery.cssHooks[ dimension ] = {\n\t\tget: function( elem, computed, extra ) {\n\t\t\tif ( computed ) {\n\n\t\t\t\t// Certain elements can have dimension info if we invisibly show them\n\t\t\t\t// but it must have a current display style that would benefit\n\t\t\t\treturn rdisplayswap.test( jQuery.css( elem, \"display\" ) ) &&\n\n\t\t\t\t\t// Support: Safari 8+\n\t\t\t\t\t// Table columns in Safari have non-zero offsetWidth & zero\n\t\t\t\t\t// getBoundingClientRect().width unless display is changed.\n\t\t\t\t\t// Support: IE <=11 only\n\t\t\t\t\t// Running getBoundingClientRect on a disconnected node\n\t\t\t\t\t// in IE throws an error.\n\t\t\t\t\t( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ?\n\t\t\t\t\t\tswap( elem, cssShow, function() {\n\t\t\t\t\t\t\treturn getWidthOrHeight( elem, dimension, extra );\n\t\t\t\t\t\t} ) :\n\t\t\t\t\t\tgetWidthOrHeight( elem, dimension, extra );\n\t\t\t}\n\t\t},\n\n\t\tset: function( elem, value, extra ) {\n\t\t\tvar matches,\n\t\t\t\tstyles = getStyles( elem ),\n\n\t\t\t\t// Only read styles.position if the test has a chance to fail\n\t\t\t\t// to avoid forcing a reflow.\n\t\t\t\tscrollboxSizeBuggy = !support.scrollboxSize() &&\n\t\t\t\t\tstyles.position === \"absolute\",\n\n\t\t\t\t// To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991)\n\t\t\t\tboxSizingNeeded = scrollboxSizeBuggy || extra,\n\t\t\t\tisBorderBox = boxSizingNeeded &&\n\t\t\t\t\tjQuery.css( elem, \"boxSizing\", false, styles ) === \"border-box\",\n\t\t\t\tsubtract = extra ?\n\t\t\t\t\tboxModelAdjustment(\n\t\t\t\t\t\telem,\n\t\t\t\t\t\tdimension,\n\t\t\t\t\t\textra,\n\t\t\t\t\t\tisBorderBox,\n\t\t\t\t\t\tstyles\n\t\t\t\t\t) :\n\t\t\t\t\t0;\n\n\t\t\t// Account for unreliable border-box dimensions by comparing offset* to computed and\n\t\t\t// faking a content-box to get border and padding (gh-3699)\n\t\t\tif ( isBorderBox && scrollboxSizeBuggy ) {\n\t\t\t\tsubtract -= Math.ceil(\n\t\t\t\t\telem[ \"offset\" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -\n\t\t\t\t\tparseFloat( styles[ dimension ] ) -\n\t\t\t\t\tboxModelAdjustment( elem, dimension, \"border\", false, styles ) -\n\t\t\t\t\t0.5\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Convert to pixels if value adjustment is needed\n\t\t\tif ( subtract && ( matches = rcssNum.exec( value ) ) &&\n\t\t\t\t( matches[ 3 ] || \"px\" ) !== \"px\" ) {\n\n\t\t\t\telem.style[ dimension ] = value;\n\t\t\t\tvalue = jQuery.css( elem, dimension );\n\t\t\t}\n\n\t\t\treturn setPositiveNumber( elem, value, subtract );\n\t\t}\n\t};\n} );\n\njQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft,\n\tfunction( elem, computed ) {\n\t\tif ( computed ) {\n\t\t\treturn ( parseFloat( curCSS( elem, \"marginLeft\" ) ) ||\n\t\t\t\telem.getBoundingClientRect().left -\n\t\t\t\t\tswap( elem, { marginLeft: 0 }, function() {\n\t\t\t\t\t\treturn elem.getBoundingClientRect().left;\n\t\t\t\t\t} )\n\t\t\t\t) + \"px\";\n\t\t}\n\t}\n);\n\n// These hooks are used by animate to expand properties\njQuery.each( {\n\tmargin: \"\",\n\tpadding: \"\",\n\tborder: \"Width\"\n}, function( prefix, suffix ) {\n\tjQuery.cssHooks[ prefix + suffix ] = {\n\t\texpand: function( value ) {\n\t\t\tvar i = 0,\n\t\t\t\texpanded = {},\n\n\t\t\t\t// Assumes a single number if not a string\n\t\t\t\tparts = typeof value === \"string\" ? value.split( \" \" ) : [ value ];\n\n\t\t\tfor ( ; i < 4; i++ ) {\n\t\t\t\texpanded[ prefix + cssExpand[ i ] + suffix ] =\n\t\t\t\t\tparts[ i ] || parts[ i - 2 ] || parts[ 0 ];\n\t\t\t}\n\n\t\t\treturn expanded;\n\t\t}\n\t};\n\n\tif ( prefix !== \"margin\" ) {\n\t\tjQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;\n\t}\n} );\n\njQuery.fn.extend( {\n\tcss: function( name, value ) {\n\t\treturn access( this, function( elem, name, value ) {\n\t\t\tvar styles, len,\n\t\t\t\tmap = {},\n\t\t\t\ti = 0;\n\n\t\t\tif ( Array.isArray( name ) ) {\n\t\t\t\tstyles = getStyles( elem );\n\t\t\t\tlen = name.length;\n\n\t\t\t\tfor ( ; i < len; i++ ) {\n\t\t\t\t\tmap[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );\n\t\t\t\t}\n\n\t\t\t\treturn map;\n\t\t\t}\n\n\t\t\treturn value !== undefined ?\n\t\t\t\tjQuery.style( elem, name, value ) :\n\t\t\t\tjQuery.css( elem, name );\n\t\t}, name, value, arguments.length > 1 );\n\t}\n} );\n\n\nfunction Tween( elem, options, prop, end, easing ) {\n\treturn new Tween.prototype.init( elem, options, prop, end, easing );\n}\njQuery.Tween = Tween;\n\nTween.prototype = {\n\tconstructor: Tween,\n\tinit: function( elem, options, prop, end, easing, unit ) {\n\t\tthis.elem = elem;\n\t\tthis.prop = prop;\n\t\tthis.easing = easing || jQuery.easing._default;\n\t\tthis.options = options;\n\t\tthis.start = this.now = this.cur();\n\t\tthis.end = end;\n\t\tthis.unit = unit || ( jQuery.cssNumber[ prop ] ? \"\" : \"px\" );\n\t},\n\tcur: function() {\n\t\tvar hooks = Tween.propHooks[ this.prop ];\n\n\t\treturn hooks && hooks.get ?\n\t\t\thooks.get( this ) :\n\t\t\tTween.propHooks._default.get( this );\n\t},\n\trun: function( percent ) {\n\t\tvar eased,\n\t\t\thooks = Tween.propHooks[ this.prop ];\n\n\t\tif ( this.options.duration ) {\n\t\t\tthis.pos = eased = jQuery.easing[ this.easing ](\n\t\t\t\tpercent, this.options.duration * percent, 0, 1, this.options.duration\n\t\t\t);\n\t\t} else {\n\t\t\tthis.pos = eased = percent;\n\t\t}\n\t\tthis.now = ( this.end - this.start ) * eased + this.start;\n\n\t\tif ( this.options.step ) {\n\t\t\tthis.options.step.call( this.elem, this.now, this );\n\t\t}\n\n\t\tif ( hooks && hooks.set ) {\n\t\t\thooks.set( this );\n\t\t} else {\n\t\t\tTween.propHooks._default.set( this );\n\t\t}\n\t\treturn this;\n\t}\n};\n\nTween.prototype.init.prototype = Tween.prototype;\n\nTween.propHooks = {\n\t_default: {\n\t\tget: function( tween ) {\n\t\t\tvar result;\n\n\t\t\t// Use a property on the element directly when it is not a DOM element,\n\t\t\t// or when there is no matching style property that exists.\n\t\t\tif ( tween.elem.nodeType !== 1 ||\n\t\t\t\ttween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) {\n\t\t\t\treturn tween.elem[ tween.prop ];\n\t\t\t}\n\n\t\t\t// Passing an empty string as a 3rd parameter to .css will automatically\n\t\t\t// attempt a parseFloat and fallback to a string if the parse fails.\n\t\t\t// Simple values such as \"10px\" are parsed to Float;\n\t\t\t// complex values such as \"rotate(1rad)\" are returned as-is.\n\t\t\tresult = jQuery.css( tween.elem, tween.prop, \"\" );\n\n\t\t\t// Empty strings, null, undefined and \"auto\" are converted to 0.\n\t\t\treturn !result || result === \"auto\" ? 0 : result;\n\t\t},\n\t\tset: function( tween ) {\n\n\t\t\t// Use step hook for back compat.\n\t\t\t// Use cssHook if its there.\n\t\t\t// Use .style if available and use plain properties where available.\n\t\t\tif ( jQuery.fx.step[ tween.prop ] ) {\n\t\t\t\tjQuery.fx.step[ tween.prop ]( tween );\n\t\t\t} else if ( tween.elem.nodeType === 1 && (\n\t\t\t\t\tjQuery.cssHooks[ tween.prop ] ||\n\t\t\t\t\ttween.elem.style[ finalPropName( tween.prop ) ] != null ) ) {\n\t\t\t\tjQuery.style( tween.elem, tween.prop, tween.now + tween.unit );\n\t\t\t} else {\n\t\t\t\ttween.elem[ tween.prop ] = tween.now;\n\t\t\t}\n\t\t}\n\t}\n};\n\n// Support: IE <=9 only\n// Panic based approach to setting things on disconnected nodes\nTween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {\n\tset: function( tween ) {\n\t\tif ( tween.elem.nodeType && tween.elem.parentNode ) {\n\t\t\ttween.elem[ tween.prop ] = tween.now;\n\t\t}\n\t}\n};\n\njQuery.easing = {\n\tlinear: function( p ) {\n\t\treturn p;\n\t},\n\tswing: function( p ) {\n\t\treturn 0.5 - Math.cos( p * Math.PI ) / 2;\n\t},\n\t_default: \"swing\"\n};\n\njQuery.fx = Tween.prototype.init;\n\n// Back compat <1.8 extension point\njQuery.fx.step = {};\n\n\n\n\nvar\n\tfxNow, inProgress,\n\trfxtypes = /^(?:toggle|show|hide)$/,\n\trrun = /queueHooks$/;\n\nfunction schedule() {\n\tif ( inProgress ) {\n\t\tif ( document.hidden === false && window.requestAnimationFrame ) {\n\t\t\twindow.requestAnimationFrame( schedule );\n\t\t} else {\n\t\t\twindow.setTimeout( schedule, jQuery.fx.interval );\n\t\t}\n\n\t\tjQuery.fx.tick();\n\t}\n}\n\n// Animations created synchronously will run synchronously\nfunction createFxNow() {\n\twindow.setTimeout( function() {\n\t\tfxNow = undefined;\n\t} );\n\treturn ( fxNow = Date.now() );\n}\n\n// Generate parameters to create a standard animation\nfunction genFx( type, includeWidth ) {\n\tvar which,\n\t\ti = 0,\n\t\tattrs = { height: type };\n\n\t// If we include width, step value is 1 to do all cssExpand values,\n\t// otherwise step value is 2 to skip over Left and Right\n\tincludeWidth = includeWidth ? 1 : 0;\n\tfor ( ; i < 4; i += 2 - includeWidth ) {\n\t\twhich = cssExpand[ i ];\n\t\tattrs[ \"margin\" + which ] = attrs[ \"padding\" + which ] = type;\n\t}\n\n\tif ( includeWidth ) {\n\t\tattrs.opacity = attrs.width = type;\n\t}\n\n\treturn attrs;\n}\n\nfunction createTween( value, prop, animation ) {\n\tvar tween,\n\t\tcollection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ \"*\" ] ),\n\t\tindex = 0,\n\t\tlength = collection.length;\n\tfor ( ; index < length; index++ ) {\n\t\tif ( ( tween = collection[ index ].call( animation, prop, value ) ) ) {\n\n\t\t\t// We're done with this property\n\t\t\treturn tween;\n\t\t}\n\t}\n}\n\nfunction defaultPrefilter( elem, props, opts ) {\n\tvar prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display,\n\t\tisBox = \"width\" in props || \"height\" in props,\n\t\tanim = this,\n\t\torig = {},\n\t\tstyle = elem.style,\n\t\thidden = elem.nodeType && isHiddenWithinTree( elem ),\n\t\tdataShow = dataPriv.get( elem, \"fxshow\" );\n\n\t// Queue-skipping animations hijack the fx hooks\n\tif ( !opts.queue ) {\n\t\thooks = jQuery._queueHooks( elem, \"fx\" );\n\t\tif ( hooks.unqueued == null ) {\n\t\t\thooks.unqueued = 0;\n\t\t\toldfire = hooks.empty.fire;\n\t\t\thooks.empty.fire = function() {\n\t\t\t\tif ( !hooks.unqueued ) {\n\t\t\t\t\toldfire();\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\thooks.unqueued++;\n\n\t\tanim.always( function() {\n\n\t\t\t// Ensure the complete handler is called before this completes\n\t\t\tanim.always( function() {\n\t\t\t\thooks.unqueued--;\n\t\t\t\tif ( !jQuery.queue( elem, \"fx\" ).length ) {\n\t\t\t\t\thooks.empty.fire();\n\t\t\t\t}\n\t\t\t} );\n\t\t} );\n\t}\n\n\t// Detect show/hide animations\n\tfor ( prop in props ) {\n\t\tvalue = props[ prop ];\n\t\tif ( rfxtypes.test( value ) ) {\n\t\t\tdelete props[ prop ];\n\t\t\ttoggle = toggle || value === \"toggle\";\n\t\t\tif ( value === ( hidden ? \"hide\" : \"show\" ) ) {\n\n\t\t\t\t// Pretend to be hidden if this is a \"show\" and\n\t\t\t\t// there is still data from a stopped show/hide\n\t\t\t\tif ( value === \"show\" && dataShow && dataShow[ prop ] !== undefined ) {\n\t\t\t\t\thidden = true;\n\n\t\t\t\t// Ignore all other no-op show/hide data\n\t\t\t\t} else {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\torig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );\n\t\t}\n\t}\n\n\t// Bail out if this is a no-op like .hide().hide()\n\tpropTween = !jQuery.isEmptyObject( props );\n\tif ( !propTween && jQuery.isEmptyObject( orig ) ) {\n\t\treturn;\n\t}\n\n\t// Restrict \"overflow\" and \"display\" styles during box animations\n\tif ( isBox && elem.nodeType === 1 ) {\n\n\t\t// Support: IE <=9 - 11, Edge 12 - 15\n\t\t// Record all 3 overflow attributes because IE does not infer the shorthand\n\t\t// from identically-valued overflowX and overflowY and Edge just mirrors\n\t\t// the overflowX value there.\n\t\topts.overflow = [ style.overflow, style.overflowX, style.overflowY ];\n\n\t\t// Identify a display type, preferring old show/hide data over the CSS cascade\n\t\trestoreDisplay = dataShow && dataShow.display;\n\t\tif ( restoreDisplay == null ) {\n\t\t\trestoreDisplay = dataPriv.get( elem, \"display\" );\n\t\t}\n\t\tdisplay = jQuery.css( elem, \"display\" );\n\t\tif ( display === \"none\" ) {\n\t\t\tif ( restoreDisplay ) {\n\t\t\t\tdisplay = restoreDisplay;\n\t\t\t} else {\n\n\t\t\t\t// Get nonempty value(s) by temporarily forcing visibility\n\t\t\t\tshowHide( [ elem ], true );\n\t\t\t\trestoreDisplay = elem.style.display || restoreDisplay;\n\t\t\t\tdisplay = jQuery.css( elem, \"display\" );\n\t\t\t\tshowHide( [ elem ] );\n\t\t\t}\n\t\t}\n\n\t\t// Animate inline elements as inline-block\n\t\tif ( display === \"inline\" || display === \"inline-block\" && restoreDisplay != null ) {\n\t\t\tif ( jQuery.css( elem, \"float\" ) === \"none\" ) {\n\n\t\t\t\t// Restore the original display value at the end of pure show/hide animations\n\t\t\t\tif ( !propTween ) {\n\t\t\t\t\tanim.done( function() {\n\t\t\t\t\t\tstyle.display = restoreDisplay;\n\t\t\t\t\t} );\n\t\t\t\t\tif ( restoreDisplay == null ) {\n\t\t\t\t\t\tdisplay = style.display;\n\t\t\t\t\t\trestoreDisplay = display === \"none\" ? \"\" : display;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tstyle.display = \"inline-block\";\n\t\t\t}\n\t\t}\n\t}\n\n\tif ( opts.overflow ) {\n\t\tstyle.overflow = \"hidden\";\n\t\tanim.always( function() {\n\t\t\tstyle.overflow = opts.overflow[ 0 ];\n\t\t\tstyle.overflowX = opts.overflow[ 1 ];\n\t\t\tstyle.overflowY = opts.overflow[ 2 ];\n\t\t} );\n\t}\n\n\t// Implement show/hide animations\n\tpropTween = false;\n\tfor ( prop in orig ) {\n\n\t\t// General show/hide setup for this element animation\n\t\tif ( !propTween ) {\n\t\t\tif ( dataShow ) {\n\t\t\t\tif ( \"hidden\" in dataShow ) {\n\t\t\t\t\thidden = dataShow.hidden;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdataShow = dataPriv.access( elem, \"fxshow\", { display: restoreDisplay } );\n\t\t\t}\n\n\t\t\t// Store hidden/visible for toggle so `.stop().toggle()` \"reverses\"\n\t\t\tif ( toggle ) {\n\t\t\t\tdataShow.hidden = !hidden;\n\t\t\t}\n\n\t\t\t// Show elements before animating them\n\t\t\tif ( hidden ) {\n\t\t\t\tshowHide( [ elem ], true );\n\t\t\t}\n\n\t\t\t/* eslint-disable no-loop-func */\n\n\t\t\tanim.done( function() {\n\n\t\t\t/* eslint-enable no-loop-func */\n\n\t\t\t\t// The final step of a \"hide\" animation is actually hiding the element\n\t\t\t\tif ( !hidden ) {\n\t\t\t\t\tshowHide( [ elem ] );\n\t\t\t\t}\n\t\t\t\tdataPriv.remove( elem, \"fxshow\" );\n\t\t\t\tfor ( prop in orig ) {\n\t\t\t\t\tjQuery.style( elem, prop, orig[ prop ] );\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\n\t\t// Per-property setup\n\t\tpropTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );\n\t\tif ( !( prop in dataShow ) ) {\n\t\t\tdataShow[ prop ] = propTween.start;\n\t\t\tif ( hidden ) {\n\t\t\t\tpropTween.end = propTween.start;\n\t\t\t\tpropTween.start = 0;\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction propFilter( props, specialEasing ) {\n\tvar index, name, easing, value, hooks;\n\n\t// camelCase, specialEasing and expand cssHook pass\n\tfor ( index in props ) {\n\t\tname = camelCase( index );\n\t\teasing = specialEasing[ name ];\n\t\tvalue = props[ index ];\n\t\tif ( Array.isArray( value ) ) {\n\t\t\teasing = value[ 1 ];\n\t\t\tvalue = props[ index ] = value[ 0 ];\n\t\t}\n\n\t\tif ( index !== name ) {\n\t\t\tprops[ name ] = value;\n\t\t\tdelete props[ index ];\n\t\t}\n\n\t\thooks = jQuery.cssHooks[ name ];\n\t\tif ( hooks && \"expand\" in hooks ) {\n\t\t\tvalue = hooks.expand( value );\n\t\t\tdelete props[ name ];\n\n\t\t\t// Not quite $.extend, this won't overwrite existing keys.\n\t\t\t// Reusing 'index' because we have the correct \"name\"\n\t\t\tfor ( index in value ) {\n\t\t\t\tif ( !( index in props ) ) {\n\t\t\t\t\tprops[ index ] = value[ index ];\n\t\t\t\t\tspecialEasing[ index ] = easing;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tspecialEasing[ name ] = easing;\n\t\t}\n\t}\n}\n\nfunction Animation( elem, properties, options ) {\n\tvar result,\n\t\tstopped,\n\t\tindex = 0,\n\t\tlength = Animation.prefilters.length,\n\t\tdeferred = jQuery.Deferred().always( function() {\n\n\t\t\t// Don't match elem in the :animated selector\n\t\t\tdelete tick.elem;\n\t\t} ),\n\t\ttick = function() {\n\t\t\tif ( stopped ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tvar currentTime = fxNow || createFxNow(),\n\t\t\t\tremaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),\n\n\t\t\t\t// Support: Android 2.3 only\n\t\t\t\t// Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)\n\t\t\t\ttemp = remaining / animation.duration || 0,\n\t\t\t\tpercent = 1 - temp,\n\t\t\t\tindex = 0,\n\t\t\t\tlength = animation.tweens.length;\n\n\t\t\tfor ( ; index < length; index++ ) {\n\t\t\t\tanimation.tweens[ index ].run( percent );\n\t\t\t}\n\n\t\t\tdeferred.notifyWith( elem, [ animation, percent, remaining ] );\n\n\t\t\t// If there's more to do, yield\n\t\t\tif ( percent < 1 && length ) {\n\t\t\t\treturn remaining;\n\t\t\t}\n\n\t\t\t// If this was an empty animation, synthesize a final progress notification\n\t\t\tif ( !length ) {\n\t\t\t\tdeferred.notifyWith( elem, [ animation, 1, 0 ] );\n\t\t\t}\n\n\t\t\t// Resolve the animation and report its conclusion\n\t\t\tdeferred.resolveWith( elem, [ animation ] );\n\t\t\treturn false;\n\t\t},\n\t\tanimation = deferred.promise( {\n\t\t\telem: elem,\n\t\t\tprops: jQuery.extend( {}, properties ),\n\t\t\topts: jQuery.extend( true, {\n\t\t\t\tspecialEasing: {},\n\t\t\t\teasing: jQuery.easing._default\n\t\t\t}, options ),\n\t\t\toriginalProperties: properties,\n\t\t\toriginalOptions: options,\n\t\t\tstartTime: fxNow || createFxNow(),\n\t\t\tduration: options.duration,\n\t\t\ttweens: [],\n\t\t\tcreateTween: function( prop, end ) {\n\t\t\t\tvar tween = jQuery.Tween( elem, animation.opts, prop, end,\n\t\t\t\t\t\tanimation.opts.specialEasing[ prop ] || animation.opts.easing );\n\t\t\t\tanimation.tweens.push( tween );\n\t\t\t\treturn tween;\n\t\t\t},\n\t\t\tstop: function( gotoEnd ) {\n\t\t\t\tvar index = 0,\n\n\t\t\t\t\t// If we are going to the end, we want to run all the tweens\n\t\t\t\t\t// otherwise we skip this part\n\t\t\t\t\tlength = gotoEnd ? animation.tweens.length : 0;\n\t\t\t\tif ( stopped ) {\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\t\t\t\tstopped = true;\n\t\t\t\tfor ( ; index < length; index++ ) {\n\t\t\t\t\tanimation.tweens[ index ].run( 1 );\n\t\t\t\t}\n\n\t\t\t\t// Resolve when we played the last frame; otherwise, reject\n\t\t\t\tif ( gotoEnd ) {\n\t\t\t\t\tdeferred.notifyWith( elem, [ animation, 1, 0 ] );\n\t\t\t\t\tdeferred.resolveWith( elem, [ animation, gotoEnd ] );\n\t\t\t\t} else {\n\t\t\t\t\tdeferred.rejectWith( elem, [ animation, gotoEnd ] );\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t}\n\t\t} ),\n\t\tprops = animation.props;\n\n\tpropFilter( props, animation.opts.specialEasing );\n\n\tfor ( ; index < length; index++ ) {\n\t\tresult = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );\n\t\tif ( result ) {\n\t\t\tif ( isFunction( result.stop ) ) {\n\t\t\t\tjQuery._queueHooks( animation.elem, animation.opts.queue ).stop =\n\t\t\t\t\tresult.stop.bind( result );\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\t}\n\n\tjQuery.map( props, createTween, animation );\n\n\tif ( isFunction( animation.opts.start ) ) {\n\t\tanimation.opts.start.call( elem, animation );\n\t}\n\n\t// Attach callbacks from options\n\tanimation\n\t\t.progress( animation.opts.progress )\n\t\t.done( animation.opts.done, animation.opts.complete )\n\t\t.fail( animation.opts.fail )\n\t\t.always( animation.opts.always );\n\n\tjQuery.fx.timer(\n\t\tjQuery.extend( tick, {\n\t\t\telem: elem,\n\t\t\tanim: animation,\n\t\t\tqueue: animation.opts.queue\n\t\t} )\n\t);\n\n\treturn animation;\n}\n\njQuery.Animation = jQuery.extend( Animation, {\n\n\ttweeners: {\n\t\t\"*\": [ function( prop, value ) {\n\t\t\tvar tween = this.createTween( prop, value );\n\t\t\tadjustCSS( tween.elem, prop, rcssNum.exec( value ), tween );\n\t\t\treturn tween;\n\t\t} ]\n\t},\n\n\ttweener: function( props, callback ) {\n\t\tif ( isFunction( props ) ) {\n\t\t\tcallback = props;\n\t\t\tprops = [ \"*\" ];\n\t\t} else {\n\t\t\tprops = props.match( rnothtmlwhite );\n\t\t}\n\n\t\tvar prop,\n\t\t\tindex = 0,\n\t\t\tlength = props.length;\n\n\t\tfor ( ; index < length; index++ ) {\n\t\t\tprop = props[ index ];\n\t\t\tAnimation.tweeners[ prop ] = Animation.tweeners[ prop ] || [];\n\t\t\tAnimation.tweeners[ prop ].unshift( callback );\n\t\t}\n\t},\n\n\tprefilters: [ defaultPrefilter ],\n\n\tprefilter: function( callback, prepend ) {\n\t\tif ( prepend ) {\n\t\t\tAnimation.prefilters.unshift( callback );\n\t\t} else {\n\t\t\tAnimation.prefilters.push( callback );\n\t\t}\n\t}\n} );\n\njQuery.speed = function( speed, easing, fn ) {\n\tvar opt = speed && typeof speed === \"object\" ? jQuery.extend( {}, speed ) : {\n\t\tcomplete: fn || !fn && easing ||\n\t\t\tisFunction( speed ) && speed,\n\t\tduration: speed,\n\t\teasing: fn && easing || easing && !isFunction( easing ) && easing\n\t};\n\n\t// Go to the end state if fx are off\n\tif ( jQuery.fx.off ) {\n\t\topt.duration = 0;\n\n\t} else {\n\t\tif ( typeof opt.duration !== \"number\" ) {\n\t\t\tif ( opt.duration in jQuery.fx.speeds ) {\n\t\t\t\topt.duration = jQuery.fx.speeds[ opt.duration ];\n\n\t\t\t} else {\n\t\t\t\topt.duration = jQuery.fx.speeds._default;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Normalize opt.queue - true/undefined/null -> \"fx\"\n\tif ( opt.queue == null || opt.queue === true ) {\n\t\topt.queue = \"fx\";\n\t}\n\n\t// Queueing\n\topt.old = opt.complete;\n\n\topt.complete = function() {\n\t\tif ( isFunction( opt.old ) ) {\n\t\t\topt.old.call( this );\n\t\t}\n\n\t\tif ( opt.queue ) {\n\t\t\tjQuery.dequeue( this, opt.queue );\n\t\t}\n\t};\n\n\treturn opt;\n};\n\njQuery.fn.extend( {\n\tfadeTo: function( speed, to, easing, callback ) {\n\n\t\t// Show any hidden elements after setting opacity to 0\n\t\treturn this.filter( isHiddenWithinTree ).css( \"opacity\", 0 ).show()\n\n\t\t\t// Animate to the value specified\n\t\t\t.end().animate( { opacity: to }, speed, easing, callback );\n\t},\n\tanimate: function( prop, speed, easing, callback ) {\n\t\tvar empty = jQuery.isEmptyObject( prop ),\n\t\t\toptall = jQuery.speed( speed, easing, callback ),\n\t\t\tdoAnimation = function() {\n\n\t\t\t\t// Operate on a copy of prop so per-property easing won't be lost\n\t\t\t\tvar anim = Animation( this, jQuery.extend( {}, prop ), optall );\n\n\t\t\t\t// Empty animations, or finishing resolves immediately\n\t\t\t\tif ( empty || dataPriv.get( this, \"finish\" ) ) {\n\t\t\t\t\tanim.stop( true );\n\t\t\t\t}\n\t\t\t};\n\t\t\tdoAnimation.finish = doAnimation;\n\n\t\treturn empty || optall.queue === false ?\n\t\t\tthis.each( doAnimation ) :\n\t\t\tthis.queue( optall.queue, doAnimation );\n\t},\n\tstop: function( type, clearQueue, gotoEnd ) {\n\t\tvar stopQueue = function( hooks ) {\n\t\t\tvar stop = hooks.stop;\n\t\t\tdelete hooks.stop;\n\t\t\tstop( gotoEnd );\n\t\t};\n\n\t\tif ( typeof type !== \"string\" ) {\n\t\t\tgotoEnd = clearQueue;\n\t\t\tclearQueue = type;\n\t\t\ttype = undefined;\n\t\t}\n\t\tif ( clearQueue ) {\n\t\t\tthis.queue( type || \"fx\", [] );\n\t\t}\n\n\t\treturn this.each( function() {\n\t\t\tvar dequeue = true,\n\t\t\t\tindex = type != null && type + \"queueHooks\",\n\t\t\t\ttimers = jQuery.timers,\n\t\t\t\tdata = dataPriv.get( this );\n\n\t\t\tif ( index ) {\n\t\t\t\tif ( data[ index ] && data[ index ].stop ) {\n\t\t\t\t\tstopQueue( data[ index ] );\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor ( index in data ) {\n\t\t\t\t\tif ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {\n\t\t\t\t\t\tstopQueue( data[ index ] );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor ( index = timers.length; index--; ) {\n\t\t\t\tif ( timers[ index ].elem === this &&\n\t\t\t\t\t( type == null || timers[ index ].queue === type ) ) {\n\n\t\t\t\t\ttimers[ index ].anim.stop( gotoEnd );\n\t\t\t\t\tdequeue = false;\n\t\t\t\t\ttimers.splice( index, 1 );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Start the next in the queue if the last step wasn't forced.\n\t\t\t// Timers currently will call their complete callbacks, which\n\t\t\t// will dequeue but only if they were gotoEnd.\n\t\t\tif ( dequeue || !gotoEnd ) {\n\t\t\t\tjQuery.dequeue( this, type );\n\t\t\t}\n\t\t} );\n\t},\n\tfinish: function( type ) {\n\t\tif ( type !== false ) {\n\t\t\ttype = type || \"fx\";\n\t\t}\n\t\treturn this.each( function() {\n\t\t\tvar index,\n\t\t\t\tdata = dataPriv.get( this ),\n\t\t\t\tqueue = data[ type + \"queue\" ],\n\t\t\t\thooks = data[ type + \"queueHooks\" ],\n\t\t\t\ttimers = jQuery.timers,\n\t\t\t\tlength = queue ? queue.length : 0;\n\n\t\t\t// Enable finishing flag on private data\n\t\t\tdata.finish = true;\n\n\t\t\t// Empty the queue first\n\t\t\tjQuery.queue( this, type, [] );\n\n\t\t\tif ( hooks && hooks.stop ) {\n\t\t\t\thooks.stop.call( this, true );\n\t\t\t}\n\n\t\t\t// Look for any active animations, and finish them\n\t\t\tfor ( index = timers.length; index--; ) {\n\t\t\t\tif ( timers[ index ].elem === this && timers[ index ].queue === type ) {\n\t\t\t\t\ttimers[ index ].anim.stop( true );\n\t\t\t\t\ttimers.splice( index, 1 );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Look for any animations in the old queue and finish them\n\t\t\tfor ( index = 0; index < length; index++ ) {\n\t\t\t\tif ( queue[ index ] && queue[ index ].finish ) {\n\t\t\t\t\tqueue[ index ].finish.call( this );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Turn off finishing flag\n\t\t\tdelete data.finish;\n\t\t} );\n\t}\n} );\n\njQuery.each( [ \"toggle\", \"show\", \"hide\" ], function( _i, name ) {\n\tvar cssFn = jQuery.fn[ name ];\n\tjQuery.fn[ name ] = function( speed, easing, callback ) {\n\t\treturn speed == null || typeof speed === \"boolean\" ?\n\t\t\tcssFn.apply( this, arguments ) :\n\t\t\tthis.animate( genFx( name, true ), speed, easing, callback );\n\t};\n} );\n\n// Generate shortcuts for custom animations\njQuery.each( {\n\tslideDown: genFx( \"show\" ),\n\tslideUp: genFx( \"hide\" ),\n\tslideToggle: genFx( \"toggle\" ),\n\tfadeIn: { opacity: \"show\" },\n\tfadeOut: { opacity: \"hide\" },\n\tfadeToggle: { opacity: \"toggle\" }\n}, function( name, props ) {\n\tjQuery.fn[ name ] = function( speed, easing, callback ) {\n\t\treturn this.animate( props, speed, easing, callback );\n\t};\n} );\n\njQuery.timers = [];\njQuery.fx.tick = function() {\n\tvar timer,\n\t\ti = 0,\n\t\ttimers = jQuery.timers;\n\n\tfxNow = Date.now();\n\n\tfor ( ; i < timers.length; i++ ) {\n\t\ttimer = timers[ i ];\n\n\t\t// Run the timer and safely remove it when done (allowing for external removal)\n\t\tif ( !timer() && timers[ i ] === timer ) {\n\t\t\ttimers.splice( i--, 1 );\n\t\t}\n\t}\n\n\tif ( !timers.length ) {\n\t\tjQuery.fx.stop();\n\t}\n\tfxNow = undefined;\n};\n\njQuery.fx.timer = function( timer ) {\n\tjQuery.timers.push( timer );\n\tjQuery.fx.start();\n};\n\njQuery.fx.interval = 13;\njQuery.fx.start = function() {\n\tif ( inProgress ) {\n\t\treturn;\n\t}\n\n\tinProgress = true;\n\tschedule();\n};\n\njQuery.fx.stop = function() {\n\tinProgress = null;\n};\n\njQuery.fx.speeds = {\n\tslow: 600,\n\tfast: 200,\n\n\t// Default speed\n\t_default: 400\n};\n\n\n// Based off of the plugin by Clint Helfers, with permission.\n// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/\njQuery.fn.delay = function( time, type ) {\n\ttime = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;\n\ttype = type || \"fx\";\n\n\treturn this.queue( type, function( next, hooks ) {\n\t\tvar timeout = window.setTimeout( next, time );\n\t\thooks.stop = function() {\n\t\t\twindow.clearTimeout( timeout );\n\t\t};\n\t} );\n};\n\n\n( function() {\n\tvar input = document.createElement( \"input\" ),\n\t\tselect = document.createElement( \"select\" ),\n\t\topt = select.appendChild( document.createElement( \"option\" ) );\n\n\tinput.type = \"checkbox\";\n\n\t// Support: Android <=4.3 only\n\t// Default value for a checkbox should be \"on\"\n\tsupport.checkOn = input.value !== \"\";\n\n\t// Support: IE <=11 only\n\t// Must access selectedIndex to make default options select\n\tsupport.optSelected = opt.selected;\n\n\t// Support: IE <=11 only\n\t// An input loses its value after becoming a radio\n\tinput = document.createElement( \"input\" );\n\tinput.value = \"t\";\n\tinput.type = \"radio\";\n\tsupport.radioValue = input.value === \"t\";\n} )();\n\n\nvar boolHook,\n\tattrHandle = jQuery.expr.attrHandle;\n\njQuery.fn.extend( {\n\tattr: function( name, value ) {\n\t\treturn access( this, jQuery.attr, name, value, arguments.length > 1 );\n\t},\n\n\tremoveAttr: function( name ) {\n\t\treturn this.each( function() {\n\t\t\tjQuery.removeAttr( this, name );\n\t\t} );\n\t}\n} );\n\njQuery.extend( {\n\tattr: function( elem, name, value ) {\n\t\tvar ret, hooks,\n\t\t\tnType = elem.nodeType;\n\n\t\t// Don't get/set attributes on text, comment and attribute nodes\n\t\tif ( nType === 3 || nType === 8 || nType === 2 ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Fallback to prop when attributes are not supported\n\t\tif ( typeof elem.getAttribute === \"undefined\" ) {\n\t\t\treturn jQuery.prop( elem, name, value );\n\t\t}\n\n\t\t// Attribute hooks are determined by the lowercase version\n\t\t// Grab necessary hook if one is defined\n\t\tif ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {\n\t\t\thooks = jQuery.attrHooks[ name.toLowerCase() ] ||\n\t\t\t\t( jQuery.expr.match.bool.test( name ) ? boolHook : undefined );\n\t\t}\n\n\t\tif ( value !== undefined ) {\n\t\t\tif ( value === null ) {\n\t\t\t\tjQuery.removeAttr( elem, name );\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ( hooks && \"set\" in hooks &&\n\t\t\t\t( ret = hooks.set( elem, value, name ) ) !== undefined ) {\n\t\t\t\treturn ret;\n\t\t\t}\n\n\t\t\telem.setAttribute( name, value + \"\" );\n\t\t\treturn value;\n\t\t}\n\n\t\tif ( hooks && \"get\" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {\n\t\t\treturn ret;\n\t\t}\n\n\t\tret = jQuery.find.attr( elem, name );\n\n\t\t// Non-existent attributes return null, we normalize to undefined\n\t\treturn ret == null ? undefined : ret;\n\t},\n\n\tattrHooks: {\n\t\ttype: {\n\t\t\tset: function( elem, value ) {\n\t\t\t\tif ( !support.radioValue && value === \"radio\" &&\n\t\t\t\t\tnodeName( elem, \"input\" ) ) {\n\t\t\t\t\tvar val = elem.value;\n\t\t\t\t\telem.setAttribute( \"type\", value );\n\t\t\t\t\tif ( val ) {\n\t\t\t\t\t\telem.value = val;\n\t\t\t\t\t}\n\t\t\t\t\treturn value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\tremoveAttr: function( elem, value ) {\n\t\tvar name,\n\t\t\ti = 0,\n\n\t\t\t// Attribute names can contain non-HTML whitespace characters\n\t\t\t// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2\n\t\t\tattrNames = value && value.match( rnothtmlwhite );\n\n\t\tif ( attrNames && elem.nodeType === 1 ) {\n\t\t\twhile ( ( name = attrNames[ i++ ] ) ) {\n\t\t\t\telem.removeAttribute( name );\n\t\t\t}\n\t\t}\n\t}\n} );\n\n// Hooks for boolean attributes\nboolHook = {\n\tset: function( elem, value, name ) {\n\t\tif ( value === false ) {\n\n\t\t\t// Remove boolean attributes when set to false\n\t\t\tjQuery.removeAttr( elem, name );\n\t\t} else {\n\t\t\telem.setAttribute( name, name );\n\t\t}\n\t\treturn name;\n\t}\n};\n\njQuery.each( jQuery.expr.match.bool.source.match( /\\w+/g ), function( _i, name ) {\n\tvar getter = attrHandle[ name ] || jQuery.find.attr;\n\n\tattrHandle[ name ] = function( elem, name, isXML ) {\n\t\tvar ret, handle,\n\t\t\tlowercaseName = name.toLowerCase();\n\n\t\tif ( !isXML ) {\n\n\t\t\t// Avoid an infinite loop by temporarily removing this function from the getter\n\t\t\thandle = attrHandle[ lowercaseName ];\n\t\t\tattrHandle[ lowercaseName ] = ret;\n\t\t\tret = getter( elem, name, isXML ) != null ?\n\t\t\t\tlowercaseName :\n\t\t\t\tnull;\n\t\t\tattrHandle[ lowercaseName ] = handle;\n\t\t}\n\t\treturn ret;\n\t};\n} );\n\n\n\n\nvar rfocusable = /^(?:input|select|textarea|button)$/i,\n\trclickable = /^(?:a|area)$/i;\n\njQuery.fn.extend( {\n\tprop: function( name, value ) {\n\t\treturn access( this, jQuery.prop, name, value, arguments.length > 1 );\n\t},\n\n\tremoveProp: function( name ) {\n\t\treturn this.each( function() {\n\t\t\tdelete this[ jQuery.propFix[ name ] || name ];\n\t\t} );\n\t}\n} );\n\njQuery.extend( {\n\tprop: function( elem, name, value ) {\n\t\tvar ret, hooks,\n\t\t\tnType = elem.nodeType;\n\n\t\t// Don't get/set properties on text, comment and attribute nodes\n\t\tif ( nType === 3 || nType === 8 || nType === 2 ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {\n\n\t\t\t// Fix name and attach hooks\n\t\t\tname = jQuery.propFix[ name ] || name;\n\t\t\thooks = jQuery.propHooks[ name ];\n\t\t}\n\n\t\tif ( value !== undefined ) {\n\t\t\tif ( hooks && \"set\" in hooks &&\n\t\t\t\t( ret = hooks.set( elem, value, name ) ) !== undefined ) {\n\t\t\t\treturn ret;\n\t\t\t}\n\n\t\t\treturn ( elem[ name ] = value );\n\t\t}\n\n\t\tif ( hooks && \"get\" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {\n\t\t\treturn ret;\n\t\t}\n\n\t\treturn elem[ name ];\n\t},\n\n\tpropHooks: {\n\t\ttabIndex: {\n\t\t\tget: function( elem ) {\n\n\t\t\t\t// Support: IE <=9 - 11 only\n\t\t\t\t// elem.tabIndex doesn't always return the\n\t\t\t\t// correct value when it hasn't been explicitly set\n\t\t\t\t// https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/\n\t\t\t\t// Use proper attribute retrieval(#12072)\n\t\t\t\tvar tabindex = jQuery.find.attr( elem, \"tabindex\" );\n\n\t\t\t\tif ( tabindex ) {\n\t\t\t\t\treturn parseInt( tabindex, 10 );\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\trfocusable.test( elem.nodeName ) ||\n\t\t\t\t\trclickable.test( elem.nodeName ) &&\n\t\t\t\t\telem.href\n\t\t\t\t) {\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t},\n\n\tpropFix: {\n\t\t\"for\": \"htmlFor\",\n\t\t\"class\": \"className\"\n\t}\n} );\n\n// Support: IE <=11 only\n// Accessing the selectedIndex property\n// forces the browser to respect setting selected\n// on the option\n// The getter ensures a default option is selected\n// when in an optgroup\n// eslint rule \"no-unused-expressions\" is disabled for this code\n// since it considers such accessions noop\nif ( !support.optSelected ) {\n\tjQuery.propHooks.selected = {\n\t\tget: function( elem ) {\n\n\t\t\t/* eslint no-unused-expressions: \"off\" */\n\n\t\t\tvar parent = elem.parentNode;\n\t\t\tif ( parent && parent.parentNode ) {\n\t\t\t\tparent.parentNode.selectedIndex;\n\t\t\t}\n\t\t\treturn null;\n\t\t},\n\t\tset: function( elem ) {\n\n\t\t\t/* eslint no-unused-expressions: \"off\" */\n\n\t\t\tvar parent = elem.parentNode;\n\t\t\tif ( parent ) {\n\t\t\t\tparent.selectedIndex;\n\n\t\t\t\tif ( parent.parentNode ) {\n\t\t\t\t\tparent.parentNode.selectedIndex;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n}\n\njQuery.each( [\n\t\"tabIndex\",\n\t\"readOnly\",\n\t\"maxLength\",\n\t\"cellSpacing\",\n\t\"cellPadding\",\n\t\"rowSpan\",\n\t\"colSpan\",\n\t\"useMap\",\n\t\"frameBorder\",\n\t\"contentEditable\"\n], function() {\n\tjQuery.propFix[ this.toLowerCase() ] = this;\n} );\n\n\n\n\n\t// Strip and collapse whitespace according to HTML spec\n\t// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace\n\tfunction stripAndCollapse( value ) {\n\t\tvar tokens = value.match( rnothtmlwhite ) || [];\n\t\treturn tokens.join( \" \" );\n\t}\n\n\nfunction getClass( elem ) {\n\treturn elem.getAttribute && elem.getAttribute( \"class\" ) || \"\";\n}\n\nfunction classesToArray( value ) {\n\tif ( Array.isArray( value ) ) {\n\t\treturn value;\n\t}\n\tif ( typeof value === \"string\" ) {\n\t\treturn value.match( rnothtmlwhite ) || [];\n\t}\n\treturn [];\n}\n\njQuery.fn.extend( {\n\taddClass: function( value ) {\n\t\tvar classes, elem, cur, curValue, clazz, j, finalValue,\n\t\t\ti = 0;\n\n\t\tif ( isFunction( value ) ) {\n\t\t\treturn this.each( function( j ) {\n\t\t\t\tjQuery( this ).addClass( value.call( this, j, getClass( this ) ) );\n\t\t\t} );\n\t\t}\n\n\t\tclasses = classesToArray( value );\n\n\t\tif ( classes.length ) {\n\t\t\twhile ( ( elem = this[ i++ ] ) ) {\n\t\t\t\tcurValue = getClass( elem );\n\t\t\t\tcur = elem.nodeType === 1 && ( \" \" + stripAndCollapse( curValue ) + \" \" );\n\n\t\t\t\tif ( cur ) {\n\t\t\t\t\tj = 0;\n\t\t\t\t\twhile ( ( clazz = classes[ j++ ] ) ) {\n\t\t\t\t\t\tif ( cur.indexOf( \" \" + clazz + \" \" ) < 0 ) {\n\t\t\t\t\t\t\tcur += clazz + \" \";\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only assign if different to avoid unneeded rendering.\n\t\t\t\t\tfinalValue = stripAndCollapse( cur );\n\t\t\t\t\tif ( curValue !== finalValue ) {\n\t\t\t\t\t\telem.setAttribute( \"class\", finalValue );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tremoveClass: function( value ) {\n\t\tvar classes, elem, cur, curValue, clazz, j, finalValue,\n\t\t\ti = 0;\n\n\t\tif ( isFunction( value ) ) {\n\t\t\treturn this.each( function( j ) {\n\t\t\t\tjQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );\n\t\t\t} );\n\t\t}\n\n\t\tif ( !arguments.length ) {\n\t\t\treturn this.attr( \"class\", \"\" );\n\t\t}\n\n\t\tclasses = classesToArray( value );\n\n\t\tif ( classes.length ) {\n\t\t\twhile ( ( elem = this[ i++ ] ) ) {\n\t\t\t\tcurValue = getClass( elem );\n\n\t\t\t\t// This expression is here for better compressibility (see addClass)\n\t\t\t\tcur = elem.nodeType === 1 && ( \" \" + stripAndCollapse( curValue ) + \" \" );\n\n\t\t\t\tif ( cur ) {\n\t\t\t\t\tj = 0;\n\t\t\t\t\twhile ( ( clazz = classes[ j++ ] ) ) {\n\n\t\t\t\t\t\t// Remove *all* instances\n\t\t\t\t\t\twhile ( cur.indexOf( \" \" + clazz + \" \" ) > -1 ) {\n\t\t\t\t\t\t\tcur = cur.replace( \" \" + clazz + \" \", \" \" );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only assign if different to avoid unneeded rendering.\n\t\t\t\t\tfinalValue = stripAndCollapse( cur );\n\t\t\t\t\tif ( curValue !== finalValue ) {\n\t\t\t\t\t\telem.setAttribute( \"class\", finalValue );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\ttoggleClass: function( value, stateVal ) {\n\t\tvar type = typeof value,\n\t\t\tisValidValue = type === \"string\" || Array.isArray( value );\n\n\t\tif ( typeof stateVal === \"boolean\" && isValidValue ) {\n\t\t\treturn stateVal ? this.addClass( value ) : this.removeClass( value );\n\t\t}\n\n\t\tif ( isFunction( value ) ) {\n\t\t\treturn this.each( function( i ) {\n\t\t\t\tjQuery( this ).toggleClass(\n\t\t\t\t\tvalue.call( this, i, getClass( this ), stateVal ),\n\t\t\t\t\tstateVal\n\t\t\t\t);\n\t\t\t} );\n\t\t}\n\n\t\treturn this.each( function() {\n\t\t\tvar className, i, self, classNames;\n\n\t\t\tif ( isValidValue ) {\n\n\t\t\t\t// Toggle individual class names\n\t\t\t\ti = 0;\n\t\t\t\tself = jQuery( this );\n\t\t\t\tclassNames = classesToArray( value );\n\n\t\t\t\twhile ( ( className = classNames[ i++ ] ) ) {\n\n\t\t\t\t\t// Check each className given, space separated list\n\t\t\t\t\tif ( self.hasClass( className ) ) {\n\t\t\t\t\t\tself.removeClass( className );\n\t\t\t\t\t} else {\n\t\t\t\t\t\tself.addClass( className );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t// Toggle whole class name\n\t\t\t} else if ( value === undefined || type === \"boolean\" ) {\n\t\t\t\tclassName = getClass( this );\n\t\t\t\tif ( className ) {\n\n\t\t\t\t\t// Store className if set\n\t\t\t\t\tdataPriv.set( this, \"__className__\", className );\n\t\t\t\t}\n\n\t\t\t\t// If the element has a class name or if we're passed `false`,\n\t\t\t\t// then remove the whole classname (if there was one, the above saved it).\n\t\t\t\t// Otherwise bring back whatever was previously saved (if anything),\n\t\t\t\t// falling back to the empty string if nothing was stored.\n\t\t\t\tif ( this.setAttribute ) {\n\t\t\t\t\tthis.setAttribute( \"class\",\n\t\t\t\t\t\tclassName || value === false ?\n\t\t\t\t\t\t\"\" :\n\t\t\t\t\t\tdataPriv.get( this, \"__className__\" ) || \"\"\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t},\n\n\thasClass: function( selector ) {\n\t\tvar className, elem,\n\t\t\ti = 0;\n\n\t\tclassName = \" \" + selector + \" \";\n\t\twhile ( ( elem = this[ i++ ] ) ) {\n\t\t\tif ( elem.nodeType === 1 &&\n\t\t\t\t( \" \" + stripAndCollapse( getClass( elem ) ) + \" \" ).indexOf( className ) > -1 ) {\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n} );\n\n\n\n\nvar rreturn = /\\r/g;\n\njQuery.fn.extend( {\n\tval: function( value ) {\n\t\tvar hooks, ret, valueIsFunction,\n\t\t\telem = this[ 0 ];\n\n\t\tif ( !arguments.length ) {\n\t\t\tif ( elem ) {\n\t\t\t\thooks = jQuery.valHooks[ elem.type ] ||\n\t\t\t\t\tjQuery.valHooks[ elem.nodeName.toLowerCase() ];\n\n\t\t\t\tif ( hooks &&\n\t\t\t\t\t\"get\" in hooks &&\n\t\t\t\t\t( ret = hooks.get( elem, \"value\" ) ) !== undefined\n\t\t\t\t) {\n\t\t\t\t\treturn ret;\n\t\t\t\t}\n\n\t\t\t\tret = elem.value;\n\n\t\t\t\t// Handle most common string cases\n\t\t\t\tif ( typeof ret === \"string\" ) {\n\t\t\t\t\treturn ret.replace( rreturn, \"\" );\n\t\t\t\t}\n\n\t\t\t\t// Handle cases where value is null/undef or number\n\t\t\t\treturn ret == null ? \"\" : ret;\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tvalueIsFunction = isFunction( value );\n\n\t\treturn this.each( function( i ) {\n\t\t\tvar val;\n\n\t\t\tif ( this.nodeType !== 1 ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ( valueIsFunction ) {\n\t\t\t\tval = value.call( this, i, jQuery( this ).val() );\n\t\t\t} else {\n\t\t\t\tval = value;\n\t\t\t}\n\n\t\t\t// Treat null/undefined as \"\"; convert numbers to string\n\t\t\tif ( val == null ) {\n\t\t\t\tval = \"\";\n\n\t\t\t} else if ( typeof val === \"number\" ) {\n\t\t\t\tval += \"\";\n\n\t\t\t} else if ( Array.isArray( val ) ) {\n\t\t\t\tval = jQuery.map( val, function( value ) {\n\t\t\t\t\treturn value == null ? \"\" : value + \"\";\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\thooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];\n\n\t\t\t// If set returns undefined, fall back to normal setting\n\t\t\tif ( !hooks || !( \"set\" in hooks ) || hooks.set( this, val, \"value\" ) === undefined ) {\n\t\t\t\tthis.value = val;\n\t\t\t}\n\t\t} );\n\t}\n} );\n\njQuery.extend( {\n\tvalHooks: {\n\t\toption: {\n\t\t\tget: function( elem ) {\n\n\t\t\t\tvar val = jQuery.find.attr( elem, \"value\" );\n\t\t\t\treturn val != null ?\n\t\t\t\t\tval :\n\n\t\t\t\t\t// Support: IE <=10 - 11 only\n\t\t\t\t\t// option.text throws exceptions (#14686, #14858)\n\t\t\t\t\t// Strip and collapse whitespace\n\t\t\t\t\t// https://html.spec.whatwg.org/#strip-and-collapse-whitespace\n\t\t\t\t\tstripAndCollapse( jQuery.text( elem ) );\n\t\t\t}\n\t\t},\n\t\tselect: {\n\t\t\tget: function( elem ) {\n\t\t\t\tvar value, option, i,\n\t\t\t\t\toptions = elem.options,\n\t\t\t\t\tindex = elem.selectedIndex,\n\t\t\t\t\tone = elem.type === \"select-one\",\n\t\t\t\t\tvalues = one ? null : [],\n\t\t\t\t\tmax = one ? index + 1 : options.length;\n\n\t\t\t\tif ( index < 0 ) {\n\t\t\t\t\ti = max;\n\n\t\t\t\t} else {\n\t\t\t\t\ti = one ? index : 0;\n\t\t\t\t}\n\n\t\t\t\t// Loop through all the selected options\n\t\t\t\tfor ( ; i < max; i++ ) {\n\t\t\t\t\toption = options[ i ];\n\n\t\t\t\t\t// Support: IE <=9 only\n\t\t\t\t\t// IE8-9 doesn't update selected after form reset (#2551)\n\t\t\t\t\tif ( ( option.selected || i === index ) &&\n\n\t\t\t\t\t\t\t// Don't return options that are disabled or in a disabled optgroup\n\t\t\t\t\t\t\t!option.disabled &&\n\t\t\t\t\t\t\t( !option.parentNode.disabled ||\n\t\t\t\t\t\t\t\t!nodeName( option.parentNode, \"optgroup\" ) ) ) {\n\n\t\t\t\t\t\t// Get the specific value for the option\n\t\t\t\t\t\tvalue = jQuery( option ).val();\n\n\t\t\t\t\t\t// We don't need an array for one selects\n\t\t\t\t\t\tif ( one ) {\n\t\t\t\t\t\t\treturn value;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Multi-Selects return an array\n\t\t\t\t\t\tvalues.push( value );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn values;\n\t\t\t},\n\n\t\t\tset: function( elem, value ) {\n\t\t\t\tvar optionSet, option,\n\t\t\t\t\toptions = elem.options,\n\t\t\t\t\tvalues = jQuery.makeArray( value ),\n\t\t\t\t\ti = options.length;\n\n\t\t\t\twhile ( i-- ) {\n\t\t\t\t\toption = options[ i ];\n\n\t\t\t\t\t/* eslint-disable no-cond-assign */\n\n\t\t\t\t\tif ( option.selected =\n\t\t\t\t\t\tjQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1\n\t\t\t\t\t) {\n\t\t\t\t\t\toptionSet = true;\n\t\t\t\t\t}\n\n\t\t\t\t\t/* eslint-enable no-cond-assign */\n\t\t\t\t}\n\n\t\t\t\t// Force browsers to behave consistently when non-matching value is set\n\t\t\t\tif ( !optionSet ) {\n\t\t\t\t\telem.selectedIndex = -1;\n\t\t\t\t}\n\t\t\t\treturn values;\n\t\t\t}\n\t\t}\n\t}\n} );\n\n// Radios and checkboxes getter/setter\njQuery.each( [ \"radio\", \"checkbox\" ], function() {\n\tjQuery.valHooks[ this ] = {\n\t\tset: function( elem, value ) {\n\t\t\tif ( Array.isArray( value ) ) {\n\t\t\t\treturn ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 );\n\t\t\t}\n\t\t}\n\t};\n\tif ( !support.checkOn ) {\n\t\tjQuery.valHooks[ this ].get = function( elem ) {\n\t\t\treturn elem.getAttribute( \"value\" ) === null ? \"on\" : elem.value;\n\t\t};\n\t}\n} );\n\n\n\n\n// Return jQuery for attributes-only inclusion\n\n\nsupport.focusin = \"onfocusin\" in window;\n\n\nvar rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,\n\tstopPropagationCallback = function( e ) {\n\t\te.stopPropagation();\n\t};\n\njQuery.extend( jQuery.event, {\n\n\ttrigger: function( event, data, elem, onlyHandlers ) {\n\n\t\tvar i, cur, tmp, bubbleType, ontype, handle, special, lastElement,\n\t\t\teventPath = [ elem || document ],\n\t\t\ttype = hasOwn.call( event, \"type\" ) ? event.type : event,\n\t\t\tnamespaces = hasOwn.call( event, \"namespace\" ) ? event.namespace.split( \".\" ) : [];\n\n\t\tcur = lastElement = tmp = elem = elem || document;\n\n\t\t// Don't do events on text and comment nodes\n\t\tif ( elem.nodeType === 3 || elem.nodeType === 8 ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// focus/blur morphs to focusin/out; ensure we're not firing them right now\n\t\tif ( rfocusMorph.test( type + jQuery.event.triggered ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( type.indexOf( \".\" ) > -1 ) {\n\n\t\t\t// Namespaced trigger; create a regexp to match event type in handle()\n\t\t\tnamespaces = type.split( \".\" );\n\t\t\ttype = namespaces.shift();\n\t\t\tnamespaces.sort();\n\t\t}\n\t\tontype = type.indexOf( \":\" ) < 0 && \"on\" + type;\n\n\t\t// Caller can pass in a jQuery.Event object, Object, or just an event type string\n\t\tevent = event[ jQuery.expando ] ?\n\t\t\tevent :\n\t\t\tnew jQuery.Event( type, typeof event === \"object\" && event );\n\n\t\t// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)\n\t\tevent.isTrigger = onlyHandlers ? 2 : 3;\n\t\tevent.namespace = namespaces.join( \".\" );\n\t\tevent.rnamespace = event.namespace ?\n\t\t\tnew RegExp( \"(^|\\\\.)\" + namespaces.join( \"\\\\.(?:.*\\\\.|)\" ) + \"(\\\\.|$)\" ) :\n\t\t\tnull;\n\n\t\t// Clean up the event in case it is being reused\n\t\tevent.result = undefined;\n\t\tif ( !event.target ) {\n\t\t\tevent.target = elem;\n\t\t}\n\n\t\t// Clone any incoming data and prepend the event, creating the handler arg list\n\t\tdata = data == null ?\n\t\t\t[ event ] :\n\t\t\tjQuery.makeArray( data, [ event ] );\n\n\t\t// Allow special events to draw outside the lines\n\t\tspecial = jQuery.event.special[ type ] || {};\n\t\tif ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Determine event propagation path in advance, per W3C events spec (#9951)\n\t\t// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)\n\t\tif ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) {\n\n\t\t\tbubbleType = special.delegateType || type;\n\t\t\tif ( !rfocusMorph.test( bubbleType + type ) ) {\n\t\t\t\tcur = cur.parentNode;\n\t\t\t}\n\t\t\tfor ( ; cur; cur = cur.parentNode ) {\n\t\t\t\teventPath.push( cur );\n\t\t\t\ttmp = cur;\n\t\t\t}\n\n\t\t\t// Only add window if we got to document (e.g., not plain obj or detached DOM)\n\t\t\tif ( tmp === ( elem.ownerDocument || document ) ) {\n\t\t\t\teventPath.push( tmp.defaultView || tmp.parentWindow || window );\n\t\t\t}\n\t\t}\n\n\t\t// Fire handlers on the event path\n\t\ti = 0;\n\t\twhile ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {\n\t\t\tlastElement = cur;\n\t\t\tevent.type = i > 1 ?\n\t\t\t\tbubbleType :\n\t\t\t\tspecial.bindType || type;\n\n\t\t\t// jQuery handler\n\t\t\thandle = (\n\t\t\t\t\tdataPriv.get( cur, \"events\" ) || Object.create( null )\n\t\t\t\t)[ event.type ] &&\n\t\t\t\tdataPriv.get( cur, \"handle\" );\n\t\t\tif ( handle ) {\n\t\t\t\thandle.apply( cur, data );\n\t\t\t}\n\n\t\t\t// Native handler\n\t\t\thandle = ontype && cur[ ontype ];\n\t\t\tif ( handle && handle.apply && acceptData( cur ) ) {\n\t\t\t\tevent.result = handle.apply( cur, data );\n\t\t\t\tif ( event.result === false ) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tevent.type = type;\n\n\t\t// If nobody prevented the default action, do it now\n\t\tif ( !onlyHandlers && !event.isDefaultPrevented() ) {\n\n\t\t\tif ( ( !special._default ||\n\t\t\t\tspecial._default.apply( eventPath.pop(), data ) === false ) &&\n\t\t\t\tacceptData( elem ) ) {\n\n\t\t\t\t// Call a native DOM method on the target with the same name as the event.\n\t\t\t\t// Don't do default actions on window, that's where global variables be (#6170)\n\t\t\t\tif ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) {\n\n\t\t\t\t\t// Don't re-trigger an onFOO event when we call its FOO() method\n\t\t\t\t\ttmp = elem[ ontype ];\n\n\t\t\t\t\tif ( tmp ) {\n\t\t\t\t\t\telem[ ontype ] = null;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Prevent re-triggering of the same event, since we already bubbled it above\n\t\t\t\t\tjQuery.event.triggered = type;\n\n\t\t\t\t\tif ( event.isPropagationStopped() ) {\n\t\t\t\t\t\tlastElement.addEventListener( type, stopPropagationCallback );\n\t\t\t\t\t}\n\n\t\t\t\t\telem[ type ]();\n\n\t\t\t\t\tif ( event.isPropagationStopped() ) {\n\t\t\t\t\t\tlastElement.removeEventListener( type, stopPropagationCallback );\n\t\t\t\t\t}\n\n\t\t\t\t\tjQuery.event.triggered = undefined;\n\n\t\t\t\t\tif ( tmp ) {\n\t\t\t\t\t\telem[ ontype ] = tmp;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn event.result;\n\t},\n\n\t// Piggyback on a donor event to simulate a different one\n\t// Used only for `focus(in | out)` events\n\tsimulate: function( type, elem, event ) {\n\t\tvar e = jQuery.extend(\n\t\t\tnew jQuery.Event(),\n\t\t\tevent,\n\t\t\t{\n\t\t\t\ttype: type,\n\t\t\t\tisSimulated: true\n\t\t\t}\n\t\t);\n\n\t\tjQuery.event.trigger( e, null, elem );\n\t}\n\n} );\n\njQuery.fn.extend( {\n\n\ttrigger: function( type, data ) {\n\t\treturn this.each( function() {\n\t\t\tjQuery.event.trigger( type, data, this );\n\t\t} );\n\t},\n\ttriggerHandler: function( type, data ) {\n\t\tvar elem = this[ 0 ];\n\t\tif ( elem ) {\n\t\t\treturn jQuery.event.trigger( type, data, elem, true );\n\t\t}\n\t}\n} );\n\n\n// Support: Firefox <=44\n// Firefox doesn't have focus(in | out) events\n// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787\n//\n// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1\n// focus(in | out) events fire after focus & blur events,\n// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order\n// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857\nif ( !support.focusin ) {\n\tjQuery.each( { focus: \"focusin\", blur: \"focusout\" }, function( orig, fix ) {\n\n\t\t// Attach a single capturing handler on the document while someone wants focusin/focusout\n\t\tvar handler = function( event ) {\n\t\t\tjQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) );\n\t\t};\n\n\t\tjQuery.event.special[ fix ] = {\n\t\t\tsetup: function() {\n\n\t\t\t\t// Handle: regular nodes (via `this.ownerDocument`), window\n\t\t\t\t// (via `this.document`) & document (via `this`).\n\t\t\t\tvar doc = this.ownerDocument || this.document || this,\n\t\t\t\t\tattaches = dataPriv.access( doc, fix );\n\n\t\t\t\tif ( !attaches ) {\n\t\t\t\t\tdoc.addEventListener( orig, handler, true );\n\t\t\t\t}\n\t\t\t\tdataPriv.access( doc, fix, ( attaches || 0 ) + 1 );\n\t\t\t},\n\t\t\tteardown: function() {\n\t\t\t\tvar doc = this.ownerDocument || this.document || this,\n\t\t\t\t\tattaches = dataPriv.access( doc, fix ) - 1;\n\n\t\t\t\tif ( !attaches ) {\n\t\t\t\t\tdoc.removeEventListener( orig, handler, true );\n\t\t\t\t\tdataPriv.remove( doc, fix );\n\n\t\t\t\t} else {\n\t\t\t\t\tdataPriv.access( doc, fix, attaches );\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t} );\n}\nvar location = window.location;\n\nvar nonce = { guid: Date.now() };\n\nvar rquery = ( /\\?/ );\n\n\n\n// Cross-browser xml parsing\njQuery.parseXML = function( data ) {\n\tvar xml;\n\tif ( !data || typeof data !== \"string\" ) {\n\t\treturn null;\n\t}\n\n\t// Support: IE 9 - 11 only\n\t// IE throws on parseFromString with invalid input.\n\ttry {\n\t\txml = ( new window.DOMParser() ).parseFromString( data, \"text/xml\" );\n\t} catch ( e ) {\n\t\txml = undefined;\n\t}\n\n\tif ( !xml || xml.getElementsByTagName( \"parsererror\" ).length ) {\n\t\tjQuery.error( \"Invalid XML: \" + data );\n\t}\n\treturn xml;\n};\n\n\nvar\n\trbracket = /\\[\\]$/,\n\trCRLF = /\\r?\\n/g,\n\trsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,\n\trsubmittable = /^(?:input|select|textarea|keygen)/i;\n\nfunction buildParams( prefix, obj, traditional, add ) {\n\tvar name;\n\n\tif ( Array.isArray( obj ) ) {\n\n\t\t// Serialize array item.\n\t\tjQuery.each( obj, function( i, v ) {\n\t\t\tif ( traditional || rbracket.test( prefix ) ) {\n\n\t\t\t\t// Treat each array item as a scalar.\n\t\t\t\tadd( prefix, v );\n\n\t\t\t} else {\n\n\t\t\t\t// Item is non-scalar (array or object), encode its numeric index.\n\t\t\t\tbuildParams(\n\t\t\t\t\tprefix + \"[\" + ( typeof v === \"object\" && v != null ? i : \"\" ) + \"]\",\n\t\t\t\t\tv,\n\t\t\t\t\ttraditional,\n\t\t\t\t\tadd\n\t\t\t\t);\n\t\t\t}\n\t\t} );\n\n\t} else if ( !traditional && toType( obj ) === \"object\" ) {\n\n\t\t// Serialize object item.\n\t\tfor ( name in obj ) {\n\t\t\tbuildParams( prefix + \"[\" + name + \"]\", obj[ name ], traditional, add );\n\t\t}\n\n\t} else {\n\n\t\t// Serialize scalar item.\n\t\tadd( prefix, obj );\n\t}\n}\n\n// Serialize an array of form elements or a set of\n// key/values into a query string\njQuery.param = function( a, traditional ) {\n\tvar prefix,\n\t\ts = [],\n\t\tadd = function( key, valueOrFunction ) {\n\n\t\t\t// If value is a function, invoke it and use its return value\n\t\t\tvar value = isFunction( valueOrFunction ) ?\n\t\t\t\tvalueOrFunction() :\n\t\t\t\tvalueOrFunction;\n\n\t\t\ts[ s.length ] = encodeURIComponent( key ) + \"=\" +\n\t\t\t\tencodeURIComponent( value == null ? \"\" : value );\n\t\t};\n\n\tif ( a == null ) {\n\t\treturn \"\";\n\t}\n\n\t// If an array was passed in, assume that it is an array of form elements.\n\tif ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {\n\n\t\t// Serialize the form elements\n\t\tjQuery.each( a, function() {\n\t\t\tadd( this.name, this.value );\n\t\t} );\n\n\t} else {\n\n\t\t// If traditional, encode the \"old\" way (the way 1.3.2 or older\n\t\t// did it), otherwise encode params recursively.\n\t\tfor ( prefix in a ) {\n\t\t\tbuildParams( prefix, a[ prefix ], traditional, add );\n\t\t}\n\t}\n\n\t// Return the resulting serialization\n\treturn s.join( \"&\" );\n};\n\njQuery.fn.extend( {\n\tserialize: function() {\n\t\treturn jQuery.param( this.serializeArray() );\n\t},\n\tserializeArray: function() {\n\t\treturn this.map( function() {\n\n\t\t\t// Can add propHook for \"elements\" to filter or add form elements\n\t\t\tvar elements = jQuery.prop( this, \"elements\" );\n\t\t\treturn elements ? jQuery.makeArray( elements ) : this;\n\t\t} )\n\t\t.filter( function() {\n\t\t\tvar type = this.type;\n\n\t\t\t// Use .is( \":disabled\" ) so that fieldset[disabled] works\n\t\t\treturn this.name && !jQuery( this ).is( \":disabled\" ) &&\n\t\t\t\trsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&\n\t\t\t\t( this.checked || !rcheckableType.test( type ) );\n\t\t} )\n\t\t.map( function( _i, elem ) {\n\t\t\tvar val = jQuery( this ).val();\n\n\t\t\tif ( val == null ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tif ( Array.isArray( val ) ) {\n\t\t\t\treturn jQuery.map( val, function( val ) {\n\t\t\t\t\treturn { name: elem.name, value: val.replace( rCRLF, \"\\r\\n\" ) };\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\treturn { name: elem.name, value: val.replace( rCRLF, \"\\r\\n\" ) };\n\t\t} ).get();\n\t}\n} );\n\n\nvar\n\tr20 = /%20/g,\n\trhash = /#.*$/,\n\trantiCache = /([?&])_=[^&]*/,\n\trheaders = /^(.*?):[ \\t]*([^\\r\\n]*)$/mg,\n\n\t// #7653, #8125, #8152: local protocol detection\n\trlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,\n\trnoContent = /^(?:GET|HEAD)$/,\n\trprotocol = /^\\/\\//,\n\n\t/* Prefilters\n\t * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)\n\t * 2) These are called:\n\t * - BEFORE asking for a transport\n\t * - AFTER param serialization (s.data is a string if s.processData is true)\n\t * 3) key is the dataType\n\t * 4) the catchall symbol \"*\" can be used\n\t * 5) execution will start with transport dataType and THEN continue down to \"*\" if needed\n\t */\n\tprefilters = {},\n\n\t/* Transports bindings\n\t * 1) key is the dataType\n\t * 2) the catchall symbol \"*\" can be used\n\t * 3) selection will start with transport dataType and THEN go to \"*\" if needed\n\t */\n\ttransports = {},\n\n\t// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression\n\tallTypes = \"*/\".concat( \"*\" ),\n\n\t// Anchor tag for parsing the document origin\n\toriginAnchor = document.createElement( \"a\" );\n\toriginAnchor.href = location.href;\n\n// Base \"constructor\" for jQuery.ajaxPrefilter and jQuery.ajaxTransport\nfunction addToPrefiltersOrTransports( structure ) {\n\n\t// dataTypeExpression is optional and defaults to \"*\"\n\treturn function( dataTypeExpression, func ) {\n\n\t\tif ( typeof dataTypeExpression !== \"string\" ) {\n\t\t\tfunc = dataTypeExpression;\n\t\t\tdataTypeExpression = \"*\";\n\t\t}\n\n\t\tvar dataType,\n\t\t\ti = 0,\n\t\t\tdataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || [];\n\n\t\tif ( isFunction( func ) ) {\n\n\t\t\t// For each dataType in the dataTypeExpression\n\t\t\twhile ( ( dataType = dataTypes[ i++ ] ) ) {\n\n\t\t\t\t// Prepend if requested\n\t\t\t\tif ( dataType[ 0 ] === \"+\" ) {\n\t\t\t\t\tdataType = dataType.slice( 1 ) || \"*\";\n\t\t\t\t\t( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func );\n\n\t\t\t\t// Otherwise append\n\t\t\t\t} else {\n\t\t\t\t\t( structure[ dataType ] = structure[ dataType ] || [] ).push( func );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n}\n\n// Base inspection function for prefilters and transports\nfunction inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {\n\n\tvar inspected = {},\n\t\tseekingTransport = ( structure === transports );\n\n\tfunction inspect( dataType ) {\n\t\tvar selected;\n\t\tinspected[ dataType ] = true;\n\t\tjQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {\n\t\t\tvar dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );\n\t\t\tif ( typeof dataTypeOrTransport === \"string\" &&\n\t\t\t\t!seekingTransport && !inspected[ dataTypeOrTransport ] ) {\n\n\t\t\t\toptions.dataTypes.unshift( dataTypeOrTransport );\n\t\t\t\tinspect( dataTypeOrTransport );\n\t\t\t\treturn false;\n\t\t\t} else if ( seekingTransport ) {\n\t\t\t\treturn !( selected = dataTypeOrTransport );\n\t\t\t}\n\t\t} );\n\t\treturn selected;\n\t}\n\n\treturn inspect( options.dataTypes[ 0 ] ) || !inspected[ \"*\" ] && inspect( \"*\" );\n}\n\n// A special extend for ajax options\n// that takes \"flat\" options (not to be deep extended)\n// Fixes #9887\nfunction ajaxExtend( target, src ) {\n\tvar key, deep,\n\t\tflatOptions = jQuery.ajaxSettings.flatOptions || {};\n\n\tfor ( key in src ) {\n\t\tif ( src[ key ] !== undefined ) {\n\t\t\t( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];\n\t\t}\n\t}\n\tif ( deep ) {\n\t\tjQuery.extend( true, target, deep );\n\t}\n\n\treturn target;\n}\n\n/* Handles responses to an ajax request:\n * - finds the right dataType (mediates between content-type and expected dataType)\n * - returns the corresponding response\n */\nfunction ajaxHandleResponses( s, jqXHR, responses ) {\n\n\tvar ct, type, finalDataType, firstDataType,\n\t\tcontents = s.contents,\n\t\tdataTypes = s.dataTypes;\n\n\t// Remove auto dataType and get content-type in the process\n\twhile ( dataTypes[ 0 ] === \"*\" ) {\n\t\tdataTypes.shift();\n\t\tif ( ct === undefined ) {\n\t\t\tct = s.mimeType || jqXHR.getResponseHeader( \"Content-Type\" );\n\t\t}\n\t}\n\n\t// Check if we're dealing with a known content-type\n\tif ( ct ) {\n\t\tfor ( type in contents ) {\n\t\t\tif ( contents[ type ] && contents[ type ].test( ct ) ) {\n\t\t\t\tdataTypes.unshift( type );\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Check to see if we have a response for the expected dataType\n\tif ( dataTypes[ 0 ] in responses ) {\n\t\tfinalDataType = dataTypes[ 0 ];\n\t} else {\n\n\t\t// Try convertible dataTypes\n\t\tfor ( type in responses ) {\n\t\t\tif ( !dataTypes[ 0 ] || s.converters[ type + \" \" + dataTypes[ 0 ] ] ) {\n\t\t\t\tfinalDataType = type;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif ( !firstDataType ) {\n\t\t\t\tfirstDataType = type;\n\t\t\t}\n\t\t}\n\n\t\t// Or just use first one\n\t\tfinalDataType = finalDataType || firstDataType;\n\t}\n\n\t// If we found a dataType\n\t// We add the dataType to the list if needed\n\t// and return the corresponding response\n\tif ( finalDataType ) {\n\t\tif ( finalDataType !== dataTypes[ 0 ] ) {\n\t\t\tdataTypes.unshift( finalDataType );\n\t\t}\n\t\treturn responses[ finalDataType ];\n\t}\n}\n\n/* Chain conversions given the request and the original response\n * Also sets the responseXXX fields on the jqXHR instance\n */\nfunction ajaxConvert( s, response, jqXHR, isSuccess ) {\n\tvar conv2, current, conv, tmp, prev,\n\t\tconverters = {},\n\n\t\t// Work with a copy of dataTypes in case we need to modify it for conversion\n\t\tdataTypes = s.dataTypes.slice();\n\n\t// Create converters map with lowercased keys\n\tif ( dataTypes[ 1 ] ) {\n\t\tfor ( conv in s.converters ) {\n\t\t\tconverters[ conv.toLowerCase() ] = s.converters[ conv ];\n\t\t}\n\t}\n\n\tcurrent = dataTypes.shift();\n\n\t// Convert to each sequential dataType\n\twhile ( current ) {\n\n\t\tif ( s.responseFields[ current ] ) {\n\t\t\tjqXHR[ s.responseFields[ current ] ] = response;\n\t\t}\n\n\t\t// Apply the dataFilter if provided\n\t\tif ( !prev && isSuccess && s.dataFilter ) {\n\t\t\tresponse = s.dataFilter( response, s.dataType );\n\t\t}\n\n\t\tprev = current;\n\t\tcurrent = dataTypes.shift();\n\n\t\tif ( current ) {\n\n\t\t\t// There's only work to do if current dataType is non-auto\n\t\t\tif ( current === \"*\" ) {\n\n\t\t\t\tcurrent = prev;\n\n\t\t\t// Convert response if prev dataType is non-auto and differs from current\n\t\t\t} else if ( prev !== \"*\" && prev !== current ) {\n\n\t\t\t\t// Seek a direct converter\n\t\t\t\tconv = converters[ prev + \" \" + current ] || converters[ \"* \" + current ];\n\n\t\t\t\t// If none found, seek a pair\n\t\t\t\tif ( !conv ) {\n\t\t\t\t\tfor ( conv2 in converters ) {\n\n\t\t\t\t\t\t// If conv2 outputs current\n\t\t\t\t\t\ttmp = conv2.split( \" \" );\n\t\t\t\t\t\tif ( tmp[ 1 ] === current ) {\n\n\t\t\t\t\t\t\t// If prev can be converted to accepted input\n\t\t\t\t\t\t\tconv = converters[ prev + \" \" + tmp[ 0 ] ] ||\n\t\t\t\t\t\t\t\tconverters[ \"* \" + tmp[ 0 ] ];\n\t\t\t\t\t\t\tif ( conv ) {\n\n\t\t\t\t\t\t\t\t// Condense equivalence converters\n\t\t\t\t\t\t\t\tif ( conv === true ) {\n\t\t\t\t\t\t\t\t\tconv = converters[ conv2 ];\n\n\t\t\t\t\t\t\t\t// Otherwise, insert the intermediate dataType\n\t\t\t\t\t\t\t\t} else if ( converters[ conv2 ] !== true ) {\n\t\t\t\t\t\t\t\t\tcurrent = tmp[ 0 ];\n\t\t\t\t\t\t\t\t\tdataTypes.unshift( tmp[ 1 ] );\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Apply converter (if not an equivalence)\n\t\t\t\tif ( conv !== true ) {\n\n\t\t\t\t\t// Unless errors are allowed to bubble, catch and return them\n\t\t\t\t\tif ( conv && s.throws ) {\n\t\t\t\t\t\tresponse = conv( response );\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tresponse = conv( response );\n\t\t\t\t\t\t} catch ( e ) {\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tstate: \"parsererror\",\n\t\t\t\t\t\t\t\terror: conv ? e : \"No conversion from \" + prev + \" to \" + current\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { state: \"success\", data: response };\n}\n\njQuery.extend( {\n\n\t// Counter for holding the number of active queries\n\tactive: 0,\n\n\t// Last-Modified header cache for next request\n\tlastModified: {},\n\tetag: {},\n\n\tajaxSettings: {\n\t\turl: location.href,\n\t\ttype: \"GET\",\n\t\tisLocal: rlocalProtocol.test( location.protocol ),\n\t\tglobal: true,\n\t\tprocessData: true,\n\t\tasync: true,\n\t\tcontentType: \"application/x-www-form-urlencoded; charset=UTF-8\",\n\n\t\t/*\n\t\ttimeout: 0,\n\t\tdata: null,\n\t\tdataType: null,\n\t\tusername: null,\n\t\tpassword: null,\n\t\tcache: null,\n\t\tthrows: false,\n\t\ttraditional: false,\n\t\theaders: {},\n\t\t*/\n\n\t\taccepts: {\n\t\t\t\"*\": allTypes,\n\t\t\ttext: \"text/plain\",\n\t\t\thtml: \"text/html\",\n\t\t\txml: \"application/xml, text/xml\",\n\t\t\tjson: \"application/json, text/javascript\"\n\t\t},\n\n\t\tcontents: {\n\t\t\txml: /\\bxml\\b/,\n\t\t\thtml: /\\bhtml/,\n\t\t\tjson: /\\bjson\\b/\n\t\t},\n\n\t\tresponseFields: {\n\t\t\txml: \"responseXML\",\n\t\t\ttext: \"responseText\",\n\t\t\tjson: \"responseJSON\"\n\t\t},\n\n\t\t// Data converters\n\t\t// Keys separate source (or catchall \"*\") and destination types with a single space\n\t\tconverters: {\n\n\t\t\t// Convert anything to text\n\t\t\t\"* text\": String,\n\n\t\t\t// Text to html (true = no transformation)\n\t\t\t\"text html\": true,\n\n\t\t\t// Evaluate text as a json expression\n\t\t\t\"text json\": JSON.parse,\n\n\t\t\t// Parse text as xml\n\t\t\t\"text xml\": jQuery.parseXML\n\t\t},\n\n\t\t// For options that shouldn't be deep extended:\n\t\t// you can add your own custom options here if\n\t\t// and when you create one that shouldn't be\n\t\t// deep extended (see ajaxExtend)\n\t\tflatOptions: {\n\t\t\turl: true,\n\t\t\tcontext: true\n\t\t}\n\t},\n\n\t// Creates a full fledged settings object into target\n\t// with both ajaxSettings and settings fields.\n\t// If target is omitted, writes into ajaxSettings.\n\tajaxSetup: function( target, settings ) {\n\t\treturn settings ?\n\n\t\t\t// Building a settings object\n\t\t\tajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :\n\n\t\t\t// Extending ajaxSettings\n\t\t\tajaxExtend( jQuery.ajaxSettings, target );\n\t},\n\n\tajaxPrefilter: addToPrefiltersOrTransports( prefilters ),\n\tajaxTransport: addToPrefiltersOrTransports( transports ),\n\n\t// Main method\n\tajax: function( url, options ) {\n\n\t\t// If url is an object, simulate pre-1.5 signature\n\t\tif ( typeof url === \"object\" ) {\n\t\t\toptions = url;\n\t\t\turl = undefined;\n\t\t}\n\n\t\t// Force options to be an object\n\t\toptions = options || {};\n\n\t\tvar transport,\n\n\t\t\t// URL without anti-cache param\n\t\t\tcacheURL,\n\n\t\t\t// Response headers\n\t\t\tresponseHeadersString,\n\t\t\tresponseHeaders,\n\n\t\t\t// timeout handle\n\t\t\ttimeoutTimer,\n\n\t\t\t// Url cleanup var\n\t\t\turlAnchor,\n\n\t\t\t// Request state (becomes false upon send and true upon completion)\n\t\t\tcompleted,\n\n\t\t\t// To know if global events are to be dispatched\n\t\t\tfireGlobals,\n\n\t\t\t// Loop variable\n\t\t\ti,\n\n\t\t\t// uncached part of the url\n\t\t\tuncached,\n\n\t\t\t// Create the final options object\n\t\t\ts = jQuery.ajaxSetup( {}, options ),\n\n\t\t\t// Callbacks context\n\t\t\tcallbackContext = s.context || s,\n\n\t\t\t// Context for global events is callbackContext if it is a DOM node or jQuery collection\n\t\t\tglobalEventContext = s.context &&\n\t\t\t\t( callbackContext.nodeType || callbackContext.jquery ) ?\n\t\t\t\t\tjQuery( callbackContext ) :\n\t\t\t\t\tjQuery.event,\n\n\t\t\t// Deferreds\n\t\t\tdeferred = jQuery.Deferred(),\n\t\t\tcompleteDeferred = jQuery.Callbacks( \"once memory\" ),\n\n\t\t\t// Status-dependent callbacks\n\t\t\tstatusCode = s.statusCode || {},\n\n\t\t\t// Headers (they are sent all at once)\n\t\t\trequestHeaders = {},\n\t\t\trequestHeadersNames = {},\n\n\t\t\t// Default abort message\n\t\t\tstrAbort = \"canceled\",\n\n\t\t\t// Fake xhr\n\t\t\tjqXHR = {\n\t\t\t\treadyState: 0,\n\n\t\t\t\t// Builds headers hashtable if needed\n\t\t\t\tgetResponseHeader: function( key ) {\n\t\t\t\t\tvar match;\n\t\t\t\t\tif ( completed ) {\n\t\t\t\t\t\tif ( !responseHeaders ) {\n\t\t\t\t\t\t\tresponseHeaders = {};\n\t\t\t\t\t\t\twhile ( ( match = rheaders.exec( responseHeadersString ) ) ) {\n\t\t\t\t\t\t\t\tresponseHeaders[ match[ 1 ].toLowerCase() + \" \" ] =\n\t\t\t\t\t\t\t\t\t( responseHeaders[ match[ 1 ].toLowerCase() + \" \" ] || [] )\n\t\t\t\t\t\t\t\t\t\t.concat( match[ 2 ] );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmatch = responseHeaders[ key.toLowerCase() + \" \" ];\n\t\t\t\t\t}\n\t\t\t\t\treturn match == null ? null : match.join( \", \" );\n\t\t\t\t},\n\n\t\t\t\t// Raw string\n\t\t\t\tgetAllResponseHeaders: function() {\n\t\t\t\t\treturn completed ? responseHeadersString : null;\n\t\t\t\t},\n\n\t\t\t\t// Caches the header\n\t\t\t\tsetRequestHeader: function( name, value ) {\n\t\t\t\t\tif ( completed == null ) {\n\t\t\t\t\t\tname = requestHeadersNames[ name.toLowerCase() ] =\n\t\t\t\t\t\t\trequestHeadersNames[ name.toLowerCase() ] || name;\n\t\t\t\t\t\trequestHeaders[ name ] = value;\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// Overrides response content-type header\n\t\t\t\toverrideMimeType: function( type ) {\n\t\t\t\t\tif ( completed == null ) {\n\t\t\t\t\t\ts.mimeType = type;\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// Status-dependent callbacks\n\t\t\t\tstatusCode: function( map ) {\n\t\t\t\t\tvar code;\n\t\t\t\t\tif ( map ) {\n\t\t\t\t\t\tif ( completed ) {\n\n\t\t\t\t\t\t\t// Execute the appropriate callbacks\n\t\t\t\t\t\t\tjqXHR.always( map[ jqXHR.status ] );\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t// Lazy-add the new callbacks in a way that preserves old ones\n\t\t\t\t\t\t\tfor ( code in map ) {\n\t\t\t\t\t\t\t\tstatusCode[ code ] = [ statusCode[ code ], map[ code ] ];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// Cancel the request\n\t\t\t\tabort: function( statusText ) {\n\t\t\t\t\tvar finalText = statusText || strAbort;\n\t\t\t\t\tif ( transport ) {\n\t\t\t\t\t\ttransport.abort( finalText );\n\t\t\t\t\t}\n\t\t\t\t\tdone( 0, finalText );\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\t\t\t};\n\n\t\t// Attach deferreds\n\t\tdeferred.promise( jqXHR );\n\n\t\t// Add protocol if not provided (prefilters might expect it)\n\t\t// Handle falsy url in the settings object (#10093: consistency with old signature)\n\t\t// We also use the url parameter if available\n\t\ts.url = ( ( url || s.url || location.href ) + \"\" )\n\t\t\t.replace( rprotocol, location.protocol + \"//\" );\n\n\t\t// Alias method option to type as per ticket #12004\n\t\ts.type = options.method || options.type || s.method || s.type;\n\n\t\t// Extract dataTypes list\n\t\ts.dataTypes = ( s.dataType || \"*\" ).toLowerCase().match( rnothtmlwhite ) || [ \"\" ];\n\n\t\t// A cross-domain request is in order when the origin doesn't match the current origin.\n\t\tif ( s.crossDomain == null ) {\n\t\t\turlAnchor = document.createElement( \"a\" );\n\n\t\t\t// Support: IE <=8 - 11, Edge 12 - 15\n\t\t\t// IE throws exception on accessing the href property if url is malformed,\n\t\t\t// e.g. http://example.com:80x/\n\t\t\ttry {\n\t\t\t\turlAnchor.href = s.url;\n\n\t\t\t\t// Support: IE <=8 - 11 only\n\t\t\t\t// Anchor's host property isn't correctly set when s.url is relative\n\t\t\t\turlAnchor.href = urlAnchor.href;\n\t\t\t\ts.crossDomain = originAnchor.protocol + \"//\" + originAnchor.host !==\n\t\t\t\t\turlAnchor.protocol + \"//\" + urlAnchor.host;\n\t\t\t} catch ( e ) {\n\n\t\t\t\t// If there is an error parsing the URL, assume it is crossDomain,\n\t\t\t\t// it can be rejected by the transport if it is invalid\n\t\t\t\ts.crossDomain = true;\n\t\t\t}\n\t\t}\n\n\t\t// Convert data if not already a string\n\t\tif ( s.data && s.processData && typeof s.data !== \"string\" ) {\n\t\t\ts.data = jQuery.param( s.data, s.traditional );\n\t\t}\n\n\t\t// Apply prefilters\n\t\tinspectPrefiltersOrTransports( prefilters, s, options, jqXHR );\n\n\t\t// If request was aborted inside a prefilter, stop there\n\t\tif ( completed ) {\n\t\t\treturn jqXHR;\n\t\t}\n\n\t\t// We can fire global events as of now if asked to\n\t\t// Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)\n\t\tfireGlobals = jQuery.event && s.global;\n\n\t\t// Watch for a new set of requests\n\t\tif ( fireGlobals && jQuery.active++ === 0 ) {\n\t\t\tjQuery.event.trigger( \"ajaxStart\" );\n\t\t}\n\n\t\t// Uppercase the type\n\t\ts.type = s.type.toUpperCase();\n\n\t\t// Determine if request has content\n\t\ts.hasContent = !rnoContent.test( s.type );\n\n\t\t// Save the URL in case we're toying with the If-Modified-Since\n\t\t// and/or If-None-Match header later on\n\t\t// Remove hash to simplify url manipulation\n\t\tcacheURL = s.url.replace( rhash, \"\" );\n\n\t\t// More options handling for requests with no content\n\t\tif ( !s.hasContent ) {\n\n\t\t\t// Remember the hash so we can put it back\n\t\t\tuncached = s.url.slice( cacheURL.length );\n\n\t\t\t// If data is available and should be processed, append data to url\n\t\t\tif ( s.data && ( s.processData || typeof s.data === \"string\" ) ) {\n\t\t\t\tcacheURL += ( rquery.test( cacheURL ) ? \"&\" : \"?\" ) + s.data;\n\n\t\t\t\t// #9682: remove data so that it's not used in an eventual retry\n\t\t\t\tdelete s.data;\n\t\t\t}\n\n\t\t\t// Add or update anti-cache param if needed\n\t\t\tif ( s.cache === false ) {\n\t\t\t\tcacheURL = cacheURL.replace( rantiCache, \"$1\" );\n\t\t\t\tuncached = ( rquery.test( cacheURL ) ? \"&\" : \"?\" ) + \"_=\" + ( nonce.guid++ ) +\n\t\t\t\t\tuncached;\n\t\t\t}\n\n\t\t\t// Put hash and anti-cache on the URL that will be requested (gh-1732)\n\t\t\ts.url = cacheURL + uncached;\n\n\t\t// Change '%20' to '+' if this is encoded form body content (gh-2658)\n\t\t} else if ( s.data && s.processData &&\n\t\t\t( s.contentType || \"\" ).indexOf( \"application/x-www-form-urlencoded\" ) === 0 ) {\n\t\t\ts.data = s.data.replace( r20, \"+\" );\n\t\t}\n\n\t\t// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.\n\t\tif ( s.ifModified ) {\n\t\t\tif ( jQuery.lastModified[ cacheURL ] ) {\n\t\t\t\tjqXHR.setRequestHeader( \"If-Modified-Since\", jQuery.lastModified[ cacheURL ] );\n\t\t\t}\n\t\t\tif ( jQuery.etag[ cacheURL ] ) {\n\t\t\t\tjqXHR.setRequestHeader( \"If-None-Match\", jQuery.etag[ cacheURL ] );\n\t\t\t}\n\t\t}\n\n\t\t// Set the correct header, if data is being sent\n\t\tif ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {\n\t\t\tjqXHR.setRequestHeader( \"Content-Type\", s.contentType );\n\t\t}\n\n\t\t// Set the Accepts header for the server, depending on the dataType\n\t\tjqXHR.setRequestHeader(\n\t\t\t\"Accept\",\n\t\t\ts.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ?\n\t\t\t\ts.accepts[ s.dataTypes[ 0 ] ] +\n\t\t\t\t\t( s.dataTypes[ 0 ] !== \"*\" ? \", \" + allTypes + \"; q=0.01\" : \"\" ) :\n\t\t\t\ts.accepts[ \"*\" ]\n\t\t);\n\n\t\t// Check for headers option\n\t\tfor ( i in s.headers ) {\n\t\t\tjqXHR.setRequestHeader( i, s.headers[ i ] );\n\t\t}\n\n\t\t// Allow custom headers/mimetypes and early abort\n\t\tif ( s.beforeSend &&\n\t\t\t( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) {\n\n\t\t\t// Abort if not done already and return\n\t\t\treturn jqXHR.abort();\n\t\t}\n\n\t\t// Aborting is no longer a cancellation\n\t\tstrAbort = \"abort\";\n\n\t\t// Install callbacks on deferreds\n\t\tcompleteDeferred.add( s.complete );\n\t\tjqXHR.done( s.success );\n\t\tjqXHR.fail( s.error );\n\n\t\t// Get transport\n\t\ttransport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );\n\n\t\t// If no transport, we auto-abort\n\t\tif ( !transport ) {\n\t\t\tdone( -1, \"No Transport\" );\n\t\t} else {\n\t\t\tjqXHR.readyState = 1;\n\n\t\t\t// Send global event\n\t\t\tif ( fireGlobals ) {\n\t\t\t\tglobalEventContext.trigger( \"ajaxSend\", [ jqXHR, s ] );\n\t\t\t}\n\n\t\t\t// If request was aborted inside ajaxSend, stop there\n\t\t\tif ( completed ) {\n\t\t\t\treturn jqXHR;\n\t\t\t}\n\n\t\t\t// Timeout\n\t\t\tif ( s.async && s.timeout > 0 ) {\n\t\t\t\ttimeoutTimer = window.setTimeout( function() {\n\t\t\t\t\tjqXHR.abort( \"timeout\" );\n\t\t\t\t}, s.timeout );\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tcompleted = false;\n\t\t\t\ttransport.send( requestHeaders, done );\n\t\t\t} catch ( e ) {\n\n\t\t\t\t// Rethrow post-completion exceptions\n\t\t\t\tif ( completed ) {\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\n\t\t\t\t// Propagate others as results\n\t\t\t\tdone( -1, e );\n\t\t\t}\n\t\t}\n\n\t\t// Callback for when everything is done\n\t\tfunction done( status, nativeStatusText, responses, headers ) {\n\t\t\tvar isSuccess, success, error, response, modified,\n\t\t\t\tstatusText = nativeStatusText;\n\n\t\t\t// Ignore repeat invocations\n\t\t\tif ( completed ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcompleted = true;\n\n\t\t\t// Clear timeout if it exists\n\t\t\tif ( timeoutTimer ) {\n\t\t\t\twindow.clearTimeout( timeoutTimer );\n\t\t\t}\n\n\t\t\t// Dereference transport for early garbage collection\n\t\t\t// (no matter how long the jqXHR object will be used)\n\t\t\ttransport = undefined;\n\n\t\t\t// Cache response headers\n\t\t\tresponseHeadersString = headers || \"\";\n\n\t\t\t// Set readyState\n\t\t\tjqXHR.readyState = status > 0 ? 4 : 0;\n\n\t\t\t// Determine if successful\n\t\t\tisSuccess = status >= 200 && status < 300 || status === 304;\n\n\t\t\t// Get response data\n\t\t\tif ( responses ) {\n\t\t\t\tresponse = ajaxHandleResponses( s, jqXHR, responses );\n\t\t\t}\n\n\t\t\t// Use a noop converter for missing script\n\t\t\tif ( !isSuccess && jQuery.inArray( \"script\", s.dataTypes ) > -1 ) {\n\t\t\t\ts.converters[ \"text script\" ] = function() {};\n\t\t\t}\n\n\t\t\t// Convert no matter what (that way responseXXX fields are always set)\n\t\t\tresponse = ajaxConvert( s, response, jqXHR, isSuccess );\n\n\t\t\t// If successful, handle type chaining\n\t\t\tif ( isSuccess ) {\n\n\t\t\t\t// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.\n\t\t\t\tif ( s.ifModified ) {\n\t\t\t\t\tmodified = jqXHR.getResponseHeader( \"Last-Modified\" );\n\t\t\t\t\tif ( modified ) {\n\t\t\t\t\t\tjQuery.lastModified[ cacheURL ] = modified;\n\t\t\t\t\t}\n\t\t\t\t\tmodified = jqXHR.getResponseHeader( \"etag\" );\n\t\t\t\t\tif ( modified ) {\n\t\t\t\t\t\tjQuery.etag[ cacheURL ] = modified;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// if no content\n\t\t\t\tif ( status === 204 || s.type === \"HEAD\" ) {\n\t\t\t\t\tstatusText = \"nocontent\";\n\n\t\t\t\t// if not modified\n\t\t\t\t} else if ( status === 304 ) {\n\t\t\t\t\tstatusText = \"notmodified\";\n\n\t\t\t\t// If we have data, let's convert it\n\t\t\t\t} else {\n\t\t\t\t\tstatusText = response.state;\n\t\t\t\t\tsuccess = response.data;\n\t\t\t\t\terror = response.error;\n\t\t\t\t\tisSuccess = !error;\n\t\t\t\t}\n\t\t\t} else {\n\n\t\t\t\t// Extract error from statusText and normalize for non-aborts\n\t\t\t\terror = statusText;\n\t\t\t\tif ( status || !statusText ) {\n\t\t\t\t\tstatusText = \"error\";\n\t\t\t\t\tif ( status < 0 ) {\n\t\t\t\t\t\tstatus = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Set data for the fake xhr object\n\t\t\tjqXHR.status = status;\n\t\t\tjqXHR.statusText = ( nativeStatusText || statusText ) + \"\";\n\n\t\t\t// Success/Error\n\t\t\tif ( isSuccess ) {\n\t\t\t\tdeferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );\n\t\t\t} else {\n\t\t\t\tdeferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );\n\t\t\t}\n\n\t\t\t// Status-dependent callbacks\n\t\t\tjqXHR.statusCode( statusCode );\n\t\t\tstatusCode = undefined;\n\n\t\t\tif ( fireGlobals ) {\n\t\t\t\tglobalEventContext.trigger( isSuccess ? \"ajaxSuccess\" : \"ajaxError\",\n\t\t\t\t\t[ jqXHR, s, isSuccess ? success : error ] );\n\t\t\t}\n\n\t\t\t// Complete\n\t\t\tcompleteDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );\n\n\t\t\tif ( fireGlobals ) {\n\t\t\t\tglobalEventContext.trigger( \"ajaxComplete\", [ jqXHR, s ] );\n\n\t\t\t\t// Handle the global AJAX counter\n\t\t\t\tif ( !( --jQuery.active ) ) {\n\t\t\t\t\tjQuery.event.trigger( \"ajaxStop\" );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn jqXHR;\n\t},\n\n\tgetJSON: function( url, data, callback ) {\n\t\treturn jQuery.get( url, data, callback, \"json\" );\n\t},\n\n\tgetScript: function( url, callback ) {\n\t\treturn jQuery.get( url, undefined, callback, \"script\" );\n\t}\n} );\n\njQuery.each( [ \"get\", \"post\" ], function( _i, method ) {\n\tjQuery[ method ] = function( url, data, callback, type ) {\n\n\t\t// Shift arguments if data argument was omitted\n\t\tif ( isFunction( data ) ) {\n\t\t\ttype = type || callback;\n\t\t\tcallback = data;\n\t\t\tdata = undefined;\n\t\t}\n\n\t\t// The url can be an options object (which then must have .url)\n\t\treturn jQuery.ajax( jQuery.extend( {\n\t\t\turl: url,\n\t\t\ttype: method,\n\t\t\tdataType: type,\n\t\t\tdata: data,\n\t\t\tsuccess: callback\n\t\t}, jQuery.isPlainObject( url ) && url ) );\n\t};\n} );\n\njQuery.ajaxPrefilter( function( s ) {\n\tvar i;\n\tfor ( i in s.headers ) {\n\t\tif ( i.toLowerCase() === \"content-type\" ) {\n\t\t\ts.contentType = s.headers[ i ] || \"\";\n\t\t}\n\t}\n} );\n\n\njQuery._evalUrl = function( url, options, doc ) {\n\treturn jQuery.ajax( {\n\t\turl: url,\n\n\t\t// Make this explicit, since user can override this through ajaxSetup (#11264)\n\t\ttype: \"GET\",\n\t\tdataType: \"script\",\n\t\tcache: true,\n\t\tasync: false,\n\t\tglobal: false,\n\n\t\t// Only evaluate the response if it is successful (gh-4126)\n\t\t// dataFilter is not invoked for failure responses, so using it instead\n\t\t// of the default converter is kludgy but it works.\n\t\tconverters: {\n\t\t\t\"text script\": function() {}\n\t\t},\n\t\tdataFilter: function( response ) {\n\t\t\tjQuery.globalEval( response, options, doc );\n\t\t}\n\t} );\n};\n\n\njQuery.fn.extend( {\n\twrapAll: function( html ) {\n\t\tvar wrap;\n\n\t\tif ( this[ 0 ] ) {\n\t\t\tif ( isFunction( html ) ) {\n\t\t\t\thtml = html.call( this[ 0 ] );\n\t\t\t}\n\n\t\t\t// The elements to wrap the target around\n\t\t\twrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );\n\n\t\t\tif ( this[ 0 ].parentNode ) {\n\t\t\t\twrap.insertBefore( this[ 0 ] );\n\t\t\t}\n\n\t\t\twrap.map( function() {\n\t\t\t\tvar elem = this;\n\n\t\t\t\twhile ( elem.firstElementChild ) {\n\t\t\t\t\telem = elem.firstElementChild;\n\t\t\t\t}\n\n\t\t\t\treturn elem;\n\t\t\t} ).append( this );\n\t\t}\n\n\t\treturn this;\n\t},\n\n\twrapInner: function( html ) {\n\t\tif ( isFunction( html ) ) {\n\t\t\treturn this.each( function( i ) {\n\t\t\t\tjQuery( this ).wrapInner( html.call( this, i ) );\n\t\t\t} );\n\t\t}\n\n\t\treturn this.each( function() {\n\t\t\tvar self = jQuery( this ),\n\t\t\t\tcontents = self.contents();\n\n\t\t\tif ( contents.length ) {\n\t\t\t\tcontents.wrapAll( html );\n\n\t\t\t} else {\n\t\t\t\tself.append( html );\n\t\t\t}\n\t\t} );\n\t},\n\n\twrap: function( html ) {\n\t\tvar htmlIsFunction = isFunction( html );\n\n\t\treturn this.each( function( i ) {\n\t\t\tjQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html );\n\t\t} );\n\t},\n\n\tunwrap: function( selector ) {\n\t\tthis.parent( selector ).not( \"body\" ).each( function() {\n\t\t\tjQuery( this ).replaceWith( this.childNodes );\n\t\t} );\n\t\treturn this;\n\t}\n} );\n\n\njQuery.expr.pseudos.hidden = function( elem ) {\n\treturn !jQuery.expr.pseudos.visible( elem );\n};\njQuery.expr.pseudos.visible = function( elem ) {\n\treturn !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );\n};\n\n\n\n\njQuery.ajaxSettings.xhr = function() {\n\ttry {\n\t\treturn new window.XMLHttpRequest();\n\t} catch ( e ) {}\n};\n\nvar xhrSuccessStatus = {\n\n\t\t// File protocol always yields status code 0, assume 200\n\t\t0: 200,\n\n\t\t// Support: IE <=9 only\n\t\t// #1450: sometimes IE returns 1223 when it should be 204\n\t\t1223: 204\n\t},\n\txhrSupported = jQuery.ajaxSettings.xhr();\n\nsupport.cors = !!xhrSupported && ( \"withCredentials\" in xhrSupported );\nsupport.ajax = xhrSupported = !!xhrSupported;\n\njQuery.ajaxTransport( function( options ) {\n\tvar callback, errorCallback;\n\n\t// Cross domain only allowed if supported through XMLHttpRequest\n\tif ( support.cors || xhrSupported && !options.crossDomain ) {\n\t\treturn {\n\t\t\tsend: function( headers, complete ) {\n\t\t\t\tvar i,\n\t\t\t\t\txhr = options.xhr();\n\n\t\t\t\txhr.open(\n\t\t\t\t\toptions.type,\n\t\t\t\t\toptions.url,\n\t\t\t\t\toptions.async,\n\t\t\t\t\toptions.username,\n\t\t\t\t\toptions.password\n\t\t\t\t);\n\n\t\t\t\t// Apply custom fields if provided\n\t\t\t\tif ( options.xhrFields ) {\n\t\t\t\t\tfor ( i in options.xhrFields ) {\n\t\t\t\t\t\txhr[ i ] = options.xhrFields[ i ];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Override mime type if needed\n\t\t\t\tif ( options.mimeType && xhr.overrideMimeType ) {\n\t\t\t\t\txhr.overrideMimeType( options.mimeType );\n\t\t\t\t}\n\n\t\t\t\t// X-Requested-With header\n\t\t\t\t// For cross-domain requests, seeing as conditions for a preflight are\n\t\t\t\t// akin to a jigsaw puzzle, we simply never set it to be sure.\n\t\t\t\t// (it can always be set on a per-request basis or even using ajaxSetup)\n\t\t\t\t// For same-domain requests, won't change header if already provided.\n\t\t\t\tif ( !options.crossDomain && !headers[ \"X-Requested-With\" ] ) {\n\t\t\t\t\theaders[ \"X-Requested-With\" ] = \"XMLHttpRequest\";\n\t\t\t\t}\n\n\t\t\t\t// Set headers\n\t\t\t\tfor ( i in headers ) {\n\t\t\t\t\txhr.setRequestHeader( i, headers[ i ] );\n\t\t\t\t}\n\n\t\t\t\t// Callback\n\t\t\t\tcallback = function( type ) {\n\t\t\t\t\treturn function() {\n\t\t\t\t\t\tif ( callback ) {\n\t\t\t\t\t\t\tcallback = errorCallback = xhr.onload =\n\t\t\t\t\t\t\t\txhr.onerror = xhr.onabort = xhr.ontimeout =\n\t\t\t\t\t\t\t\t\txhr.onreadystatechange = null;\n\n\t\t\t\t\t\t\tif ( type === \"abort\" ) {\n\t\t\t\t\t\t\t\txhr.abort();\n\t\t\t\t\t\t\t} else if ( type === \"error\" ) {\n\n\t\t\t\t\t\t\t\t// Support: IE <=9 only\n\t\t\t\t\t\t\t\t// On a manual native abort, IE9 throws\n\t\t\t\t\t\t\t\t// errors on any property access that is not readyState\n\t\t\t\t\t\t\t\tif ( typeof xhr.status !== \"number\" ) {\n\t\t\t\t\t\t\t\t\tcomplete( 0, \"error\" );\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tcomplete(\n\n\t\t\t\t\t\t\t\t\t\t// File: protocol always yields status 0; see #8605, #14207\n\t\t\t\t\t\t\t\t\t\txhr.status,\n\t\t\t\t\t\t\t\t\t\txhr.statusText\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcomplete(\n\t\t\t\t\t\t\t\t\txhrSuccessStatus[ xhr.status ] || xhr.status,\n\t\t\t\t\t\t\t\t\txhr.statusText,\n\n\t\t\t\t\t\t\t\t\t// Support: IE <=9 only\n\t\t\t\t\t\t\t\t\t// IE9 has no XHR2 but throws on binary (trac-11426)\n\t\t\t\t\t\t\t\t\t// For XHR2 non-text, let the caller handle it (gh-2498)\n\t\t\t\t\t\t\t\t\t( xhr.responseType || \"text\" ) !== \"text\" ||\n\t\t\t\t\t\t\t\t\ttypeof xhr.responseText !== \"string\" ?\n\t\t\t\t\t\t\t\t\t\t{ binary: xhr.response } :\n\t\t\t\t\t\t\t\t\t\t{ text: xhr.responseText },\n\t\t\t\t\t\t\t\t\txhr.getAllResponseHeaders()\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t};\n\n\t\t\t\t// Listen to events\n\t\t\t\txhr.onload = callback();\n\t\t\t\terrorCallback = xhr.onerror = xhr.ontimeout = callback( \"error\" );\n\n\t\t\t\t// Support: IE 9 only\n\t\t\t\t// Use onreadystatechange to replace onabort\n\t\t\t\t// to handle uncaught aborts\n\t\t\t\tif ( xhr.onabort !== undefined ) {\n\t\t\t\t\txhr.onabort = errorCallback;\n\t\t\t\t} else {\n\t\t\t\t\txhr.onreadystatechange = function() {\n\n\t\t\t\t\t\t// Check readyState before timeout as it changes\n\t\t\t\t\t\tif ( xhr.readyState === 4 ) {\n\n\t\t\t\t\t\t\t// Allow onerror to be called first,\n\t\t\t\t\t\t\t// but that will not handle a native abort\n\t\t\t\t\t\t\t// Also, save errorCallback to a variable\n\t\t\t\t\t\t\t// as xhr.onerror cannot be accessed\n\t\t\t\t\t\t\twindow.setTimeout( function() {\n\t\t\t\t\t\t\t\tif ( callback ) {\n\t\t\t\t\t\t\t\t\terrorCallback();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\t// Create the abort callback\n\t\t\t\tcallback = callback( \"abort\" );\n\n\t\t\t\ttry {\n\n\t\t\t\t\t// Do send the request (this may raise an exception)\n\t\t\t\t\txhr.send( options.hasContent && options.data || null );\n\t\t\t\t} catch ( e ) {\n\n\t\t\t\t\t// #14683: Only rethrow if this hasn't been notified as an error yet\n\t\t\t\t\tif ( callback ) {\n\t\t\t\t\t\tthrow e;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\n\t\t\tabort: function() {\n\t\t\t\tif ( callback ) {\n\t\t\t\t\tcallback();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n} );\n\n\n\n\n// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432)\njQuery.ajaxPrefilter( function( s ) {\n\tif ( s.crossDomain ) {\n\t\ts.contents.script = false;\n\t}\n} );\n\n// Install script dataType\njQuery.ajaxSetup( {\n\taccepts: {\n\t\tscript: \"text/javascript, application/javascript, \" +\n\t\t\t\"application/ecmascript, application/x-ecmascript\"\n\t},\n\tcontents: {\n\t\tscript: /\\b(?:java|ecma)script\\b/\n\t},\n\tconverters: {\n\t\t\"text script\": function( text ) {\n\t\t\tjQuery.globalEval( text );\n\t\t\treturn text;\n\t\t}\n\t}\n} );\n\n// Handle cache's special case and crossDomain\njQuery.ajaxPrefilter( \"script\", function( s ) {\n\tif ( s.cache === undefined ) {\n\t\ts.cache = false;\n\t}\n\tif ( s.crossDomain ) {\n\t\ts.type = \"GET\";\n\t}\n} );\n\n// Bind script tag hack transport\njQuery.ajaxTransport( \"script\", function( s ) {\n\n\t// This transport only deals with cross domain or forced-by-attrs requests\n\tif ( s.crossDomain || s.scriptAttrs ) {\n\t\tvar script, callback;\n\t\treturn {\n\t\t\tsend: function( _, complete ) {\n\t\t\t\tscript = jQuery( \"\n\n\n","import { render, staticRenderFns } from \"./VueTypeaheadBootstrapListItem.vue?vue&type=template&id=6230cb76&scoped=true&\"\nimport script from \"./VueTypeaheadBootstrapListItem.vue?vue&type=script&lang=js&\"\nexport * from \"./VueTypeaheadBootstrapListItem.vue?vue&type=script&lang=js&\"\nimport style0 from \"./VueTypeaheadBootstrapListItem.vue?vue&type=style&index=0&id=6230cb76&scoped=true&lang=css&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"6230cb76\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('a',{class:_vm.textClasses,attrs:{\"tabindex\":\"0\",\"href\":\"#\"},on:{\"keydown\":[function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,\"tab\",9,$event.key,\"Tab\")){ return null; }return _vm.$emit('listItemBlur')},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,\"esc\",27,$event.key,[\"Esc\",\"Escape\"])){ return null; }$event.stopPropagation();$event.preventDefault();return _vm.$emit('listItemBlur')},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,\"down\",40,$event.key,[\"Down\",\"ArrowDown\"])){ return null; }$event.preventDefault();},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,\"up\",38,$event.key,[\"Up\",\"ArrowUp\"])){ return null; }$event.preventDefault();}],\"keyup\":[function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,\"down\",40,$event.key,[\"Down\",\"ArrowDown\"])){ return null; }return _vm.$parent.selectNextListItem($event)},function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,\"up\",38,$event.key,[\"Up\",\"ArrowUp\"])){ return null; }return _vm.$parent.selectPreviousListItem($event)}],\"blur\":_vm.processFocusOut}},[_c('div',{staticClass:\"sr-only\"},[_vm._v(_vm._s(_vm.screenReaderText))]),_vm._v(\" \"),_c('div',{attrs:{\"aria-hidden\":\"true\"}},[_vm._t(\"suggestion\",[_c('span',{domProps:{\"innerHTML\":_vm._s(_vm.htmlText)}})],null,{ data: _vm.data, htmlText: _vm.htmlText })],2)])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n\n\n","import mod from \"-!../../../vue-loader/lib/index.js??vue-loader-options!./VueTypeaheadBootstrapList.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../vue-loader/lib/index.js??vue-loader-options!./VueTypeaheadBootstrapList.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./VueTypeaheadBootstrapList.vue?vue&type=template&id=e64f5270&\"\nimport script from \"./VueTypeaheadBootstrapList.vue?vue&type=script&lang=js&\"\nexport * from \"./VueTypeaheadBootstrapList.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{ref:\"suggestionList\",staticClass:\"list-group shadow\"},_vm._l((_vm.matchedItems),function(item,id){return _c('vue-typeahead-bootstrap-list-item',_vm._g({key:id,attrs:{\"active\":_vm.isListItemActive(id),\"id\":(_vm.isListItemActive(id)) ? (\"selected-option-\" + _vm.vbtUniqueId) : false,\"data\":item.data,\"html-text\":_vm.highlight(item.text),\"role\":\"option\",\"aria-selected\":(_vm.isListItemActive(id)) ? 'true' : 'false',\"screen-reader-text\":item.text,\"disabled\":_vm.isDisabledItem(item),\"background-variant\":_vm.backgroundVariant,\"background-variant-resolver\":_vm.backgroundVariantResolver,\"text-variant\":_vm.textVariant},nativeOn:{\"click\":function($event){return _vm.handleHit(item, $event)}},scopedSlots:_vm._u([{key:\"suggestion\",fn:function(ref){\nvar data = ref.data;\nvar htmlText = ref.htmlText;\nreturn (_vm.$scopedSlots.suggestion)?[_vm._t(\"suggestion\",null,null,{ data: data, htmlText: htmlText })]:undefined}}],null,true)},_vm.$listeners))}),1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../vue-loader/lib/index.js??vue-loader-options!./VueTypeaheadBootstrap.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../vue-loader/lib/index.js??vue-loader-options!./VueTypeaheadBootstrap.vue?vue&type=script&lang=js&\"","\n\n\n\n\n","import { render, staticRenderFns } from \"./VueTypeaheadBootstrap.vue?vue&type=template&id=dbe69e32&scoped=true&\"\nimport script from \"./VueTypeaheadBootstrap.vue?vue&type=script&lang=js&\"\nexport * from \"./VueTypeaheadBootstrap.vue?vue&type=script&lang=js&\"\nimport style0 from \"./VueTypeaheadBootstrap.vue?vue&type=style&index=0&id=dbe69e32&scoped=true&lang=css&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"dbe69e32\",\n null\n \n)\n\nexport default component.exports","var baseIsNative = require('./_baseIsNative'),\n getValue = require('./_getValue');\n\n/**\n * Gets the native function at `key` of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {string} key The key of the method to get.\n * @returns {*} Returns the function if it's native, else `undefined`.\n */\nfunction getNative(object, key) {\n var value = getValue(object, key);\n return baseIsNative(value) ? value : undefined;\n}\n\nmodule.exports = getNative;\n","/**\n * Checks if `value` is the\n * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)\n * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n * @example\n *\n * _.isObject({});\n * // => true\n *\n * _.isObject([1, 2, 3]);\n * // => true\n *\n * _.isObject(_.noop);\n * // => true\n *\n * _.isObject(null);\n * // => false\n */\nfunction isObject(value) {\n var type = typeof value;\n return value != null && (type == 'object' || type == 'function');\n}\n\nmodule.exports = isObject;\n","/*!\n * vue-i18n v8.24.1 \n * (c) 2021 kazuya kawaguchi\n * Released under the MIT License.\n */\n/* */\n\n/**\n * constants\n */\n\nvar numberFormatKeys = [\n 'compactDisplay',\n 'currency',\n 'currencyDisplay',\n 'currencySign',\n 'localeMatcher',\n 'notation',\n 'numberingSystem',\n 'signDisplay',\n 'style',\n 'unit',\n 'unitDisplay',\n 'useGrouping',\n 'minimumIntegerDigits',\n 'minimumFractionDigits',\n 'maximumFractionDigits',\n 'minimumSignificantDigits',\n 'maximumSignificantDigits'\n];\n\n/**\n * utilities\n */\n\nfunction warn (msg, err) {\n if (typeof console !== 'undefined') {\n console.warn('[vue-i18n] ' + msg);\n /* istanbul ignore if */\n if (err) {\n console.warn(err.stack);\n }\n }\n}\n\nfunction error (msg, err) {\n if (typeof console !== 'undefined') {\n console.error('[vue-i18n] ' + msg);\n /* istanbul ignore if */\n if (err) {\n console.error(err.stack);\n }\n }\n}\n\nvar isArray = Array.isArray;\n\nfunction isObject (obj) {\n return obj !== null && typeof obj === 'object'\n}\n\nfunction isBoolean (val) {\n return typeof val === 'boolean'\n}\n\nfunction isString (val) {\n return typeof val === 'string'\n}\n\nvar toString = Object.prototype.toString;\nvar OBJECT_STRING = '[object Object]';\nfunction isPlainObject (obj) {\n return toString.call(obj) === OBJECT_STRING\n}\n\nfunction isNull (val) {\n return val === null || val === undefined\n}\n\nfunction isFunction (val) {\n return typeof val === 'function'\n}\n\nfunction parseArgs () {\n var args = [], len = arguments.length;\n while ( len-- ) args[ len ] = arguments[ len ];\n\n var locale = null;\n var params = null;\n if (args.length === 1) {\n if (isObject(args[0]) || isArray(args[0])) {\n params = args[0];\n } else if (typeof args[0] === 'string') {\n locale = args[0];\n }\n } else if (args.length === 2) {\n if (typeof args[0] === 'string') {\n locale = args[0];\n }\n /* istanbul ignore if */\n if (isObject(args[1]) || isArray(args[1])) {\n params = args[1];\n }\n }\n\n return { locale: locale, params: params }\n}\n\nfunction looseClone (obj) {\n return JSON.parse(JSON.stringify(obj))\n}\n\nfunction remove (arr, item) {\n if (arr.length) {\n var index = arr.indexOf(item);\n if (index > -1) {\n return arr.splice(index, 1)\n }\n }\n}\n\nfunction includes (arr, item) {\n return !!~arr.indexOf(item)\n}\n\nvar hasOwnProperty = Object.prototype.hasOwnProperty;\nfunction hasOwn (obj, key) {\n return hasOwnProperty.call(obj, key)\n}\n\nfunction merge (target) {\n var arguments$1 = arguments;\n\n var output = Object(target);\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments$1[i];\n if (source !== undefined && source !== null) {\n var key = (void 0);\n for (key in source) {\n if (hasOwn(source, key)) {\n if (isObject(source[key])) {\n output[key] = merge(output[key], source[key]);\n } else {\n output[key] = source[key];\n }\n }\n }\n }\n }\n return output\n}\n\nfunction looseEqual (a, b) {\n if (a === b) { return true }\n var isObjectA = isObject(a);\n var isObjectB = isObject(b);\n if (isObjectA && isObjectB) {\n try {\n var isArrayA = isArray(a);\n var isArrayB = isArray(b);\n if (isArrayA && isArrayB) {\n return a.length === b.length && a.every(function (e, i) {\n return looseEqual(e, b[i])\n })\n } else if (!isArrayA && !isArrayB) {\n var keysA = Object.keys(a);\n var keysB = Object.keys(b);\n return keysA.length === keysB.length && keysA.every(function (key) {\n return looseEqual(a[key], b[key])\n })\n } else {\n /* istanbul ignore next */\n return false\n }\n } catch (e) {\n /* istanbul ignore next */\n return false\n }\n } else if (!isObjectA && !isObjectB) {\n return String(a) === String(b)\n } else {\n return false\n }\n}\n\n/**\n * Sanitizes html special characters from input strings. For mitigating risk of XSS attacks.\n * @param rawText The raw input from the user that should be escaped.\n */\nfunction escapeHtml(rawText) {\n return rawText\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n}\n\n/**\n * Escapes html tags and special symbols from all provided params which were returned from parseArgs().params.\n * This method performs an in-place operation on the params object.\n *\n * @param {any} params Parameters as provided from `parseArgs().params`.\n * May be either an array of strings or a string->any map.\n *\n * @returns The manipulated `params` object.\n */\nfunction escapeParams(params) {\n if(params != null) {\n Object.keys(params).forEach(function (key) {\n if(typeof(params[key]) == 'string') {\n params[key] = escapeHtml(params[key]);\n }\n });\n }\n return params\n}\n\n/* */\n\nfunction extend (Vue) {\n if (!Vue.prototype.hasOwnProperty('$i18n')) {\n // $FlowFixMe\n Object.defineProperty(Vue.prototype, '$i18n', {\n get: function get () { return this._i18n }\n });\n }\n\n Vue.prototype.$t = function (key) {\n var values = [], len = arguments.length - 1;\n while ( len-- > 0 ) values[ len ] = arguments[ len + 1 ];\n\n var i18n = this.$i18n;\n return i18n._t.apply(i18n, [ key, i18n.locale, i18n._getMessages(), this ].concat( values ))\n };\n\n Vue.prototype.$tc = function (key, choice) {\n var values = [], len = arguments.length - 2;\n while ( len-- > 0 ) values[ len ] = arguments[ len + 2 ];\n\n var i18n = this.$i18n;\n return i18n._tc.apply(i18n, [ key, i18n.locale, i18n._getMessages(), this, choice ].concat( values ))\n };\n\n Vue.prototype.$te = function (key, locale) {\n var i18n = this.$i18n;\n return i18n._te(key, i18n.locale, i18n._getMessages(), locale)\n };\n\n Vue.prototype.$d = function (value) {\n var ref;\n\n var args = [], len = arguments.length - 1;\n while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];\n return (ref = this.$i18n).d.apply(ref, [ value ].concat( args ))\n };\n\n Vue.prototype.$n = function (value) {\n var ref;\n\n var args = [], len = arguments.length - 1;\n while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];\n return (ref = this.$i18n).n.apply(ref, [ value ].concat( args ))\n };\n}\n\n/* */\n\nvar mixin = {\n beforeCreate: function beforeCreate () {\n var options = this.$options;\n options.i18n = options.i18n || (options.__i18n ? {} : null);\n\n if (options.i18n) {\n if (options.i18n instanceof VueI18n) {\n // init locale messages via custom blocks\n if (options.__i18n) {\n try {\n var localeMessages = options.i18n && options.i18n.messages ? options.i18n.messages : {};\n options.__i18n.forEach(function (resource) {\n localeMessages = merge(localeMessages, JSON.parse(resource));\n });\n Object.keys(localeMessages).forEach(function (locale) {\n options.i18n.mergeLocaleMessage(locale, localeMessages[locale]);\n });\n } catch (e) {\n if (process.env.NODE_ENV !== 'production') {\n error(\"Cannot parse locale messages via custom blocks.\", e);\n }\n }\n }\n this._i18n = options.i18n;\n this._i18nWatcher = this._i18n.watchI18nData();\n } else if (isPlainObject(options.i18n)) {\n var rootI18n = this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n\n ? this.$root.$i18n\n : null;\n // component local i18n\n if (rootI18n) {\n options.i18n.root = this.$root;\n options.i18n.formatter = rootI18n.formatter;\n options.i18n.fallbackLocale = rootI18n.fallbackLocale;\n options.i18n.formatFallbackMessages = rootI18n.formatFallbackMessages;\n options.i18n.silentTranslationWarn = rootI18n.silentTranslationWarn;\n options.i18n.silentFallbackWarn = rootI18n.silentFallbackWarn;\n options.i18n.pluralizationRules = rootI18n.pluralizationRules;\n options.i18n.preserveDirectiveContent = rootI18n.preserveDirectiveContent;\n }\n\n // init locale messages via custom blocks\n if (options.__i18n) {\n try {\n var localeMessages$1 = options.i18n && options.i18n.messages ? options.i18n.messages : {};\n options.__i18n.forEach(function (resource) {\n localeMessages$1 = merge(localeMessages$1, JSON.parse(resource));\n });\n options.i18n.messages = localeMessages$1;\n } catch (e) {\n if (process.env.NODE_ENV !== 'production') {\n warn(\"Cannot parse locale messages via custom blocks.\", e);\n }\n }\n }\n\n var ref = options.i18n;\n var sharedMessages = ref.sharedMessages;\n if (sharedMessages && isPlainObject(sharedMessages)) {\n options.i18n.messages = merge(options.i18n.messages, sharedMessages);\n }\n\n this._i18n = new VueI18n(options.i18n);\n this._i18nWatcher = this._i18n.watchI18nData();\n\n if (options.i18n.sync === undefined || !!options.i18n.sync) {\n this._localeWatcher = this.$i18n.watchLocale();\n }\n\n if (rootI18n) {\n rootI18n.onComponentInstanceCreated(this._i18n);\n }\n } else {\n if (process.env.NODE_ENV !== 'production') {\n warn(\"Cannot be interpreted 'i18n' option.\");\n }\n }\n } else if (this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n) {\n // root i18n\n this._i18n = this.$root.$i18n;\n } else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) {\n // parent i18n\n this._i18n = options.parent.$i18n;\n }\n },\n\n beforeMount: function beforeMount () {\n var options = this.$options;\n options.i18n = options.i18n || (options.__i18n ? {} : null);\n\n if (options.i18n) {\n if (options.i18n instanceof VueI18n) {\n // init locale messages via custom blocks\n this._i18n.subscribeDataChanging(this);\n this._subscribing = true;\n } else if (isPlainObject(options.i18n)) {\n this._i18n.subscribeDataChanging(this);\n this._subscribing = true;\n } else {\n if (process.env.NODE_ENV !== 'production') {\n warn(\"Cannot be interpreted 'i18n' option.\");\n }\n }\n } else if (this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n) {\n this._i18n.subscribeDataChanging(this);\n this._subscribing = true;\n } else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) {\n this._i18n.subscribeDataChanging(this);\n this._subscribing = true;\n }\n },\n\n mounted: function mounted () {\n if (this !== this.$root && this.$options.__INTLIFY_META__ && this.$el) {\n this.$el.setAttribute('data-intlify', this.$options.__INTLIFY_META__);\n }\n },\n\n beforeDestroy: function beforeDestroy () {\n if (!this._i18n) { return }\n\n var self = this;\n this.$nextTick(function () {\n if (self._subscribing) {\n self._i18n.unsubscribeDataChanging(self);\n delete self._subscribing;\n }\n\n if (self._i18nWatcher) {\n self._i18nWatcher();\n self._i18n.destroyVM();\n delete self._i18nWatcher;\n }\n\n if (self._localeWatcher) {\n self._localeWatcher();\n delete self._localeWatcher;\n }\n });\n }\n};\n\n/* */\n\nvar interpolationComponent = {\n name: 'i18n',\n functional: true,\n props: {\n tag: {\n type: [String, Boolean, Object],\n default: 'span'\n },\n path: {\n type: String,\n required: true\n },\n locale: {\n type: String\n },\n places: {\n type: [Array, Object]\n }\n },\n render: function render (h, ref) {\n var data = ref.data;\n var parent = ref.parent;\n var props = ref.props;\n var slots = ref.slots;\n\n var $i18n = parent.$i18n;\n if (!$i18n) {\n if (process.env.NODE_ENV !== 'production') {\n warn('Cannot find VueI18n instance!');\n }\n return\n }\n\n var path = props.path;\n var locale = props.locale;\n var places = props.places;\n var params = slots();\n var children = $i18n.i(\n path,\n locale,\n onlyHasDefaultPlace(params) || places\n ? useLegacyPlaces(params.default, places)\n : params\n );\n\n var tag = (!!props.tag && props.tag !== true) || props.tag === false ? props.tag : 'span';\n return tag ? h(tag, data, children) : children\n }\n};\n\nfunction onlyHasDefaultPlace (params) {\n var prop;\n for (prop in params) {\n if (prop !== 'default') { return false }\n }\n return Boolean(prop)\n}\n\nfunction useLegacyPlaces (children, places) {\n var params = places ? createParamsFromPlaces(places) : {};\n\n if (!children) { return params }\n\n // Filter empty text nodes\n children = children.filter(function (child) {\n return child.tag || child.text.trim() !== ''\n });\n\n var everyPlace = children.every(vnodeHasPlaceAttribute);\n if (process.env.NODE_ENV !== 'production' && everyPlace) {\n warn('`place` attribute is deprecated in next major version. Please switch to Vue slots.');\n }\n\n return children.reduce(\n everyPlace ? assignChildPlace : assignChildIndex,\n params\n )\n}\n\nfunction createParamsFromPlaces (places) {\n if (process.env.NODE_ENV !== 'production') {\n warn('`places` prop is deprecated in next major version. Please switch to Vue slots.');\n }\n\n return Array.isArray(places)\n ? places.reduce(assignChildIndex, {})\n : Object.assign({}, places)\n}\n\nfunction assignChildPlace (params, child) {\n if (child.data && child.data.attrs && child.data.attrs.place) {\n params[child.data.attrs.place] = child;\n }\n return params\n}\n\nfunction assignChildIndex (params, child, index) {\n params[index] = child;\n return params\n}\n\nfunction vnodeHasPlaceAttribute (vnode) {\n return Boolean(vnode.data && vnode.data.attrs && vnode.data.attrs.place)\n}\n\n/* */\n\nvar numberComponent = {\n name: 'i18n-n',\n functional: true,\n props: {\n tag: {\n type: [String, Boolean, Object],\n default: 'span'\n },\n value: {\n type: Number,\n required: true\n },\n format: {\n type: [String, Object]\n },\n locale: {\n type: String\n }\n },\n render: function render (h, ref) {\n var props = ref.props;\n var parent = ref.parent;\n var data = ref.data;\n\n var i18n = parent.$i18n;\n\n if (!i18n) {\n if (process.env.NODE_ENV !== 'production') {\n warn('Cannot find VueI18n instance!');\n }\n return null\n }\n\n var key = null;\n var options = null;\n\n if (isString(props.format)) {\n key = props.format;\n } else if (isObject(props.format)) {\n if (props.format.key) {\n key = props.format.key;\n }\n\n // Filter out number format options only\n options = Object.keys(props.format).reduce(function (acc, prop) {\n var obj;\n\n if (includes(numberFormatKeys, prop)) {\n return Object.assign({}, acc, ( obj = {}, obj[prop] = props.format[prop], obj ))\n }\n return acc\n }, null);\n }\n\n var locale = props.locale || i18n.locale;\n var parts = i18n._ntp(props.value, locale, key, options);\n\n var values = parts.map(function (part, index) {\n var obj;\n\n var slot = data.scopedSlots && data.scopedSlots[part.type];\n return slot ? slot(( obj = {}, obj[part.type] = part.value, obj.index = index, obj.parts = parts, obj )) : part.value\n });\n\n var tag = (!!props.tag && props.tag !== true) || props.tag === false ? props.tag : 'span';\n return tag\n ? h(tag, {\n attrs: data.attrs,\n 'class': data['class'],\n staticClass: data.staticClass\n }, values)\n : values\n }\n};\n\n/* */\n\nfunction bind (el, binding, vnode) {\n if (!assert(el, vnode)) { return }\n\n t(el, binding, vnode);\n}\n\nfunction update (el, binding, vnode, oldVNode) {\n if (!assert(el, vnode)) { return }\n\n var i18n = vnode.context.$i18n;\n if (localeEqual(el, vnode) &&\n (looseEqual(binding.value, binding.oldValue) &&\n looseEqual(el._localeMessage, i18n.getLocaleMessage(i18n.locale)))) { return }\n\n t(el, binding, vnode);\n}\n\nfunction unbind (el, binding, vnode, oldVNode) {\n var vm = vnode.context;\n if (!vm) {\n warn('Vue instance does not exists in VNode context');\n return\n }\n\n var i18n = vnode.context.$i18n || {};\n if (!binding.modifiers.preserve && !i18n.preserveDirectiveContent) {\n el.textContent = '';\n }\n el._vt = undefined;\n delete el['_vt'];\n el._locale = undefined;\n delete el['_locale'];\n el._localeMessage = undefined;\n delete el['_localeMessage'];\n}\n\nfunction assert (el, vnode) {\n var vm = vnode.context;\n if (!vm) {\n warn('Vue instance does not exists in VNode context');\n return false\n }\n\n if (!vm.$i18n) {\n warn('VueI18n instance does not exists in Vue instance');\n return false\n }\n\n return true\n}\n\nfunction localeEqual (el, vnode) {\n var vm = vnode.context;\n return el._locale === vm.$i18n.locale\n}\n\nfunction t (el, binding, vnode) {\n var ref$1, ref$2;\n\n var value = binding.value;\n\n var ref = parseValue(value);\n var path = ref.path;\n var locale = ref.locale;\n var args = ref.args;\n var choice = ref.choice;\n if (!path && !locale && !args) {\n warn('value type not supported');\n return\n }\n\n if (!path) {\n warn('`path` is required in v-t directive');\n return\n }\n\n var vm = vnode.context;\n if (choice != null) {\n el._vt = el.textContent = (ref$1 = vm.$i18n).tc.apply(ref$1, [ path, choice ].concat( makeParams(locale, args) ));\n } else {\n el._vt = el.textContent = (ref$2 = vm.$i18n).t.apply(ref$2, [ path ].concat( makeParams(locale, args) ));\n }\n el._locale = vm.$i18n.locale;\n el._localeMessage = vm.$i18n.getLocaleMessage(vm.$i18n.locale);\n}\n\nfunction parseValue (value) {\n var path;\n var locale;\n var args;\n var choice;\n\n if (isString(value)) {\n path = value;\n } else if (isPlainObject(value)) {\n path = value.path;\n locale = value.locale;\n args = value.args;\n choice = value.choice;\n }\n\n return { path: path, locale: locale, args: args, choice: choice }\n}\n\nfunction makeParams (locale, args) {\n var params = [];\n\n locale && params.push(locale);\n if (args && (Array.isArray(args) || isPlainObject(args))) {\n params.push(args);\n }\n\n return params\n}\n\nvar Vue;\n\nfunction install (_Vue) {\n /* istanbul ignore if */\n if (process.env.NODE_ENV !== 'production' && install.installed && _Vue === Vue) {\n warn('already installed.');\n return\n }\n install.installed = true;\n\n Vue = _Vue;\n\n var version = (Vue.version && Number(Vue.version.split('.')[0])) || -1;\n /* istanbul ignore if */\n if (process.env.NODE_ENV !== 'production' && version < 2) {\n warn((\"vue-i18n (\" + (install.version) + \") need to use Vue 2.0 or later (Vue: \" + (Vue.version) + \").\"));\n return\n }\n\n extend(Vue);\n Vue.mixin(mixin);\n Vue.directive('t', { bind: bind, update: update, unbind: unbind });\n Vue.component(interpolationComponent.name, interpolationComponent);\n Vue.component(numberComponent.name, numberComponent);\n\n // use simple mergeStrategies to prevent i18n instance lose '__proto__'\n var strats = Vue.config.optionMergeStrategies;\n strats.i18n = function (parentVal, childVal) {\n return childVal === undefined\n ? parentVal\n : childVal\n };\n}\n\n/* */\n\nvar BaseFormatter = function BaseFormatter () {\n this._caches = Object.create(null);\n};\n\nBaseFormatter.prototype.interpolate = function interpolate (message, values) {\n if (!values) {\n return [message]\n }\n var tokens = this._caches[message];\n if (!tokens) {\n tokens = parse(message);\n this._caches[message] = tokens;\n }\n return compile(tokens, values)\n};\n\n\n\nvar RE_TOKEN_LIST_VALUE = /^(?:\\d)+/;\nvar RE_TOKEN_NAMED_VALUE = /^(?:\\w)+/;\n\nfunction parse (format) {\n var tokens = [];\n var position = 0;\n\n var text = '';\n while (position < format.length) {\n var char = format[position++];\n if (char === '{') {\n if (text) {\n tokens.push({ type: 'text', value: text });\n }\n\n text = '';\n var sub = '';\n char = format[position++];\n while (char !== undefined && char !== '}') {\n sub += char;\n char = format[position++];\n }\n var isClosed = char === '}';\n\n var type = RE_TOKEN_LIST_VALUE.test(sub)\n ? 'list'\n : isClosed && RE_TOKEN_NAMED_VALUE.test(sub)\n ? 'named'\n : 'unknown';\n tokens.push({ value: sub, type: type });\n } else if (char === '%') {\n // when found rails i18n syntax, skip text capture\n if (format[(position)] !== '{') {\n text += char;\n }\n } else {\n text += char;\n }\n }\n\n text && tokens.push({ type: 'text', value: text });\n\n return tokens\n}\n\nfunction compile (tokens, values) {\n var compiled = [];\n var index = 0;\n\n var mode = Array.isArray(values)\n ? 'list'\n : isObject(values)\n ? 'named'\n : 'unknown';\n if (mode === 'unknown') { return compiled }\n\n while (index < tokens.length) {\n var token = tokens[index];\n switch (token.type) {\n case 'text':\n compiled.push(token.value);\n break\n case 'list':\n compiled.push(values[parseInt(token.value, 10)]);\n break\n case 'named':\n if (mode === 'named') {\n compiled.push((values)[token.value]);\n } else {\n if (process.env.NODE_ENV !== 'production') {\n warn((\"Type of token '\" + (token.type) + \"' and format of value '\" + mode + \"' don't match!\"));\n }\n }\n break\n case 'unknown':\n if (process.env.NODE_ENV !== 'production') {\n warn(\"Detect 'unknown' type of token!\");\n }\n break\n }\n index++;\n }\n\n return compiled\n}\n\n/* */\n\n/**\n * Path parser\n * - Inspired:\n * Vue.js Path parser\n */\n\n// actions\nvar APPEND = 0;\nvar PUSH = 1;\nvar INC_SUB_PATH_DEPTH = 2;\nvar PUSH_SUB_PATH = 3;\n\n// states\nvar BEFORE_PATH = 0;\nvar IN_PATH = 1;\nvar BEFORE_IDENT = 2;\nvar IN_IDENT = 3;\nvar IN_SUB_PATH = 4;\nvar IN_SINGLE_QUOTE = 5;\nvar IN_DOUBLE_QUOTE = 6;\nvar AFTER_PATH = 7;\nvar ERROR = 8;\n\nvar pathStateMachine = [];\n\npathStateMachine[BEFORE_PATH] = {\n 'ws': [BEFORE_PATH],\n 'ident': [IN_IDENT, APPEND],\n '[': [IN_SUB_PATH],\n 'eof': [AFTER_PATH]\n};\n\npathStateMachine[IN_PATH] = {\n 'ws': [IN_PATH],\n '.': [BEFORE_IDENT],\n '[': [IN_SUB_PATH],\n 'eof': [AFTER_PATH]\n};\n\npathStateMachine[BEFORE_IDENT] = {\n 'ws': [BEFORE_IDENT],\n 'ident': [IN_IDENT, APPEND],\n '0': [IN_IDENT, APPEND],\n 'number': [IN_IDENT, APPEND]\n};\n\npathStateMachine[IN_IDENT] = {\n 'ident': [IN_IDENT, APPEND],\n '0': [IN_IDENT, APPEND],\n 'number': [IN_IDENT, APPEND],\n 'ws': [IN_PATH, PUSH],\n '.': [BEFORE_IDENT, PUSH],\n '[': [IN_SUB_PATH, PUSH],\n 'eof': [AFTER_PATH, PUSH]\n};\n\npathStateMachine[IN_SUB_PATH] = {\n \"'\": [IN_SINGLE_QUOTE, APPEND],\n '\"': [IN_DOUBLE_QUOTE, APPEND],\n '[': [IN_SUB_PATH, INC_SUB_PATH_DEPTH],\n ']': [IN_PATH, PUSH_SUB_PATH],\n 'eof': ERROR,\n 'else': [IN_SUB_PATH, APPEND]\n};\n\npathStateMachine[IN_SINGLE_QUOTE] = {\n \"'\": [IN_SUB_PATH, APPEND],\n 'eof': ERROR,\n 'else': [IN_SINGLE_QUOTE, APPEND]\n};\n\npathStateMachine[IN_DOUBLE_QUOTE] = {\n '\"': [IN_SUB_PATH, APPEND],\n 'eof': ERROR,\n 'else': [IN_DOUBLE_QUOTE, APPEND]\n};\n\n/**\n * Check if an expression is a literal value.\n */\n\nvar literalValueRE = /^\\s?(?:true|false|-?[\\d.]+|'[^']*'|\"[^\"]*\")\\s?$/;\nfunction isLiteral (exp) {\n return literalValueRE.test(exp)\n}\n\n/**\n * Strip quotes from a string\n */\n\nfunction stripQuotes (str) {\n var a = str.charCodeAt(0);\n var b = str.charCodeAt(str.length - 1);\n return a === b && (a === 0x22 || a === 0x27)\n ? str.slice(1, -1)\n : str\n}\n\n/**\n * Determine the type of a character in a keypath.\n */\n\nfunction getPathCharType (ch) {\n if (ch === undefined || ch === null) { return 'eof' }\n\n var code = ch.charCodeAt(0);\n\n switch (code) {\n case 0x5B: // [\n case 0x5D: // ]\n case 0x2E: // .\n case 0x22: // \"\n case 0x27: // '\n return ch\n\n case 0x5F: // _\n case 0x24: // $\n case 0x2D: // -\n return 'ident'\n\n case 0x09: // Tab\n case 0x0A: // Newline\n case 0x0D: // Return\n case 0xA0: // No-break space\n case 0xFEFF: // Byte Order Mark\n case 0x2028: // Line Separator\n case 0x2029: // Paragraph Separator\n return 'ws'\n }\n\n return 'ident'\n}\n\n/**\n * Format a subPath, return its plain form if it is\n * a literal string or number. Otherwise prepend the\n * dynamic indicator (*).\n */\n\nfunction formatSubPath (path) {\n var trimmed = path.trim();\n // invalid leading 0\n if (path.charAt(0) === '0' && isNaN(path)) { return false }\n\n return isLiteral(trimmed) ? stripQuotes(trimmed) : '*' + trimmed\n}\n\n/**\n * Parse a string path into an array of segments\n */\n\nfunction parse$1 (path) {\n var keys = [];\n var index = -1;\n var mode = BEFORE_PATH;\n var subPathDepth = 0;\n var c;\n var key;\n var newChar;\n var type;\n var transition;\n var action;\n var typeMap;\n var actions = [];\n\n actions[PUSH] = function () {\n if (key !== undefined) {\n keys.push(key);\n key = undefined;\n }\n };\n\n actions[APPEND] = function () {\n if (key === undefined) {\n key = newChar;\n } else {\n key += newChar;\n }\n };\n\n actions[INC_SUB_PATH_DEPTH] = function () {\n actions[APPEND]();\n subPathDepth++;\n };\n\n actions[PUSH_SUB_PATH] = function () {\n if (subPathDepth > 0) {\n subPathDepth--;\n mode = IN_SUB_PATH;\n actions[APPEND]();\n } else {\n subPathDepth = 0;\n if (key === undefined) { return false }\n key = formatSubPath(key);\n if (key === false) {\n return false\n } else {\n actions[PUSH]();\n }\n }\n };\n\n function maybeUnescapeQuote () {\n var nextChar = path[index + 1];\n if ((mode === IN_SINGLE_QUOTE && nextChar === \"'\") ||\n (mode === IN_DOUBLE_QUOTE && nextChar === '\"')) {\n index++;\n newChar = '\\\\' + nextChar;\n actions[APPEND]();\n return true\n }\n }\n\n while (mode !== null) {\n index++;\n c = path[index];\n\n if (c === '\\\\' && maybeUnescapeQuote()) {\n continue\n }\n\n type = getPathCharType(c);\n typeMap = pathStateMachine[mode];\n transition = typeMap[type] || typeMap['else'] || ERROR;\n\n if (transition === ERROR) {\n return // parse error\n }\n\n mode = transition[0];\n action = actions[transition[1]];\n if (action) {\n newChar = transition[2];\n newChar = newChar === undefined\n ? c\n : newChar;\n if (action() === false) {\n return\n }\n }\n\n if (mode === AFTER_PATH) {\n return keys\n }\n }\n}\n\n\n\n\n\nvar I18nPath = function I18nPath () {\n this._cache = Object.create(null);\n};\n\n/**\n * External parse that check for a cache hit first\n */\nI18nPath.prototype.parsePath = function parsePath (path) {\n var hit = this._cache[path];\n if (!hit) {\n hit = parse$1(path);\n if (hit) {\n this._cache[path] = hit;\n }\n }\n return hit || []\n};\n\n/**\n * Get path value from path string\n */\nI18nPath.prototype.getPathValue = function getPathValue (obj, path) {\n if (!isObject(obj)) { return null }\n\n var paths = this.parsePath(path);\n if (paths.length === 0) {\n return null\n } else {\n var length = paths.length;\n var last = obj;\n var i = 0;\n while (i < length) {\n var value = last[paths[i]];\n if (value === undefined || value === null) {\n return null\n }\n last = value;\n i++;\n }\n\n return last\n }\n};\n\n/* */\n\n\n\nvar htmlTagMatcher = /<\\/?[\\w\\s=\"/.':;#-\\/]+>/;\nvar linkKeyMatcher = /(?:@(?:\\.[a-z]+)?:(?:[\\w\\-_|.]+|\\([\\w\\-_|.]+\\)))/g;\nvar linkKeyPrefixMatcher = /^@(?:\\.([a-z]+))?:/;\nvar bracketsMatcher = /[()]/g;\nvar defaultModifiers = {\n 'upper': function (str) { return str.toLocaleUpperCase(); },\n 'lower': function (str) { return str.toLocaleLowerCase(); },\n 'capitalize': function (str) { return (\"\" + (str.charAt(0).toLocaleUpperCase()) + (str.substr(1))); }\n};\n\nvar defaultFormatter = new BaseFormatter();\n\nvar VueI18n = function VueI18n (options) {\n var this$1 = this;\n if ( options === void 0 ) options = {};\n\n // Auto install if it is not done yet and `window` has `Vue`.\n // To allow users to avoid auto-installation in some cases,\n // this code should be placed here. See #290\n /* istanbul ignore if */\n if (!Vue && typeof window !== 'undefined' && window.Vue) {\n install(window.Vue);\n }\n\n var locale = options.locale || 'en-US';\n var fallbackLocale = options.fallbackLocale === false\n ? false\n : options.fallbackLocale || 'en-US';\n var messages = options.messages || {};\n var dateTimeFormats = options.dateTimeFormats || {};\n var numberFormats = options.numberFormats || {};\n\n this._vm = null;\n this._formatter = options.formatter || defaultFormatter;\n this._modifiers = options.modifiers || {};\n this._missing = options.missing || null;\n this._root = options.root || null;\n this._sync = options.sync === undefined ? true : !!options.sync;\n this._fallbackRoot = options.fallbackRoot === undefined\n ? true\n : !!options.fallbackRoot;\n this._formatFallbackMessages = options.formatFallbackMessages === undefined\n ? false\n : !!options.formatFallbackMessages;\n this._silentTranslationWarn = options.silentTranslationWarn === undefined\n ? false\n : options.silentTranslationWarn;\n this._silentFallbackWarn = options.silentFallbackWarn === undefined\n ? false\n : !!options.silentFallbackWarn;\n this._dateTimeFormatters = {};\n this._numberFormatters = {};\n this._path = new I18nPath();\n this._dataListeners = [];\n this._componentInstanceCreatedListener = options.componentInstanceCreatedListener || null;\n this._preserveDirectiveContent = options.preserveDirectiveContent === undefined\n ? false\n : !!options.preserveDirectiveContent;\n this.pluralizationRules = options.pluralizationRules || {};\n this._warnHtmlInMessage = options.warnHtmlInMessage || 'off';\n this._postTranslation = options.postTranslation || null;\n this._escapeParameterHtml = options.escapeParameterHtml || false;\n\n /**\n * @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)`\n * @param choicesLength {number} an overall amount of available choices\n * @returns a final choice index\n */\n this.getChoiceIndex = function (choice, choicesLength) {\n var thisPrototype = Object.getPrototypeOf(this$1);\n if (thisPrototype && thisPrototype.getChoiceIndex) {\n var prototypeGetChoiceIndex = (thisPrototype.getChoiceIndex);\n return (prototypeGetChoiceIndex).call(this$1, choice, choicesLength)\n }\n\n // Default (old) getChoiceIndex implementation - english-compatible\n var defaultImpl = function (_choice, _choicesLength) {\n _choice = Math.abs(_choice);\n\n if (_choicesLength === 2) {\n return _choice\n ? _choice > 1\n ? 1\n : 0\n : 1\n }\n\n return _choice ? Math.min(_choice, 2) : 0\n };\n\n if (this$1.locale in this$1.pluralizationRules) {\n return this$1.pluralizationRules[this$1.locale].apply(this$1, [choice, choicesLength])\n } else {\n return defaultImpl(choice, choicesLength)\n }\n };\n\n\n this._exist = function (message, key) {\n if (!message || !key) { return false }\n if (!isNull(this$1._path.getPathValue(message, key))) { return true }\n // fallback for flat key\n if (message[key]) { return true }\n return false\n };\n\n if (this._warnHtmlInMessage === 'warn' || this._warnHtmlInMessage === 'error') {\n Object.keys(messages).forEach(function (locale) {\n this$1._checkLocaleMessage(locale, this$1._warnHtmlInMessage, messages[locale]);\n });\n }\n\n this._initVM({\n locale: locale,\n fallbackLocale: fallbackLocale,\n messages: messages,\n dateTimeFormats: dateTimeFormats,\n numberFormats: numberFormats\n });\n};\n\nvar prototypeAccessors = { vm: { configurable: true },messages: { configurable: true },dateTimeFormats: { configurable: true },numberFormats: { configurable: true },availableLocales: { configurable: true },locale: { configurable: true },fallbackLocale: { configurable: true },formatFallbackMessages: { configurable: true },missing: { configurable: true },formatter: { configurable: true },silentTranslationWarn: { configurable: true },silentFallbackWarn: { configurable: true },preserveDirectiveContent: { configurable: true },warnHtmlInMessage: { configurable: true },postTranslation: { configurable: true } };\n\nVueI18n.prototype._checkLocaleMessage = function _checkLocaleMessage (locale, level, message) {\n var paths = [];\n\n var fn = function (level, locale, message, paths) {\n if (isPlainObject(message)) {\n Object.keys(message).forEach(function (key) {\n var val = message[key];\n if (isPlainObject(val)) {\n paths.push(key);\n paths.push('.');\n fn(level, locale, val, paths);\n paths.pop();\n paths.pop();\n } else {\n paths.push(key);\n fn(level, locale, val, paths);\n paths.pop();\n }\n });\n } else if (isArray(message)) {\n message.forEach(function (item, index) {\n if (isPlainObject(item)) {\n paths.push((\"[\" + index + \"]\"));\n paths.push('.');\n fn(level, locale, item, paths);\n paths.pop();\n paths.pop();\n } else {\n paths.push((\"[\" + index + \"]\"));\n fn(level, locale, item, paths);\n paths.pop();\n }\n });\n } else if (isString(message)) {\n var ret = htmlTagMatcher.test(message);\n if (ret) {\n var msg = \"Detected HTML in message '\" + message + \"' of keypath '\" + (paths.join('')) + \"' at '\" + locale + \"'. Consider component interpolation with '' to avoid XSS. See https://bit.ly/2ZqJzkp\";\n if (level === 'warn') {\n warn(msg);\n } else if (level === 'error') {\n error(msg);\n }\n }\n }\n };\n\n fn(level, locale, message, paths);\n};\n\nVueI18n.prototype._initVM = function _initVM (data) {\n var silent = Vue.config.silent;\n Vue.config.silent = true;\n this._vm = new Vue({ data: data });\n Vue.config.silent = silent;\n};\n\nVueI18n.prototype.destroyVM = function destroyVM () {\n this._vm.$destroy();\n};\n\nVueI18n.prototype.subscribeDataChanging = function subscribeDataChanging (vm) {\n this._dataListeners.push(vm);\n};\n\nVueI18n.prototype.unsubscribeDataChanging = function unsubscribeDataChanging (vm) {\n remove(this._dataListeners, vm);\n};\n\nVueI18n.prototype.watchI18nData = function watchI18nData () {\n var self = this;\n return this._vm.$watch('$data', function () {\n var i = self._dataListeners.length;\n while (i--) {\n Vue.nextTick(function () {\n self._dataListeners[i] && self._dataListeners[i].$forceUpdate();\n });\n }\n }, { deep: true })\n};\n\nVueI18n.prototype.watchLocale = function watchLocale () {\n /* istanbul ignore if */\n if (!this._sync || !this._root) { return null }\n var target = this._vm;\n return this._root.$i18n.vm.$watch('locale', function (val) {\n target.$set(target, 'locale', val);\n target.$forceUpdate();\n }, { immediate: true })\n};\n\nVueI18n.prototype.onComponentInstanceCreated = function onComponentInstanceCreated (newI18n) {\n if (this._componentInstanceCreatedListener) {\n this._componentInstanceCreatedListener(newI18n, this);\n }\n};\n\nprototypeAccessors.vm.get = function () { return this._vm };\n\nprototypeAccessors.messages.get = function () { return looseClone(this._getMessages()) };\nprototypeAccessors.dateTimeFormats.get = function () { return looseClone(this._getDateTimeFormats()) };\nprototypeAccessors.numberFormats.get = function () { return looseClone(this._getNumberFormats()) };\nprototypeAccessors.availableLocales.get = function () { return Object.keys(this.messages).sort() };\n\nprototypeAccessors.locale.get = function () { return this._vm.locale };\nprototypeAccessors.locale.set = function (locale) {\n this._vm.$set(this._vm, 'locale', locale);\n};\n\nprototypeAccessors.fallbackLocale.get = function () { return this._vm.fallbackLocale };\nprototypeAccessors.fallbackLocale.set = function (locale) {\n this._localeChainCache = {};\n this._vm.$set(this._vm, 'fallbackLocale', locale);\n};\n\nprototypeAccessors.formatFallbackMessages.get = function () { return this._formatFallbackMessages };\nprototypeAccessors.formatFallbackMessages.set = function (fallback) { this._formatFallbackMessages = fallback; };\n\nprototypeAccessors.missing.get = function () { return this._missing };\nprototypeAccessors.missing.set = function (handler) { this._missing = handler; };\n\nprototypeAccessors.formatter.get = function () { return this._formatter };\nprototypeAccessors.formatter.set = function (formatter) { this._formatter = formatter; };\n\nprototypeAccessors.silentTranslationWarn.get = function () { return this._silentTranslationWarn };\nprototypeAccessors.silentTranslationWarn.set = function (silent) { this._silentTranslationWarn = silent; };\n\nprototypeAccessors.silentFallbackWarn.get = function () { return this._silentFallbackWarn };\nprototypeAccessors.silentFallbackWarn.set = function (silent) { this._silentFallbackWarn = silent; };\n\nprototypeAccessors.preserveDirectiveContent.get = function () { return this._preserveDirectiveContent };\nprototypeAccessors.preserveDirectiveContent.set = function (preserve) { this._preserveDirectiveContent = preserve; };\n\nprototypeAccessors.warnHtmlInMessage.get = function () { return this._warnHtmlInMessage };\nprototypeAccessors.warnHtmlInMessage.set = function (level) {\n var this$1 = this;\n\n var orgLevel = this._warnHtmlInMessage;\n this._warnHtmlInMessage = level;\n if (orgLevel !== level && (level === 'warn' || level === 'error')) {\n var messages = this._getMessages();\n Object.keys(messages).forEach(function (locale) {\n this$1._checkLocaleMessage(locale, this$1._warnHtmlInMessage, messages[locale]);\n });\n }\n};\n\nprototypeAccessors.postTranslation.get = function () { return this._postTranslation };\nprototypeAccessors.postTranslation.set = function (handler) { this._postTranslation = handler; };\n\nVueI18n.prototype._getMessages = function _getMessages () { return this._vm.messages };\nVueI18n.prototype._getDateTimeFormats = function _getDateTimeFormats () { return this._vm.dateTimeFormats };\nVueI18n.prototype._getNumberFormats = function _getNumberFormats () { return this._vm.numberFormats };\n\nVueI18n.prototype._warnDefault = function _warnDefault (locale, key, result, vm, values, interpolateMode) {\n if (!isNull(result)) { return result }\n if (this._missing) {\n var missingRet = this._missing.apply(null, [locale, key, vm, values]);\n if (isString(missingRet)) {\n return missingRet\n }\n } else {\n if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key)) {\n warn(\n \"Cannot translate the value of keypath '\" + key + \"'. \" +\n 'Use the value of keypath as default.'\n );\n }\n }\n\n if (this._formatFallbackMessages) {\n var parsedArgs = parseArgs.apply(void 0, values);\n return this._render(key, interpolateMode, parsedArgs.params, key)\n } else {\n return key\n }\n};\n\nVueI18n.prototype._isFallbackRoot = function _isFallbackRoot (val) {\n return !val && !isNull(this._root) && this._fallbackRoot\n};\n\nVueI18n.prototype._isSilentFallbackWarn = function _isSilentFallbackWarn (key) {\n return this._silentFallbackWarn instanceof RegExp\n ? this._silentFallbackWarn.test(key)\n : this._silentFallbackWarn\n};\n\nVueI18n.prototype._isSilentFallback = function _isSilentFallback (locale, key) {\n return this._isSilentFallbackWarn(key) && (this._isFallbackRoot() || locale !== this.fallbackLocale)\n};\n\nVueI18n.prototype._isSilentTranslationWarn = function _isSilentTranslationWarn (key) {\n return this._silentTranslationWarn instanceof RegExp\n ? this._silentTranslationWarn.test(key)\n : this._silentTranslationWarn\n};\n\nVueI18n.prototype._interpolate = function _interpolate (\n locale,\n message,\n key,\n host,\n interpolateMode,\n values,\n visitedLinkStack\n) {\n if (!message) { return null }\n\n var pathRet = this._path.getPathValue(message, key);\n if (isArray(pathRet) || isPlainObject(pathRet)) { return pathRet }\n\n var ret;\n if (isNull(pathRet)) {\n /* istanbul ignore else */\n if (isPlainObject(message)) {\n ret = message[key];\n if (!(isString(ret) || isFunction(ret))) {\n if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallback(locale, key)) {\n warn((\"Value of key '\" + key + \"' is not a string or function !\"));\n }\n return null\n }\n } else {\n return null\n }\n } else {\n /* istanbul ignore else */\n if (isString(pathRet) || isFunction(pathRet)) {\n ret = pathRet;\n } else {\n if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallback(locale, key)) {\n warn((\"Value of key '\" + key + \"' is not a string or function!\"));\n }\n return null\n }\n }\n\n // Check for the existence of links within the translated string\n if (isString(ret) && (ret.indexOf('@:') >= 0 || ret.indexOf('@.') >= 0)) {\n ret = this._link(locale, message, ret, host, 'raw', values, visitedLinkStack);\n }\n\n return this._render(ret, interpolateMode, values, key)\n};\n\nVueI18n.prototype._link = function _link (\n locale,\n message,\n str,\n host,\n interpolateMode,\n values,\n visitedLinkStack\n) {\n var ret = str;\n\n // Match all the links within the local\n // We are going to replace each of\n // them with its translation\n var matches = ret.match(linkKeyMatcher);\n for (var idx in matches) {\n // ie compatible: filter custom array\n // prototype method\n if (!matches.hasOwnProperty(idx)) {\n continue\n }\n var link = matches[idx];\n var linkKeyPrefixMatches = link.match(linkKeyPrefixMatcher);\n var linkPrefix = linkKeyPrefixMatches[0];\n var formatterName = linkKeyPrefixMatches[1];\n\n // Remove the leading @:, @.case: and the brackets\n var linkPlaceholder = link.replace(linkPrefix, '').replace(bracketsMatcher, '');\n\n if (includes(visitedLinkStack, linkPlaceholder)) {\n if (process.env.NODE_ENV !== 'production') {\n warn((\"Circular reference found. \\\"\" + link + \"\\\" is already visited in the chain of \" + (visitedLinkStack.reverse().join(' <- '))));\n }\n return ret\n }\n visitedLinkStack.push(linkPlaceholder);\n\n // Translate the link\n var translated = this._interpolate(\n locale, message, linkPlaceholder, host,\n interpolateMode === 'raw' ? 'string' : interpolateMode,\n interpolateMode === 'raw' ? undefined : values,\n visitedLinkStack\n );\n\n if (this._isFallbackRoot(translated)) {\n if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(linkPlaceholder)) {\n warn((\"Fall back to translate the link placeholder '\" + linkPlaceholder + \"' with root locale.\"));\n }\n /* istanbul ignore if */\n if (!this._root) { throw Error('unexpected error') }\n var root = this._root.$i18n;\n translated = root._translate(\n root._getMessages(), root.locale, root.fallbackLocale,\n linkPlaceholder, host, interpolateMode, values\n );\n }\n translated = this._warnDefault(\n locale, linkPlaceholder, translated, host,\n isArray(values) ? values : [values],\n interpolateMode\n );\n\n if (this._modifiers.hasOwnProperty(formatterName)) {\n translated = this._modifiers[formatterName](translated);\n } else if (defaultModifiers.hasOwnProperty(formatterName)) {\n translated = defaultModifiers[formatterName](translated);\n }\n\n visitedLinkStack.pop();\n\n // Replace the link with the translated\n ret = !translated ? ret : ret.replace(link, translated);\n }\n\n return ret\n};\n\nVueI18n.prototype._createMessageContext = function _createMessageContext (values) {\n var _list = isArray(values) ? values : [];\n var _named = isObject(values) ? values : {};\n var list = function (index) { return _list[index]; };\n var named = function (key) { return _named[key]; };\n return {\n list: list,\n named: named\n }\n};\n\nVueI18n.prototype._render = function _render (message, interpolateMode, values, path) {\n if (isFunction(message)) {\n return message(this._createMessageContext(values))\n }\n\n var ret = this._formatter.interpolate(message, values, path);\n\n // If the custom formatter refuses to work - apply the default one\n if (!ret) {\n ret = defaultFormatter.interpolate(message, values, path);\n }\n\n // if interpolateMode is **not** 'string' ('row'),\n // return the compiled data (e.g. ['foo', VNode, 'bar']) with formatter\n return interpolateMode === 'string' && !isString(ret) ? ret.join('') : ret\n};\n\nVueI18n.prototype._appendItemToChain = function _appendItemToChain (chain, item, blocks) {\n var follow = false;\n if (!includes(chain, item)) {\n follow = true;\n if (item) {\n follow = item[item.length - 1] !== '!';\n item = item.replace(/!/g, '');\n chain.push(item);\n if (blocks && blocks[item]) {\n follow = blocks[item];\n }\n }\n }\n return follow\n};\n\nVueI18n.prototype._appendLocaleToChain = function _appendLocaleToChain (chain, locale, blocks) {\n var follow;\n var tokens = locale.split('-');\n do {\n var item = tokens.join('-');\n follow = this._appendItemToChain(chain, item, blocks);\n tokens.splice(-1, 1);\n } while (tokens.length && (follow === true))\n return follow\n};\n\nVueI18n.prototype._appendBlockToChain = function _appendBlockToChain (chain, block, blocks) {\n var follow = true;\n for (var i = 0; (i < block.length) && (isBoolean(follow)); i++) {\n var locale = block[i];\n if (isString(locale)) {\n follow = this._appendLocaleToChain(chain, locale, blocks);\n }\n }\n return follow\n};\n\nVueI18n.prototype._getLocaleChain = function _getLocaleChain (start, fallbackLocale) {\n if (start === '') { return [] }\n\n if (!this._localeChainCache) {\n this._localeChainCache = {};\n }\n\n var chain = this._localeChainCache[start];\n if (!chain) {\n if (!fallbackLocale) {\n fallbackLocale = this.fallbackLocale;\n }\n chain = [];\n\n // first block defined by start\n var block = [start];\n\n // while any intervening block found\n while (isArray(block)) {\n block = this._appendBlockToChain(\n chain,\n block,\n fallbackLocale\n );\n }\n\n // last block defined by default\n var defaults;\n if (isArray(fallbackLocale)) {\n defaults = fallbackLocale;\n } else if (isObject(fallbackLocale)) {\n /* $FlowFixMe */\n if (fallbackLocale['default']) {\n defaults = fallbackLocale['default'];\n } else {\n defaults = null;\n }\n } else {\n defaults = fallbackLocale;\n }\n\n // convert defaults to array\n if (isString(defaults)) {\n block = [defaults];\n } else {\n block = defaults;\n }\n if (block) {\n this._appendBlockToChain(\n chain,\n block,\n null\n );\n }\n this._localeChainCache[start] = chain;\n }\n return chain\n};\n\nVueI18n.prototype._translate = function _translate (\n messages,\n locale,\n fallback,\n key,\n host,\n interpolateMode,\n args\n) {\n var chain = this._getLocaleChain(locale, fallback);\n var res;\n for (var i = 0; i < chain.length; i++) {\n var step = chain[i];\n res =\n this._interpolate(step, messages[step], key, host, interpolateMode, args, [key]);\n if (!isNull(res)) {\n if (step !== locale && process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {\n warn((\"Fall back to translate the keypath '\" + key + \"' with '\" + step + \"' locale.\"));\n }\n return res\n }\n }\n return null\n};\n\nVueI18n.prototype._t = function _t (key, _locale, messages, host) {\n var ref;\n\n var values = [], len = arguments.length - 4;\n while ( len-- > 0 ) values[ len ] = arguments[ len + 4 ];\n if (!key) { return '' }\n\n var parsedArgs = parseArgs.apply(void 0, values);\n if(this._escapeParameterHtml) {\n parsedArgs.params = escapeParams(parsedArgs.params);\n }\n\n var locale = parsedArgs.locale || _locale;\n\n var ret = this._translate(\n messages, locale, this.fallbackLocale, key,\n host, 'string', parsedArgs.params\n );\n if (this._isFallbackRoot(ret)) {\n if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {\n warn((\"Fall back to translate the keypath '\" + key + \"' with root locale.\"));\n }\n /* istanbul ignore if */\n if (!this._root) { throw Error('unexpected error') }\n return (ref = this._root).$t.apply(ref, [ key ].concat( values ))\n } else {\n ret = this._warnDefault(locale, key, ret, host, values, 'string');\n if (this._postTranslation && ret !== null && ret !== undefined) {\n ret = this._postTranslation(ret, key);\n }\n return ret\n }\n};\n\nVueI18n.prototype.t = function t (key) {\n var ref;\n\n var values = [], len = arguments.length - 1;\n while ( len-- > 0 ) values[ len ] = arguments[ len + 1 ];\n return (ref = this)._t.apply(ref, [ key, this.locale, this._getMessages(), null ].concat( values ))\n};\n\nVueI18n.prototype._i = function _i (key, locale, messages, host, values) {\n var ret =\n this._translate(messages, locale, this.fallbackLocale, key, host, 'raw', values);\n if (this._isFallbackRoot(ret)) {\n if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key)) {\n warn((\"Fall back to interpolate the keypath '\" + key + \"' with root locale.\"));\n }\n if (!this._root) { throw Error('unexpected error') }\n return this._root.$i18n.i(key, locale, values)\n } else {\n return this._warnDefault(locale, key, ret, host, [values], 'raw')\n }\n};\n\nVueI18n.prototype.i = function i (key, locale, values) {\n /* istanbul ignore if */\n if (!key) { return '' }\n\n if (!isString(locale)) {\n locale = this.locale;\n }\n\n return this._i(key, locale, this._getMessages(), null, values)\n};\n\nVueI18n.prototype._tc = function _tc (\n key,\n _locale,\n messages,\n host,\n choice\n) {\n var ref;\n\n var values = [], len = arguments.length - 5;\n while ( len-- > 0 ) values[ len ] = arguments[ len + 5 ];\n if (!key) { return '' }\n if (choice === undefined) {\n choice = 1;\n }\n\n var predefined = { 'count': choice, 'n': choice };\n var parsedArgs = parseArgs.apply(void 0, values);\n parsedArgs.params = Object.assign(predefined, parsedArgs.params);\n values = parsedArgs.locale === null ? [parsedArgs.params] : [parsedArgs.locale, parsedArgs.params];\n return this.fetchChoice((ref = this)._t.apply(ref, [ key, _locale, messages, host ].concat( values )), choice)\n};\n\nVueI18n.prototype.fetchChoice = function fetchChoice (message, choice) {\n /* istanbul ignore if */\n if (!message || !isString(message)) { return null }\n var choices = message.split('|');\n\n choice = this.getChoiceIndex(choice, choices.length);\n if (!choices[choice]) { return message }\n return choices[choice].trim()\n};\n\nVueI18n.prototype.tc = function tc (key, choice) {\n var ref;\n\n var values = [], len = arguments.length - 2;\n while ( len-- > 0 ) values[ len ] = arguments[ len + 2 ];\n return (ref = this)._tc.apply(ref, [ key, this.locale, this._getMessages(), null, choice ].concat( values ))\n};\n\nVueI18n.prototype._te = function _te (key, locale, messages) {\n var args = [], len = arguments.length - 3;\n while ( len-- > 0 ) args[ len ] = arguments[ len + 3 ];\n\n var _locale = parseArgs.apply(void 0, args).locale || locale;\n return this._exist(messages[_locale], key)\n};\n\nVueI18n.prototype.te = function te (key, locale) {\n return this._te(key, this.locale, this._getMessages(), locale)\n};\n\nVueI18n.prototype.getLocaleMessage = function getLocaleMessage (locale) {\n return looseClone(this._vm.messages[locale] || {})\n};\n\nVueI18n.prototype.setLocaleMessage = function setLocaleMessage (locale, message) {\n if (this._warnHtmlInMessage === 'warn' || this._warnHtmlInMessage === 'error') {\n this._checkLocaleMessage(locale, this._warnHtmlInMessage, message);\n }\n this._vm.$set(this._vm.messages, locale, message);\n};\n\nVueI18n.prototype.mergeLocaleMessage = function mergeLocaleMessage (locale, message) {\n if (this._warnHtmlInMessage === 'warn' || this._warnHtmlInMessage === 'error') {\n this._checkLocaleMessage(locale, this._warnHtmlInMessage, message);\n }\n this._vm.$set(this._vm.messages, locale, merge(\n typeof this._vm.messages[locale] !== 'undefined' && Object.keys(this._vm.messages[locale]).length\n ? this._vm.messages[locale]\n : {},\n message\n ));\n};\n\nVueI18n.prototype.getDateTimeFormat = function getDateTimeFormat (locale) {\n return looseClone(this._vm.dateTimeFormats[locale] || {})\n};\n\nVueI18n.prototype.setDateTimeFormat = function setDateTimeFormat (locale, format) {\n this._vm.$set(this._vm.dateTimeFormats, locale, format);\n this._clearDateTimeFormat(locale, format);\n};\n\nVueI18n.prototype.mergeDateTimeFormat = function mergeDateTimeFormat (locale, format) {\n this._vm.$set(this._vm.dateTimeFormats, locale, merge(this._vm.dateTimeFormats[locale] || {}, format));\n this._clearDateTimeFormat(locale, format);\n};\n\nVueI18n.prototype._clearDateTimeFormat = function _clearDateTimeFormat (locale, format) {\n for (var key in format) {\n var id = locale + \"__\" + key;\n\n if (!this._dateTimeFormatters.hasOwnProperty(id)) {\n continue\n }\n\n delete this._dateTimeFormatters[id];\n }\n};\n\nVueI18n.prototype._localizeDateTime = function _localizeDateTime (\n value,\n locale,\n fallback,\n dateTimeFormats,\n key\n) {\n var _locale = locale;\n var formats = dateTimeFormats[_locale];\n\n var chain = this._getLocaleChain(locale, fallback);\n for (var i = 0; i < chain.length; i++) {\n var current = _locale;\n var step = chain[i];\n formats = dateTimeFormats[step];\n _locale = step;\n // fallback locale\n if (isNull(formats) || isNull(formats[key])) {\n if (step !== locale && process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {\n warn((\"Fall back to '\" + step + \"' datetime formats from '\" + current + \"' datetime formats.\"));\n }\n } else {\n break\n }\n }\n\n if (isNull(formats) || isNull(formats[key])) {\n return null\n } else {\n var format = formats[key];\n var id = _locale + \"__\" + key;\n var formatter = this._dateTimeFormatters[id];\n if (!formatter) {\n formatter = this._dateTimeFormatters[id] = new Intl.DateTimeFormat(_locale, format);\n }\n return formatter.format(value)\n }\n};\n\nVueI18n.prototype._d = function _d (value, locale, key) {\n /* istanbul ignore if */\n if (process.env.NODE_ENV !== 'production' && !VueI18n.availabilities.dateTimeFormat) {\n warn('Cannot format a Date value due to not supported Intl.DateTimeFormat.');\n return ''\n }\n\n if (!key) {\n return new Intl.DateTimeFormat(locale).format(value)\n }\n\n var ret =\n this._localizeDateTime(value, locale, this.fallbackLocale, this._getDateTimeFormats(), key);\n if (this._isFallbackRoot(ret)) {\n if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {\n warn((\"Fall back to datetime localization of root: key '\" + key + \"'.\"));\n }\n /* istanbul ignore if */\n if (!this._root) { throw Error('unexpected error') }\n return this._root.$i18n.d(value, key, locale)\n } else {\n return ret || ''\n }\n};\n\nVueI18n.prototype.d = function d (value) {\n var args = [], len = arguments.length - 1;\n while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];\n\n var locale = this.locale;\n var key = null;\n\n if (args.length === 1) {\n if (isString(args[0])) {\n key = args[0];\n } else if (isObject(args[0])) {\n if (args[0].locale) {\n locale = args[0].locale;\n }\n if (args[0].key) {\n key = args[0].key;\n }\n }\n } else if (args.length === 2) {\n if (isString(args[0])) {\n key = args[0];\n }\n if (isString(args[1])) {\n locale = args[1];\n }\n }\n\n return this._d(value, locale, key)\n};\n\nVueI18n.prototype.getNumberFormat = function getNumberFormat (locale) {\n return looseClone(this._vm.numberFormats[locale] || {})\n};\n\nVueI18n.prototype.setNumberFormat = function setNumberFormat (locale, format) {\n this._vm.$set(this._vm.numberFormats, locale, format);\n this._clearNumberFormat(locale, format);\n};\n\nVueI18n.prototype.mergeNumberFormat = function mergeNumberFormat (locale, format) {\n this._vm.$set(this._vm.numberFormats, locale, merge(this._vm.numberFormats[locale] || {}, format));\n this._clearNumberFormat(locale, format);\n};\n\nVueI18n.prototype._clearNumberFormat = function _clearNumberFormat (locale, format) {\n for (var key in format) {\n var id = locale + \"__\" + key;\n\n if (!this._numberFormatters.hasOwnProperty(id)) {\n continue\n }\n\n delete this._numberFormatters[id];\n }\n};\n\nVueI18n.prototype._getNumberFormatter = function _getNumberFormatter (\n value,\n locale,\n fallback,\n numberFormats,\n key,\n options\n) {\n var _locale = locale;\n var formats = numberFormats[_locale];\n\n var chain = this._getLocaleChain(locale, fallback);\n for (var i = 0; i < chain.length; i++) {\n var current = _locale;\n var step = chain[i];\n formats = numberFormats[step];\n _locale = step;\n // fallback locale\n if (isNull(formats) || isNull(formats[key])) {\n if (step !== locale && process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {\n warn((\"Fall back to '\" + step + \"' number formats from '\" + current + \"' number formats.\"));\n }\n } else {\n break\n }\n }\n\n if (isNull(formats) || isNull(formats[key])) {\n return null\n } else {\n var format = formats[key];\n\n var formatter;\n if (options) {\n // If options specified - create one time number formatter\n formatter = new Intl.NumberFormat(_locale, Object.assign({}, format, options));\n } else {\n var id = _locale + \"__\" + key;\n formatter = this._numberFormatters[id];\n if (!formatter) {\n formatter = this._numberFormatters[id] = new Intl.NumberFormat(_locale, format);\n }\n }\n return formatter\n }\n};\n\nVueI18n.prototype._n = function _n (value, locale, key, options) {\n /* istanbul ignore if */\n if (!VueI18n.availabilities.numberFormat) {\n if (process.env.NODE_ENV !== 'production') {\n warn('Cannot format a Number value due to not supported Intl.NumberFormat.');\n }\n return ''\n }\n\n if (!key) {\n var nf = !options ? new Intl.NumberFormat(locale) : new Intl.NumberFormat(locale, options);\n return nf.format(value)\n }\n\n var formatter = this._getNumberFormatter(value, locale, this.fallbackLocale, this._getNumberFormats(), key, options);\n var ret = formatter && formatter.format(value);\n if (this._isFallbackRoot(ret)) {\n if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) {\n warn((\"Fall back to number localization of root: key '\" + key + \"'.\"));\n }\n /* istanbul ignore if */\n if (!this._root) { throw Error('unexpected error') }\n return this._root.$i18n.n(value, Object.assign({}, { key: key, locale: locale }, options))\n } else {\n return ret || ''\n }\n};\n\nVueI18n.prototype.n = function n (value) {\n var args = [], len = arguments.length - 1;\n while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];\n\n var locale = this.locale;\n var key = null;\n var options = null;\n\n if (args.length === 1) {\n if (isString(args[0])) {\n key = args[0];\n } else if (isObject(args[0])) {\n if (args[0].locale) {\n locale = args[0].locale;\n }\n if (args[0].key) {\n key = args[0].key;\n }\n\n // Filter out number format options only\n options = Object.keys(args[0]).reduce(function (acc, key) {\n var obj;\n\n if (includes(numberFormatKeys, key)) {\n return Object.assign({}, acc, ( obj = {}, obj[key] = args[0][key], obj ))\n }\n return acc\n }, null);\n }\n } else if (args.length === 2) {\n if (isString(args[0])) {\n key = args[0];\n }\n if (isString(args[1])) {\n locale = args[1];\n }\n }\n\n return this._n(value, locale, key, options)\n};\n\nVueI18n.prototype._ntp = function _ntp (value, locale, key, options) {\n /* istanbul ignore if */\n if (!VueI18n.availabilities.numberFormat) {\n if (process.env.NODE_ENV !== 'production') {\n warn('Cannot format to parts a Number value due to not supported Intl.NumberFormat.');\n }\n return []\n }\n\n if (!key) {\n var nf = !options ? new Intl.NumberFormat(locale) : new Intl.NumberFormat(locale, options);\n return nf.formatToParts(value)\n }\n\n var formatter = this._getNumberFormatter(value, locale, this.fallbackLocale, this._getNumberFormats(), key, options);\n var ret = formatter && formatter.formatToParts(value);\n if (this._isFallbackRoot(ret)) {\n if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key)) {\n warn((\"Fall back to format number to parts of root: key '\" + key + \"' .\"));\n }\n /* istanbul ignore if */\n if (!this._root) { throw Error('unexpected error') }\n return this._root.$i18n._ntp(value, locale, key, options)\n } else {\n return ret || []\n }\n};\n\nObject.defineProperties( VueI18n.prototype, prototypeAccessors );\n\nvar availabilities;\n// $FlowFixMe\nObject.defineProperty(VueI18n, 'availabilities', {\n get: function get () {\n if (!availabilities) {\n var intlDefined = typeof Intl !== 'undefined';\n availabilities = {\n dateTimeFormat: intlDefined && typeof Intl.DateTimeFormat !== 'undefined',\n numberFormat: intlDefined && typeof Intl.NumberFormat !== 'undefined'\n };\n }\n\n return availabilities\n }\n});\n\nVueI18n.install = install;\nVueI18n.version = '8.24.1';\n\nexport default VueI18n;\n","import Vue from 'vue';\n\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill\nfunction assign (target, varArgs) {\n var arguments$1 = arguments;\n\n if (target === null || target === undefined) {\n throw new TypeError('Cannot convert undefined or null to object')\n }\n var to = Object(target);\n for (var index = 1; index < arguments.length; index++) {\n var nextSource = arguments$1[index];\n if (nextSource !== null && nextSource !== undefined) {\n for (var nextKey in nextSource) {\n // Avoid bugs when hasOwnProperty is shadowed\n /* istanbul ignore else */\n if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {\n to[nextKey] = nextSource[nextKey];\n }\n }\n }\n }\n return to\n}\n\nfunction isExist (obj) {\n return typeof obj !== 'undefined' && obj !== null\n}\n\nfunction isFunction (obj) {\n return typeof obj === 'function'\n}\n\nfunction isNumber (obj) {\n return typeof obj === 'number'\n}\n\nfunction isString (obj) {\n return typeof obj === 'string'\n}\n\nfunction isBoolean (obj) {\n return typeof obj === 'boolean'\n}\n\nfunction isPromiseSupported () {\n return typeof window !== 'undefined' && isExist(window.Promise)\n}\n\nfunction hasOwnProperty (o, k) {\n return Object.prototype.hasOwnProperty.call(o, k)\n}\n\nvar script = {\n props: {\n value: Number,\n indicators: {\n type: Boolean,\n default: true\n },\n controls: {\n type: Boolean,\n default: true\n },\n interval: {\n type: Number,\n default: 5000\n },\n iconControlLeft: {\n type: String,\n default: 'glyphicon glyphicon-chevron-left'\n },\n iconControlRight: {\n type: String,\n default: 'glyphicon glyphicon-chevron-right'\n }\n },\n data: function data () {\n return {\n slides: [],\n activeIndex: 0, // Make v-model not required\n timeoutId: 0,\n intervalId: 0\n }\n },\n watch: {\n interval: function interval () {\n this.startInterval();\n },\n value: function value (index, oldValue) {\n this.run(index, oldValue);\n this.activeIndex = index;\n }\n },\n mounted: function mounted () {\n if (isExist(this.value)) {\n this.activeIndex = this.value;\n }\n if (this.slides.length > 0) {\n this.$select(this.activeIndex);\n }\n this.startInterval();\n },\n beforeDestroy: function beforeDestroy () {\n this.stopInterval();\n },\n methods: {\n run: function run (newIndex, oldIndex) {\n var this$1 = this;\n\n var currentActiveIndex = oldIndex || 0;\n var direction;\n if (newIndex > currentActiveIndex) {\n direction = ['next', 'left'];\n } else {\n direction = ['prev', 'right'];\n }\n this.slides[newIndex].slideClass[direction[0]] = true;\n this.$nextTick(function () {\n this$1.slides[newIndex].$el.offsetHeight;\n this$1.slides.forEach(function (slide, i) {\n if (i === currentActiveIndex) {\n slide.slideClass.active = true;\n slide.slideClass[direction[1]] = true;\n } else if (i === newIndex) {\n slide.slideClass[direction[1]] = true;\n }\n });\n this$1.timeoutId = setTimeout(function () {\n this$1.$select(newIndex);\n this$1.$emit('change', newIndex);\n this$1.timeoutId = 0;\n }, 600);\n });\n },\n startInterval: function startInterval () {\n var this$1 = this;\n\n this.stopInterval();\n if (this.interval > 0) {\n this.intervalId = setInterval(function () {\n this$1.next();\n }, this.interval);\n }\n },\n stopInterval: function stopInterval () {\n clearInterval(this.intervalId);\n this.intervalId = 0;\n },\n resetAllSlideClass: function resetAllSlideClass () {\n this.slides.forEach(function (slide) {\n slide.slideClass.active = false;\n slide.slideClass.left = false;\n slide.slideClass.right = false;\n slide.slideClass.next = false;\n slide.slideClass.prev = false;\n });\n },\n $select: function $select (index) {\n this.resetAllSlideClass();\n this.slides[index].slideClass.active = true;\n },\n select: function select (index) {\n if (this.timeoutId !== 0 || index === this.activeIndex) {\n return\n }\n if (isExist(this.value)) {\n this.$emit('input', index);\n } else {\n this.run(index, this.activeIndex);\n this.activeIndex = index;\n }\n },\n prev: function prev () {\n this.select(this.activeIndex === 0 ? this.slides.length - 1 : this.activeIndex - 1);\n },\n next: function next () {\n this.select(this.activeIndex === this.slides.length - 1 ? 0 : this.activeIndex + 1);\n }\n }\n};\n\nfunction normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier /* server only */, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) {\r\n if (typeof shadowMode !== 'boolean') {\r\n createInjectorSSR = createInjector;\r\n createInjector = shadowMode;\r\n shadowMode = false;\r\n }\r\n // Vue.extend constructor export interop.\r\n var options = typeof script === 'function' ? script.options : script;\r\n // render functions\r\n if (template && template.render) {\r\n options.render = template.render;\r\n options.staticRenderFns = template.staticRenderFns;\r\n options._compiled = true;\r\n // functional template\r\n if (isFunctionalTemplate) {\r\n options.functional = true;\r\n }\r\n }\r\n // scopedId\r\n if (scopeId) {\r\n options._scopeId = scopeId;\r\n }\r\n var hook;\r\n if (moduleIdentifier) {\r\n // server build\r\n hook = function (context) {\r\n // 2.3 injection\r\n context =\r\n context || // cached call\r\n (this.$vnode && this.$vnode.ssrContext) || // stateful\r\n (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext); // functional\r\n // 2.2 with runInNewContext: true\r\n if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {\r\n context = __VUE_SSR_CONTEXT__;\r\n }\r\n // inject component styles\r\n if (style) {\r\n style.call(this, createInjectorSSR(context));\r\n }\r\n // register component module identifier for async chunk inference\r\n if (context && context._registeredComponents) {\r\n context._registeredComponents.add(moduleIdentifier);\r\n }\r\n };\r\n // used by ssr in case component is cached and beforeCreate\r\n // never gets called\r\n options._ssrRegister = hook;\r\n }\r\n else if (style) {\r\n hook = shadowMode\r\n ? function (context) {\r\n style.call(this, createInjectorShadow(context, this.$root.$options.shadowRoot));\r\n }\r\n : function (context) {\r\n style.call(this, createInjector(context));\r\n };\r\n }\r\n if (hook) {\r\n if (options.functional) {\r\n // register for functional component in vue file\r\n var originalRender = options.render;\r\n options.render = function renderWithStyleInjection(h, context) {\r\n hook.call(context);\r\n return originalRender(h, context);\r\n };\r\n }\r\n else {\r\n // inject component registration as beforeCreate hook\r\n var existing = options.beforeCreate;\r\n options.beforeCreate = existing ? [].concat(existing, hook) : [hook];\r\n }\r\n }\r\n return script;\r\n}\n\n/* script */\nvar __vue_script__ = script;\n\n/* template */\nvar __vue_render__ = function() {\n var _vm = this;\n var _h = _vm.$createElement;\n var _c = _vm._self._c || _h;\n return _c(\n \"div\",\n {\n staticClass: \"carousel slide\",\n attrs: { \"data-ride\": \"carousel\" },\n on: { mouseenter: _vm.stopInterval, mouseleave: _vm.startInterval }\n },\n [\n _vm.indicators\n ? _vm._t(\n \"indicators\",\n [\n _c(\n \"ol\",\n { staticClass: \"carousel-indicators\" },\n _vm._l(_vm.slides, function(slide, index) {\n return _c(\"li\", {\n class: { active: index === _vm.activeIndex },\n on: {\n click: function($event) {\n return _vm.select(index)\n }\n }\n })\n }),\n 0\n )\n ],\n { select: _vm.select, activeIndex: _vm.activeIndex }\n )\n : _vm._e(),\n _vm._v(\" \"),\n _c(\n \"div\",\n { staticClass: \"carousel-inner\", attrs: { role: \"listbox\" } },\n [_vm._t(\"default\")],\n 2\n ),\n _vm._v(\" \"),\n _vm.controls\n ? _c(\n \"a\",\n {\n staticClass: \"left carousel-control\",\n attrs: { href: \"#\", role: \"button\" },\n on: {\n click: function($event) {\n $event.preventDefault();\n return _vm.prev()\n }\n }\n },\n [\n _c(\"span\", {\n class: _vm.iconControlLeft,\n attrs: { \"aria-hidden\": \"true\" }\n }),\n _vm._v(\" \"),\n _c(\"span\", { staticClass: \"sr-only\" }, [_vm._v(\"Previous\")])\n ]\n )\n : _vm._e(),\n _vm._v(\" \"),\n _vm.controls\n ? _c(\n \"a\",\n {\n staticClass: \"right carousel-control\",\n attrs: { href: \"#\", role: \"button\" },\n on: {\n click: function($event) {\n $event.preventDefault();\n return _vm.next()\n }\n }\n },\n [\n _c(\"span\", {\n class: _vm.iconControlRight,\n attrs: { \"aria-hidden\": \"true\" }\n }),\n _vm._v(\" \"),\n _c(\"span\", { staticClass: \"sr-only\" }, [_vm._v(\"Next\")])\n ]\n )\n : _vm._e()\n ],\n 2\n )\n};\nvar __vue_staticRenderFns__ = [];\n__vue_render__._withStripped = true;\n\n /* style */\n var __vue_inject_styles__ = undefined;\n /* scoped */\n var __vue_scope_id__ = undefined;\n /* module identifier */\n var __vue_module_identifier__ = undefined;\n /* functional template */\n var __vue_is_functional_template__ = false;\n /* style inject */\n \n /* style inject SSR */\n \n /* style inject shadow dom */\n \n\n \n var __vue_component__ = /*#__PURE__*/normalizeComponent(\n { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ },\n __vue_inject_styles__,\n __vue_script__,\n __vue_scope_id__,\n __vue_is_functional_template__,\n __vue_module_identifier__,\n false,\n undefined,\n undefined,\n undefined\n );\n\nfunction spliceIfExist (arr, item) {\n if (Array.isArray(arr)) {\n var index = arr.indexOf(item);\n if (index >= 0) {\n arr.splice(index, 1);\n }\n }\n}\n\nfunction range (end, start, step) {\n if ( start === void 0 ) start = 0;\n if ( step === void 0 ) step = 1;\n\n var arr = [];\n for (var i = start; i < end; i += step) {\n arr.push(i);\n }\n return arr\n}\n\nfunction nodeListToArray (nodeList) {\n return Array.prototype.slice.call(nodeList || [])\n}\n\nfunction onlyUnique (value, index, self) {\n return self.indexOf(value) === index\n}\n\nvar script$1 = {\n data: function data () {\n return {\n slideClass: {\n active: false,\n prev: false,\n next: false,\n left: false,\n right: false\n }\n }\n },\n created: function created () {\n try {\n this.$parent.slides.push(this);\n } catch (e) {\n throw new Error('Slide parent must be Carousel.')\n }\n },\n beforeDestroy: function beforeDestroy () {\n var slides = this.$parent && this.$parent.slides;\n spliceIfExist(slides, this);\n }\n};\n\n/* script */\nvar __vue_script__$1 = script$1;\n\n/* template */\nvar __vue_render__$1 = function() {\n var _vm = this;\n var _h = _vm.$createElement;\n var _c = _vm._self._c || _h;\n return _c(\n \"div\",\n { staticClass: \"item\", class: _vm.slideClass },\n [_vm._t(\"default\")],\n 2\n )\n};\nvar __vue_staticRenderFns__$1 = [];\n__vue_render__$1._withStripped = true;\n\n /* style */\n var __vue_inject_styles__$1 = undefined;\n /* scoped */\n var __vue_scope_id__$1 = undefined;\n /* module identifier */\n var __vue_module_identifier__$1 = undefined;\n /* functional template */\n var __vue_is_functional_template__$1 = false;\n /* style inject */\n \n /* style inject SSR */\n \n /* style inject shadow dom */\n \n\n \n var __vue_component__$1 = /*#__PURE__*/normalizeComponent(\n { render: __vue_render__$1, staticRenderFns: __vue_staticRenderFns__$1 },\n __vue_inject_styles__$1,\n __vue_script__$1,\n __vue_scope_id__$1,\n __vue_is_functional_template__$1,\n __vue_module_identifier__$1,\n false,\n undefined,\n undefined,\n undefined\n );\n\nvar EVENTS = {\n MOUSE_ENTER: 'mouseenter',\n MOUSE_LEAVE: 'mouseleave',\n MOUSE_DOWN: 'mousedown',\n MOUSE_UP: 'mouseup',\n FOCUS: 'focus',\n BLUR: 'blur',\n CLICK: 'click',\n INPUT: 'input',\n KEY_DOWN: 'keydown',\n KEY_UP: 'keyup',\n KEY_PRESS: 'keypress',\n RESIZE: 'resize',\n SCROLL: 'scroll',\n TOUCH_START: 'touchstart',\n TOUCH_END: 'touchend'\n};\n\nvar TRIGGERS = {\n CLICK: 'click',\n HOVER: 'hover',\n FOCUS: 'focus',\n HOVER_FOCUS: 'hover-focus',\n OUTSIDE_CLICK: 'outside-click',\n MANUAL: 'manual'\n};\n\nvar PLACEMENTS = {\n TOP: 'top',\n RIGHT: 'right',\n BOTTOM: 'bottom',\n LEFT: 'left'\n};\n\nfunction isIE11 () {\n /* istanbul ignore next */\n return !!window.MSInputMethodContext && !!document.documentMode\n}\n\nfunction isIE10 () {\n return window.navigator.appVersion.indexOf('MSIE 10') !== -1\n}\n\nfunction getComputedStyle (el) {\n return window.getComputedStyle(el)\n}\n\nfunction getViewportSize () {\n /* istanbul ignore next */\n var width = Math.max(document.documentElement.clientWidth, window.innerWidth) || 0;\n /* istanbul ignore next */\n var height = Math.max(document.documentElement.clientHeight, window.innerHeight) || 0;\n return { width: width, height: height }\n}\n\nvar scrollbarWidth = null;\nvar savedScreenSize = null;\n\nfunction getScrollbarWidth (recalculate) {\n if ( recalculate === void 0 ) recalculate = false;\n\n var screenSize = getViewportSize();\n // return directly when already calculated & not force recalculate & screen size not changed\n if (scrollbarWidth !== null && !recalculate &&\n screenSize.height === savedScreenSize.height && screenSize.width === savedScreenSize.width) {\n return scrollbarWidth\n }\n /* istanbul ignore next */\n if (document.readyState === 'loading') {\n return null\n }\n var div1 = document.createElement('div');\n var div2 = document.createElement('div');\n div1.style.width = div2.style.width = div1.style.height = div2.style.height = '100px';\n div1.style.overflow = 'scroll';\n div2.style.overflow = 'hidden';\n document.body.appendChild(div1);\n document.body.appendChild(div2);\n scrollbarWidth = Math.abs(div1.scrollHeight - div2.scrollHeight);\n document.body.removeChild(div1);\n document.body.removeChild(div2);\n // save new screen size\n savedScreenSize = screenSize;\n return scrollbarWidth\n}\n\nfunction on (element, event, handler) {\n /* istanbul ignore next */\n element.addEventListener(event, handler);\n}\n\nfunction off (element, event, handler) {\n /* istanbul ignore next */\n element.removeEventListener(event, handler);\n}\n\nfunction isElement (el) {\n return el && el.nodeType === Node.ELEMENT_NODE\n}\n\nfunction removeFromDom (el) {\n isElement(el) && isElement(el.parentNode) && el.parentNode.removeChild(el);\n}\n\nfunction ensureElementMatchesFunction () {\n /* istanbul ignore next */\n if (!Element.prototype.matches) {\n Element.prototype.matches =\n Element.prototype.matchesSelector ||\n Element.prototype.mozMatchesSelector ||\n Element.prototype.msMatchesSelector ||\n Element.prototype.oMatchesSelector ||\n Element.prototype.webkitMatchesSelector ||\n function (s) {\n var matches = (this.document || this.ownerDocument).querySelectorAll(s);\n var i = matches.length;\n // eslint-disable-next-line no-empty\n while (--i >= 0 && matches.item(i) !== this) {}\n return i > -1\n };\n }\n}\n\nfunction addClass (el, className) {\n if (!isElement(el)) {\n return\n }\n if (el.className) {\n var classes = el.className.split(' ');\n if (classes.indexOf(className) < 0) {\n classes.push(className);\n el.className = classes.join(' ');\n }\n } else {\n el.className = className;\n }\n}\n\nfunction removeClass (el, className) {\n if (!isElement(el)) {\n return\n }\n if (el.className) {\n var classes = el.className.split(' ');\n var newClasses = [];\n for (var i = 0, l = classes.length; i < l; i++) {\n if (classes[i] !== className) {\n newClasses.push(classes[i]);\n }\n }\n el.className = newClasses.join(' ');\n }\n}\n\nfunction hasClass (el, className) {\n if (!isElement(el)) {\n return false\n }\n var classes = el.className.split(' ');\n for (var i = 0, l = classes.length; i < l; i++) {\n if (classes[i] === className) {\n return true\n }\n }\n return false\n}\n\nfunction setDropdownPosition (dropdown, trigger, options) {\n if ( options === void 0 ) options = {};\n\n var doc = document.documentElement;\n var containerScrollLeft = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);\n var containerScrollTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);\n var rect = trigger.getBoundingClientRect();\n var dropdownRect = dropdown.getBoundingClientRect();\n dropdown.style.right = 'auto';\n dropdown.style.bottom = 'auto';\n if (options.menuRight) {\n dropdown.style.left = containerScrollLeft + rect.left + rect.width - dropdownRect.width + 'px';\n } else {\n dropdown.style.left = containerScrollLeft + rect.left + 'px';\n }\n if (options.dropup) {\n dropdown.style.top = containerScrollTop + rect.top - dropdownRect.height - 4 + 'px';\n } else {\n dropdown.style.top = containerScrollTop + rect.top + rect.height + 'px';\n }\n}\n\nfunction isAvailableAtPosition (trigger, popup, placement) {\n var triggerRect = trigger.getBoundingClientRect();\n var popupRect = popup.getBoundingClientRect();\n var viewPortSize = getViewportSize();\n var top = true;\n var right = true;\n var bottom = true;\n var left = true;\n switch (placement) {\n case PLACEMENTS.TOP:\n top = triggerRect.top >= popupRect.height;\n left = triggerRect.left + triggerRect.width / 2 >= popupRect.width / 2;\n right = triggerRect.right - triggerRect.width / 2 + popupRect.width / 2 <= viewPortSize.width;\n break\n case PLACEMENTS.BOTTOM:\n bottom = triggerRect.bottom + popupRect.height <= viewPortSize.height;\n left = triggerRect.left + triggerRect.width / 2 >= popupRect.width / 2;\n right = triggerRect.right - triggerRect.width / 2 + popupRect.width / 2 <= viewPortSize.width;\n break\n case PLACEMENTS.RIGHT:\n right = triggerRect.right + popupRect.width <= viewPortSize.width;\n top = triggerRect.top + triggerRect.height / 2 >= popupRect.height / 2;\n bottom = triggerRect.bottom - triggerRect.height / 2 + popupRect.height / 2 <= viewPortSize.height;\n break\n case PLACEMENTS.LEFT:\n left = triggerRect.left >= popupRect.width;\n top = triggerRect.top + triggerRect.height / 2 >= popupRect.height / 2;\n bottom = triggerRect.bottom - triggerRect.height / 2 + popupRect.height / 2 <= viewPortSize.height;\n break\n }\n return top && right && bottom && left\n}\n\nfunction setTooltipPosition (tooltip, trigger, placement, auto, appendTo, positionBy, viewport) {\n if (!isElement(tooltip) || !isElement(trigger)) {\n return\n }\n var isPopover = tooltip && tooltip.className && tooltip.className.indexOf('popover') >= 0;\n var containerScrollTop;\n var containerScrollLeft;\n if (!isExist(appendTo) || appendTo === 'body' || positionBy === 'body') {\n var doc = document.documentElement;\n containerScrollLeft = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);\n containerScrollTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);\n } else {\n var container = getElementBySelectorOrRef(positionBy || appendTo);\n containerScrollLeft = container.scrollLeft;\n containerScrollTop = container.scrollTop;\n }\n // auto adjust placement\n if (auto) {\n // Try: right -> bottom -> left -> top\n // Cause the default placement is top\n var placements = [PLACEMENTS.RIGHT, PLACEMENTS.BOTTOM, PLACEMENTS.LEFT, PLACEMENTS.TOP];\n // The class switch helper function\n var changePlacementClass = function (placement) {\n // console.log(placement)\n placements.forEach(function (placement) {\n removeClass(tooltip, placement);\n });\n addClass(tooltip, placement);\n };\n // No need to adjust if the default placement fits\n if (!isAvailableAtPosition(trigger, tooltip, placement)) {\n for (var i = 0, l = placements.length; i < l; i++) {\n // Re-assign placement class\n changePlacementClass(placements[i]);\n // Break if new placement fits\n if (isAvailableAtPosition(trigger, tooltip, placements[i])) {\n placement = placements[i];\n break\n }\n }\n changePlacementClass(placement);\n }\n }\n // fix left and top for tooltip\n var rect = trigger.getBoundingClientRect();\n var tooltipRect = tooltip.getBoundingClientRect();\n var top;\n var left;\n if (placement === PLACEMENTS.BOTTOM) {\n top = containerScrollTop + rect.top + rect.height;\n left = containerScrollLeft + rect.left + rect.width / 2 - tooltipRect.width / 2;\n } else if (placement === PLACEMENTS.LEFT) {\n top = containerScrollTop + rect.top + rect.height / 2 - tooltipRect.height / 2;\n left = containerScrollLeft + rect.left - tooltipRect.width;\n } else if (placement === PLACEMENTS.RIGHT) {\n top = containerScrollTop + rect.top + rect.height / 2 - tooltipRect.height / 2;\n // https://github.com/uiv-lib/uiv/issues/272\n // add 1px to fix above issue\n left = containerScrollLeft + rect.left + rect.width + 1;\n } else {\n top = containerScrollTop + rect.top - tooltipRect.height;\n left = containerScrollLeft + rect.left + rect.width / 2 - tooltipRect.width / 2;\n }\n var viewportEl;\n // viewport option\n if (isString(viewport)) {\n viewportEl = document.querySelector(viewport);\n } else if (isFunction(viewport)) {\n viewportEl = viewport(trigger);\n }\n if (isElement(viewportEl)) {\n var popoverFix = isPopover ? 11 : 0;\n var viewportReact = viewportEl.getBoundingClientRect();\n var viewportTop = containerScrollTop + viewportReact.top;\n var viewportLeft = containerScrollLeft + viewportReact.left;\n var viewportBottom = viewportTop + viewportReact.height;\n var viewportRight = viewportLeft + viewportReact.width;\n // fix top\n if (top < viewportTop) {\n top = viewportTop;\n } else if (top + tooltipRect.height > viewportBottom) {\n top = viewportBottom - tooltipRect.height;\n }\n // fix left\n if (left < viewportLeft) {\n left = viewportLeft;\n } else if (left + tooltipRect.width > viewportRight) {\n left = viewportRight - tooltipRect.width;\n }\n // fix for popover pointer\n if (placement === PLACEMENTS.BOTTOM) {\n top -= popoverFix;\n } else if (placement === PLACEMENTS.LEFT) {\n left += popoverFix;\n } else if (placement === PLACEMENTS.RIGHT) {\n left -= popoverFix;\n } else {\n top += popoverFix;\n }\n }\n // set position finally\n tooltip.style.top = top + \"px\";\n tooltip.style.left = left + \"px\";\n}\n\nfunction hasScrollbar (el) {\n var SCROLL = 'scroll';\n var hasVScroll = el.scrollHeight > el.clientHeight;\n var style = getComputedStyle(el);\n return hasVScroll || style.overflow === SCROLL || style.overflowY === SCROLL\n}\n\nfunction toggleBodyOverflow (enable) {\n var MODAL_OPEN = 'modal-open';\n var FIXED_CONTENT = '.navbar-fixed-top, .navbar-fixed-bottom';\n var body = document.body;\n if (enable) {\n removeClass(body, MODAL_OPEN);\n body.style.paddingRight = null;\n nodeListToArray(document.querySelectorAll(FIXED_CONTENT)).forEach(function (node) {\n node.style.paddingRight = null;\n });\n } else {\n var browsersWithFloatingScrollbar = isIE10() || isIE11();\n var documentHasScrollbar = hasScrollbar(document.documentElement) || hasScrollbar(document.body);\n if (documentHasScrollbar && !browsersWithFloatingScrollbar) {\n var scrollbarWidth = getScrollbarWidth();\n body.style.paddingRight = scrollbarWidth + \"px\";\n nodeListToArray(document.querySelectorAll(FIXED_CONTENT)).forEach(function (node) {\n node.style.paddingRight = scrollbarWidth + \"px\";\n });\n }\n addClass(body, MODAL_OPEN);\n }\n}\n\nfunction getClosest (el, selector) {\n ensureElementMatchesFunction();\n var parent;\n var _el = el;\n while (_el) {\n parent = _el.parentElement;\n if (parent && parent.matches(selector)) {\n return parent\n }\n _el = parent;\n }\n return null\n}\n\nfunction getParents (el, selector, until) {\n if ( until === void 0 ) until = null;\n\n ensureElementMatchesFunction();\n var parents = [];\n var parent = el.parentElement;\n while (parent) {\n if (parent.matches(selector)) {\n parents.push(parent);\n } else if (until && (until === parent || parent.matches(until))) {\n break\n }\n parent = parent.parentElement;\n }\n return parents\n}\n\nfunction focus (el) {\n if (!isElement(el)) {\n return\n }\n el.getAttribute('tabindex') ? null : el.setAttribute('tabindex', '-1');\n el.focus();\n}\n\nvar MODAL_BACKDROP = 'modal-backdrop';\n\nfunction getOpenModals () {\n return document.querySelectorAll((\".\" + MODAL_BACKDROP))\n}\n\nfunction getOpenModalNum () {\n return getOpenModals().length\n}\n\nfunction getElementBySelectorOrRef (q) {\n if (isString(q)) { // is selector\n return document.querySelector(q)\n } else if (isElement(q)) { // is element\n return q\n } else if (isElement(q.$el)) { // is component\n return q.$el\n } else {\n return null\n }\n}\n\nvar COLLAPSE = 'collapse';\nvar IN = 'in';\nvar COLLAPSING = 'collapsing';\n\nvar Collapse = {\n render: function render (h) {\n return h(this.tag, {}, this.$slots.default)\n },\n props: {\n tag: {\n type: String,\n default: 'div'\n },\n value: {\n type: Boolean,\n default: false\n },\n transition: {\n type: Number,\n default: 350\n }\n },\n data: function data () {\n return {\n timeoutId: 0\n }\n },\n watch: {\n value: function value (show) {\n this.toggle(show);\n }\n },\n mounted: function mounted () {\n var el = this.$el;\n addClass(el, COLLAPSE);\n if (this.value) {\n addClass(el, IN);\n }\n },\n methods: {\n toggle: function toggle (show) {\n var this$1 = this;\n\n clearTimeout(this.timeoutId);\n var el = this.$el;\n if (show) {\n this.$emit('show');\n removeClass(el, COLLAPSE);\n el.style.height = 'auto';\n var height = window.getComputedStyle(el).height;\n el.style.height = null;\n addClass(el, COLLAPSING);\n el.offsetHeight; // force repaint\n el.style.height = height;\n this.timeoutId = setTimeout(function () {\n removeClass(el, COLLAPSING);\n addClass(el, COLLAPSE);\n addClass(el, IN);\n el.style.height = null;\n this$1.timeoutId = 0;\n this$1.$emit('shown');\n }, this.transition);\n } else {\n this.$emit('hide');\n el.style.height = window.getComputedStyle(el).height;\n removeClass(el, IN);\n removeClass(el, COLLAPSE);\n el.offsetHeight;\n el.style.height = null;\n addClass(el, COLLAPSING);\n this.timeoutId = setTimeout(function () {\n addClass(el, COLLAPSE);\n removeClass(el, COLLAPSING);\n el.style.height = null;\n this$1.timeoutId = 0;\n this$1.$emit('hidden');\n }, this.transition);\n }\n }\n }\n};\n\nvar DEFAULT_TAG = 'div';\n\nvar Dropdown = {\n render: function render (h) {\n return h(\n this.tag,\n {\n class: {\n 'btn-group': this.tag === DEFAULT_TAG,\n dropdown: !this.dropup,\n dropup: this.dropup,\n open: this.show\n }\n },\n [\n this.$slots.default,\n h(\n 'ul',\n {\n class: {\n 'dropdown-menu': true,\n 'dropdown-menu-right': this.menuRight\n },\n ref: 'dropdown'\n },\n [this.$slots.dropdown]\n )\n ]\n )\n },\n props: {\n tag: {\n type: String,\n default: DEFAULT_TAG\n },\n appendToBody: {\n type: Boolean,\n default: false\n },\n value: Boolean,\n dropup: {\n type: Boolean,\n default: false\n },\n menuRight: {\n type: Boolean,\n default: false\n },\n disabled: {\n type: Boolean,\n default: false\n },\n notCloseElements: Array,\n positionElement: null\n },\n data: function data () {\n return {\n show: false,\n triggerEl: undefined\n }\n },\n watch: {\n value: function value (v) {\n this.toggle(v);\n }\n },\n mounted: function mounted () {\n this.initTrigger();\n if (this.triggerEl) {\n on(this.triggerEl, EVENTS.CLICK, this.toggle);\n on(this.triggerEl, EVENTS.KEY_DOWN, this.onKeyPress);\n }\n on(this.$refs.dropdown, EVENTS.KEY_DOWN, this.onKeyPress);\n on(window, EVENTS.CLICK, this.windowClicked);\n on(window, EVENTS.TOUCH_END, this.windowClicked);\n if (this.value) {\n this.toggle(true);\n }\n },\n beforeDestroy: function beforeDestroy () {\n this.removeDropdownFromBody();\n if (this.triggerEl) {\n off(this.triggerEl, EVENTS.CLICK, this.toggle);\n off(this.triggerEl, EVENTS.KEY_DOWN, this.onKeyPress);\n }\n off(this.$refs.dropdown, EVENTS.KEY_DOWN, this.onKeyPress);\n off(window, EVENTS.CLICK, this.windowClicked);\n off(window, EVENTS.TOUCH_END, this.windowClicked);\n },\n methods: {\n getFocusItem: function getFocusItem () {\n var dropdownEl = this.$refs.dropdown;\n /* istanbul ignore next */\n return dropdownEl.querySelector('li > a:focus')\n },\n onKeyPress: function onKeyPress (event) {\n if (this.show) {\n var dropdownEl = this.$refs.dropdown;\n var keyCode = event.keyCode;\n if (keyCode === 27) {\n // esc\n this.toggle(false);\n this.triggerEl && this.triggerEl.focus();\n } else if (keyCode === 13) {\n // enter\n var currentFocus = this.getFocusItem();\n currentFocus && currentFocus.click();\n } else if (keyCode === 38 || keyCode === 40) {\n // up || down\n event.preventDefault();\n event.stopPropagation();\n var currentFocus$1 = this.getFocusItem();\n var items = dropdownEl.querySelectorAll('li:not(.disabled) > a');\n if (!currentFocus$1) {\n focus(items[0]);\n } else {\n for (var i = 0; i < items.length; i++) {\n if (currentFocus$1 === items[i]) {\n if (keyCode === 38 && i < items.length > 0) {\n focus(items[i - 1]);\n } else if (keyCode === 40 && i < items.length - 1) {\n focus(items[i + 1]);\n }\n break\n }\n }\n }\n }\n }\n },\n initTrigger: function initTrigger () {\n var trigger = this.$el.querySelector('[data-role=\"trigger\"]') || this.$el.querySelector('.dropdown-toggle') || this.$el.firstChild;\n this.triggerEl = trigger && trigger !== this.$refs.dropdown ? trigger : null;\n },\n toggle: function toggle (show) {\n if (this.disabled) {\n return\n }\n if (isBoolean(show)) {\n this.show = show;\n } else {\n this.show = !this.show;\n }\n if (this.appendToBody) {\n this.show ? this.appendDropdownToBody() : this.removeDropdownFromBody();\n }\n this.$emit('input', this.show);\n },\n windowClicked: function windowClicked (event) {\n var target = event.target;\n if (this.show && target) {\n var targetInNotCloseElements = false;\n if (this.notCloseElements) {\n for (var i = 0, l = this.notCloseElements.length; i < l; i++) {\n var isTargetInElement = this.notCloseElements[i].contains(target);\n var shouldBreak = isTargetInElement;\n /* istanbul ignore else */\n if (this.appendToBody) {\n var isTargetInDropdown = this.$refs.dropdown.contains(target);\n var isElInElements = this.notCloseElements.indexOf(this.$el) >= 0;\n shouldBreak = isTargetInElement || (isTargetInDropdown && isElInElements);\n }\n if (shouldBreak) {\n targetInNotCloseElements = true;\n break\n }\n }\n }\n var targetInDropdownBody = this.$refs.dropdown.contains(target);\n var targetInTrigger = this.$el.contains(target) && !targetInDropdownBody;\n // normally, a dropdown select event is handled by @click that trigger after @touchend\n // then @touchend event have to be ignore in this case\n var targetInDropdownAndIsTouchEvent = targetInDropdownBody && event.type === 'touchend';\n if (!targetInTrigger && !targetInNotCloseElements && !targetInDropdownAndIsTouchEvent) {\n this.toggle(false);\n }\n }\n },\n appendDropdownToBody: function appendDropdownToBody () {\n try {\n var el = this.$refs.dropdown;\n el.style.display = 'block';\n document.body.appendChild(el);\n var positionElement = this.positionElement || this.$el;\n setDropdownPosition(el, positionElement, this);\n } catch (e) {\n // Silent\n }\n },\n removeDropdownFromBody: function removeDropdownFromBody () {\n try {\n var el = this.$refs.dropdown;\n el.removeAttribute('style');\n this.$el.appendChild(el);\n } catch (e) {\n // Silent\n }\n }\n }\n};\n\nvar defaultLang = {\n uiv: {\n datePicker: {\n clear: 'Clear',\n today: 'Today',\n month: 'Month',\n month1: 'January',\n month2: 'February',\n month3: 'March',\n month4: 'April',\n month5: 'May',\n month6: 'June',\n month7: 'July',\n month8: 'August',\n month9: 'September',\n month10: 'October',\n month11: 'November',\n month12: 'December',\n year: 'Year',\n week1: 'Mon',\n week2: 'Tue',\n week3: 'Wed',\n week4: 'Thu',\n week5: 'Fri',\n week6: 'Sat',\n week7: 'Sun'\n },\n timePicker: {\n am: 'AM',\n pm: 'PM'\n },\n modal: {\n cancel: 'Cancel',\n ok: 'OK'\n },\n multiSelect: {\n placeholder: 'Select...',\n filterPlaceholder: 'Search...'\n }\n }\n};\n\n// https://github.com/ElemeFE/element/blob/dev/src/locale/index.js\n\nvar lang = defaultLang;\n\nvar i18nHandler = function () {\n var vuei18n = Object.getPrototypeOf(this).$t;\n /* istanbul ignore else */\n /* istanbul ignore next */\n if (isFunction(vuei18n)) {\n /* istanbul ignore next */\n try {\n return vuei18n.apply(this, arguments)\n } catch (err) {\n return this.$t.apply(this, arguments)\n }\n }\n};\n\nvar t = function (path, options) {\n options = options || {};\n var value;\n try {\n value = i18nHandler.apply(this, arguments);\n /* istanbul ignore next */\n if (isExist(value) && !options.$$locale) {\n return value\n }\n } catch (e) {\n // ignore\n }\n var array = path.split('.');\n var current = options.$$locale || lang;\n\n for (var i = 0, j = array.length; i < j; i++) {\n var property = array[i];\n value = current[property];\n if (i === j - 1) { return value }\n if (!value) { return '' }\n current = value;\n }\n /* istanbul ignore next */\n return ''\n};\n\nvar use = function (l) {\n lang = l || lang;\n};\n\nvar i18n = function (fn) {\n i18nHandler = fn || i18nHandler;\n};\n\nvar locale = { use: use, t: t, i18n: i18n };\n\nvar Local = {\n methods: {\n t: function t$1 () {\n var arguments$1 = arguments;\n\n var args = [];\n for (var i = 0; i < arguments.length; ++i) {\n args.push(arguments$1[i]);\n }\n args[1] = assign({}, { $$locale: this.locale }, args[1]);\n return t.apply(this, args)\n }\n },\n props: {\n locale: Object\n }\n};\n\nvar e=function(){return (e=Object.assign||function(e){for(var t,r=1,s=arguments.length;r props\n href: String,\n target: String,\n // props\n to: null,\n replace: {\n type: Boolean,\n default: false\n },\n append: {\n type: Boolean,\n default: false\n },\n exact: {\n type: Boolean,\n default: false\n }\n }\n};\n\nvar BtnGroup = {\n functional: true,\n render: function render (h, ref) {\n var obj;\n\n var props = ref.props;\n var children = ref.children;\n var data = ref.data;\n return h(\n 'div',\n a(data, {\n class: ( obj = {\n 'btn-group': !props.vertical,\n 'btn-group-vertical': props.vertical,\n 'btn-group-justified': props.justified\n }, obj[(\"btn-group-\" + (props.size))] = props.size, obj ),\n attrs: {\n role: 'group',\n 'data-toggle': 'buttons'\n }\n }),\n children\n )\n },\n props: {\n size: String,\n vertical: {\n type: Boolean,\n default: false\n },\n justified: {\n type: Boolean,\n default: false\n }\n }\n};\n\nvar INPUT_TYPE_CHECKBOX = 'checkbox';\nvar INPUT_TYPE_RADIO = 'radio';\n\nvar Btn = {\n functional: true,\n mixins: [linkMixin],\n render: function render (h, ref) {\n var children = ref.children;\n var props = ref.props;\n var data = ref.data;\n\n // event listeners\n var listeners = data.on || {};\n // checkbox: model contain inputValue\n // radio: model === inputValue\n var isInputActive = props.inputType === INPUT_TYPE_CHECKBOX ? props.value.indexOf(props.inputValue) >= 0 : props.value === props.inputValue;\n // button class\n var classes = {\n btn: true,\n active: props.inputType ? isInputActive : props.active,\n disabled: props.disabled,\n 'btn-block': props.block\n };\n classes[(\"btn-\" + (props.type))] = Boolean(props.type);\n classes[(\"btn-\" + (props.size))] = Boolean(props.size);\n // prevent event for disabled links\n var on = {\n click: function click (e) {\n if (props.disabled && e instanceof Event) {\n e.preventDefault();\n e.stopPropagation();\n }\n }\n };\n // render params\n var tag, options, slot;\n\n if (props.href) {\n // is native link\n tag = 'a';\n slot = children;\n options = a(data, {\n on: on,\n class: classes,\n attrs: {\n role: 'button',\n href: props.href,\n target: props.target\n }\n });\n } else if (props.to) {\n // is vue router link\n tag = 'router-link';\n slot = children;\n options = a(data, {\n nativeOn: on,\n class: classes,\n props: {\n event: props.disabled ? '' : 'click', // prevent nav while disabled\n to: props.to,\n replace: props.replace,\n append: props.append,\n exact: props.exact\n },\n attrs: {\n role: 'button'\n }\n });\n } else if (props.inputType) {\n // is input checkbox or radio\n tag = 'label';\n options = a(data, {\n on: on,\n class: classes\n });\n slot = [\n h('input', {\n attrs: {\n autocomplete: 'off',\n type: props.inputType,\n checked: isInputActive ? 'checked' : null,\n disabled: props.disabled\n },\n domProps: {\n checked: isInputActive // required\n },\n on: {\n input: function input (evt) {\n evt.stopPropagation();\n },\n change: function change () {\n if (props.inputType === INPUT_TYPE_CHECKBOX) {\n var valueCopied = props.value.slice();\n if (isInputActive) {\n valueCopied.splice(valueCopied.indexOf(props.inputValue), 1);\n } else {\n valueCopied.push(props.inputValue);\n }\n listeners.input(valueCopied);\n } else {\n listeners.input(props.inputValue);\n }\n }\n }\n }),\n children\n ];\n } else if (props.justified) {\n // is in justified btn-group\n tag = BtnGroup;\n options = {};\n slot = [\n h('button', a(data, {\n on: on,\n class: classes,\n attrs: {\n type: props.nativeType,\n disabled: props.disabled\n }\n }), children)\n ];\n } else {\n // is button\n tag = 'button';\n slot = children;\n options = a(data, {\n on: on,\n class: classes,\n attrs: {\n type: props.nativeType,\n disabled: props.disabled\n }\n });\n }\n\n return h(tag, options, slot)\n },\n props: {\n justified: {\n type: Boolean,\n default: false\n },\n type: {\n type: String,\n default: 'default'\n },\n nativeType: {\n type: String,\n default: 'button'\n },\n size: String,\n block: {\n type: Boolean,\n default: false\n },\n active: {\n type: Boolean,\n default: false\n },\n disabled: {\n type: Boolean,\n default: false\n },\n // props\n value: null,\n inputValue: null,\n inputType: {\n type: String,\n validator: function validator (value) {\n return value === INPUT_TYPE_CHECKBOX || value === INPUT_TYPE_RADIO\n }\n }\n }\n};\n\nvar IN$1 = 'in';\n\nvar script$2 = {\n mixins: [Local],\n components: { Btn: Btn },\n props: {\n value: {\n type: Boolean,\n default: false\n },\n title: String,\n size: String,\n backdrop: {\n type: Boolean,\n default: true\n },\n footer: {\n type: Boolean,\n default: true\n },\n header: {\n type: Boolean,\n default: true\n },\n cancelText: String,\n cancelType: {\n type: String,\n default: 'default'\n },\n okText: String,\n okType: {\n type: String,\n default: 'primary'\n },\n dismissBtn: {\n type: Boolean,\n default: true\n },\n transition: {\n type: Number,\n default: 150\n },\n autoFocus: {\n type: Boolean,\n default: false\n },\n keyboard: {\n type: Boolean,\n default: true\n },\n beforeClose: Function,\n zOffset: {\n type: Number,\n default: 20\n },\n appendToBody: {\n type: Boolean,\n default: false\n },\n displayStyle: {\n type: String,\n default: 'block'\n }\n },\n data: function data () {\n return {\n msg: ''\n }\n },\n computed: {\n modalSizeClass: function modalSizeClass () {\n var obj;\n\n return ( obj = {}, obj[(\"modal-\" + (this.size))] = Boolean(this.size), obj )\n }\n },\n watch: {\n value: function value (v) {\n this.$toggle(v);\n }\n },\n mounted: function mounted () {\n removeFromDom(this.$refs.backdrop);\n on(window, EVENTS.MOUSE_DOWN, this.suppressBackgroundClose);\n on(window, EVENTS.KEY_UP, this.onKeyPress);\n if (this.value) {\n this.$toggle(true);\n }\n },\n beforeDestroy: function beforeDestroy () {\n clearTimeout(this.timeoutId);\n removeFromDom(this.$refs.backdrop);\n removeFromDom(this.$el);\n if (getOpenModalNum() === 0) {\n toggleBodyOverflow(true);\n }\n off(window, EVENTS.MOUSE_DOWN, this.suppressBackgroundClose);\n off(window, EVENTS.MOUSE_UP, this.unsuppressBackgroundClose);\n off(window, EVENTS.KEY_UP, this.onKeyPress);\n },\n methods: {\n onKeyPress: function onKeyPress (event) {\n if (this.keyboard && this.value && event.keyCode === 27) {\n var thisModal = this.$refs.backdrop;\n var thisZIndex = thisModal.style.zIndex;\n thisZIndex = thisZIndex && thisZIndex !== 'auto' ? parseInt(thisZIndex) : 0;\n // Find out if this modal is the top most one.\n var modals = getOpenModals();\n var modalsLength = modals.length;\n for (var i = 0; i < modalsLength; i++) {\n if (modals[i] !== thisModal) {\n var zIndex = modals[i].style.zIndex;\n zIndex = zIndex && zIndex !== 'auto' ? parseInt(zIndex) : 0;\n // if any existing modal has higher zIndex, ignore\n if (zIndex > thisZIndex) {\n return\n }\n }\n }\n this.toggle(false);\n }\n },\n toggle: function toggle (show, msg) {\n var this$1 = this;\n\n var shouldClose = true;\n if (isFunction(this.beforeClose)) {\n shouldClose = this.beforeClose(msg);\n }\n\n if (isPromiseSupported()) {\n // Skip the hiding when beforeClose returning falsely value or returned Promise resolves to falsely value\n // Use Promise.resolve to accept both Boolean values and Promises\n Promise.resolve(shouldClose).then(function (shouldClose) {\n // Skip the hiding while show===false\n if (!show && shouldClose) {\n this$1.msg = msg;\n this$1.$emit('input', show);\n }\n });\n } else {\n // Fallback to old version if promise is not supported\n // skip the hiding while show===false & beforeClose returning falsely value\n if (!show && !shouldClose) {\n return\n }\n\n this.msg = msg;\n this.$emit('input', show);\n }\n },\n $toggle: function $toggle (show) {\n var this$1 = this;\n\n var modal = this.$el;\n var backdrop = this.$refs.backdrop;\n clearTimeout(this.timeoutId);\n if (show) {\n // If two modals share the same v-if condition the calculated z-index is incorrect,\n // resulting in popover misbehaviour.\n // solved by adding a nextTick.\n // https://github.com/uiv-lib/uiv/issues/342\n this.$nextTick(function () {\n var alreadyOpenModalNum = getOpenModalNum();\n document.body.appendChild(backdrop);\n if (this$1.appendToBody) {\n document.body.appendChild(modal);\n }\n modal.style.display = this$1.displayStyle;\n modal.scrollTop = 0;\n backdrop.offsetHeight; // force repaint\n toggleBodyOverflow(false);\n addClass(backdrop, IN$1);\n addClass(modal, IN$1);\n // fix z-index for nested modals\n // no need to calculate if no modal is already open\n if (alreadyOpenModalNum > 0) {\n var modalBaseZ = parseInt(getComputedStyle(modal).zIndex) || 1050; // 1050 is default modal z-Index\n var backdropBaseZ = parseInt(getComputedStyle(backdrop).zIndex) || 1040; // 1040 is default backdrop z-Index\n var offset = alreadyOpenModalNum * this$1.zOffset;\n modal.style.zIndex = \"\" + (modalBaseZ + offset);\n backdrop.style.zIndex = \"\" + (backdropBaseZ + offset);\n }\n // z-index fix end\n this$1.timeoutId = setTimeout(function () {\n if (this$1.autoFocus) {\n var btn = this$1.$el.querySelector('[data-action=\"auto-focus\"]');\n if (btn) {\n btn.focus();\n }\n }\n this$1.$emit('show');\n this$1.timeoutId = 0;\n }, this$1.transition);\n });\n } else {\n removeClass(backdrop, IN$1);\n removeClass(modal, IN$1);\n this.timeoutId = setTimeout(function () {\n modal.style.display = 'none';\n removeFromDom(backdrop);\n if (this$1.appendToBody) {\n removeFromDom(modal);\n }\n if (getOpenModalNum() === 0) {\n toggleBodyOverflow(true);\n }\n this$1.$emit('hide', this$1.msg || 'dismiss');\n this$1.msg = '';\n this$1.timeoutId = 0;\n // restore z-index for nested modals\n modal.style.zIndex = '';\n backdrop.style.zIndex = '';\n // z-index fix end\n }, this.transition);\n }\n },\n suppressBackgroundClose: function suppressBackgroundClose (event) {\n if (event && event.target === this.$el) {\n return\n }\n this.isCloseSuppressed = true;\n on(window, 'mouseup', this.unsuppressBackgroundClose);\n },\n unsuppressBackgroundClose: function unsuppressBackgroundClose () {\n var this$1 = this;\n\n if (this.isCloseSuppressed) {\n off(window, 'mouseup', this.unsuppressBackgroundClose);\n setTimeout(function () {\n this$1.isCloseSuppressed = false;\n }, 1);\n }\n },\n backdropClicked: function backdropClicked (event) {\n if (this.backdrop && !this.isCloseSuppressed) {\n this.toggle(false);\n }\n }\n }\n};\n\n/* script */\nvar __vue_script__$2 = script$2;\n\n/* template */\nvar __vue_render__$2 = function() {\n var _vm = this;\n var _h = _vm.$createElement;\n var _c = _vm._self._c || _h;\n return _c(\n \"div\",\n {\n staticClass: \"modal\",\n class: { fade: _vm.transition > 0 },\n attrs: { tabindex: \"-1\", role: \"dialog\" },\n on: {\n click: function($event) {\n if ($event.target !== $event.currentTarget) {\n return null\n }\n return _vm.backdropClicked($event)\n }\n }\n },\n [\n _c(\n \"div\",\n {\n ref: \"dialog\",\n staticClass: \"modal-dialog\",\n class: _vm.modalSizeClass,\n attrs: { role: \"document\" }\n },\n [\n _c(\"div\", { staticClass: \"modal-content\" }, [\n _vm.header\n ? _c(\n \"div\",\n { staticClass: \"modal-header\" },\n [\n _vm._t(\"header\", [\n _vm.dismissBtn\n ? _c(\n \"button\",\n {\n staticClass: \"close\",\n staticStyle: {\n position: \"relative\",\n \"z-index\": \"1060\"\n },\n attrs: { type: \"button\", \"aria-label\": \"Close\" },\n on: {\n click: function($event) {\n return _vm.toggle(false)\n }\n }\n },\n [\n _c(\"span\", { attrs: { \"aria-hidden\": \"true\" } }, [\n _vm._v(\"×\")\n ])\n ]\n )\n : _vm._e(),\n _vm._v(\" \"),\n _c(\n \"h4\",\n { staticClass: \"modal-title\" },\n [_vm._t(\"title\", [_vm._v(_vm._s(_vm.title))])],\n 2\n )\n ])\n ],\n 2\n )\n : _vm._e(),\n _vm._v(\" \"),\n _c(\"div\", { staticClass: \"modal-body\" }, [_vm._t(\"default\")], 2),\n _vm._v(\" \"),\n _vm.footer\n ? _c(\n \"div\",\n { staticClass: \"modal-footer\" },\n [\n _vm._t(\"footer\", [\n _c(\n \"btn\",\n {\n attrs: { type: _vm.cancelType },\n on: {\n click: function($event) {\n return _vm.toggle(false, \"cancel\")\n }\n }\n },\n [\n _c(\"span\", [\n _vm._v(\n _vm._s(\n _vm.cancelText || _vm.t(\"uiv.modal.cancel\")\n )\n )\n ])\n ]\n ),\n _vm._v(\" \"),\n _c(\n \"btn\",\n {\n attrs: {\n type: _vm.okType,\n \"data-action\": \"auto-focus\"\n },\n on: {\n click: function($event) {\n return _vm.toggle(false, \"ok\")\n }\n }\n },\n [\n _c(\"span\", [\n _vm._v(_vm._s(_vm.okText || _vm.t(\"uiv.modal.ok\")))\n ])\n ]\n )\n ])\n ],\n 2\n )\n : _vm._e()\n ])\n ]\n ),\n _vm._v(\" \"),\n _c(\"div\", {\n ref: \"backdrop\",\n staticClass: \"modal-backdrop\",\n class: { fade: _vm.transition > 0 }\n })\n ]\n )\n};\nvar __vue_staticRenderFns__$2 = [];\n__vue_render__$2._withStripped = true;\n\n /* style */\n var __vue_inject_styles__$2 = undefined;\n /* scoped */\n var __vue_scope_id__$2 = undefined;\n /* module identifier */\n var __vue_module_identifier__$2 = undefined;\n /* functional template */\n var __vue_is_functional_template__$2 = false;\n /* style inject */\n \n /* style inject SSR */\n \n /* style inject shadow dom */\n \n\n \n var __vue_component__$2 = /*#__PURE__*/normalizeComponent(\n { render: __vue_render__$2, staticRenderFns: __vue_staticRenderFns__$2 },\n __vue_inject_styles__$2,\n __vue_script__$2,\n __vue_scope_id__$2,\n __vue_is_functional_template__$2,\n __vue_module_identifier__$2,\n false,\n undefined,\n undefined,\n undefined\n );\n\nfunction _typeof(obj) {\n if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") {\n _typeof = function (obj) {\n return typeof obj;\n };\n } else {\n _typeof = function (obj) {\n return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj;\n };\n }\n\n return _typeof(obj);\n}\n\nfunction _toConsumableArray(arr) {\n return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();\n}\n\nfunction _arrayWithoutHoles(arr) {\n if (Array.isArray(arr)) {\n for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; }\n\n return arr2;\n }\n}\n\nfunction _iterableToArray(iter) {\n if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === \"[object Arguments]\") { return Array.from(iter); }\n}\n\nfunction _nonIterableSpread() {\n throw new TypeError(\"Invalid attempt to spread non-iterable instance\");\n}\n\nvar inBrowser = typeof window !== 'undefined';\nfunction freeze(item) {\n if (Array.isArray(item) || _typeof(item) === 'object') {\n return Object.freeze(item);\n }\n\n return item;\n}\nfunction combinePassengers(transports) {\n var slotProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n return transports.reduce(function (passengers, transport) {\n var temp = transport.passengers[0];\n var newPassengers = typeof temp === 'function' ? temp(slotProps) : transport.passengers;\n return passengers.concat(newPassengers);\n }, []);\n}\nfunction stableSort(array, compareFn) {\n return array.map(function (v, idx) {\n return [idx, v];\n }).sort(function (a, b) {\n return compareFn(a[1], b[1]) || a[0] - b[0];\n }).map(function (c) {\n return c[1];\n });\n}\nfunction pick(obj, keys) {\n return keys.reduce(function (acc, key) {\n if (obj.hasOwnProperty(key)) {\n acc[key] = obj[key];\n }\n\n return acc;\n }, {});\n}\n\nvar transports = {};\nvar targets = {};\nvar sources = {};\nvar Wormhole = Vue.extend({\n data: function data() {\n return {\n transports: transports,\n targets: targets,\n sources: sources,\n trackInstances: inBrowser\n };\n },\n methods: {\n open: function open(transport) {\n if (!inBrowser) { return; }\n var to = transport.to,\n from = transport.from,\n passengers = transport.passengers,\n _transport$order = transport.order,\n order = _transport$order === void 0 ? Infinity : _transport$order;\n if (!to || !from || !passengers) { return; }\n var newTransport = {\n to: to,\n from: from,\n passengers: freeze(passengers),\n order: order\n };\n var keys = Object.keys(this.transports);\n\n if (keys.indexOf(to) === -1) {\n Vue.set(this.transports, to, []);\n }\n\n var currentIndex = this.$_getTransportIndex(newTransport); // Copying the array here so that the PortalTarget change event will actually contain two distinct arrays\n\n var newTransports = this.transports[to].slice(0);\n\n if (currentIndex === -1) {\n newTransports.push(newTransport);\n } else {\n newTransports[currentIndex] = newTransport;\n }\n\n this.transports[to] = stableSort(newTransports, function (a, b) {\n return a.order - b.order;\n });\n },\n close: function close(transport) {\n var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n var to = transport.to,\n from = transport.from;\n if (!to || !from && force === false) { return; }\n\n if (!this.transports[to]) {\n return;\n }\n\n if (force) {\n this.transports[to] = [];\n } else {\n var index = this.$_getTransportIndex(transport);\n\n if (index >= 0) {\n // Copying the array here so that the PortalTarget change event will actually contain two distinct arrays\n var newTransports = this.transports[to].slice(0);\n newTransports.splice(index, 1);\n this.transports[to] = newTransports;\n }\n }\n },\n registerTarget: function registerTarget(target, vm, force) {\n if (!inBrowser) { return; }\n\n if (this.trackInstances && !force && this.targets[target]) {\n console.warn(\"[portal-vue]: Target \".concat(target, \" already exists\"));\n }\n\n this.$set(this.targets, target, Object.freeze([vm]));\n },\n unregisterTarget: function unregisterTarget(target) {\n this.$delete(this.targets, target);\n },\n registerSource: function registerSource(source, vm, force) {\n if (!inBrowser) { return; }\n\n if (this.trackInstances && !force && this.sources[source]) {\n console.warn(\"[portal-vue]: source \".concat(source, \" already exists\"));\n }\n\n this.$set(this.sources, source, Object.freeze([vm]));\n },\n unregisterSource: function unregisterSource(source) {\n this.$delete(this.sources, source);\n },\n hasTarget: function hasTarget(to) {\n return !!(this.targets[to] && this.targets[to][0]);\n },\n hasSource: function hasSource(to) {\n return !!(this.sources[to] && this.sources[to][0]);\n },\n hasContentFor: function hasContentFor(to) {\n return !!this.transports[to] && !!this.transports[to].length;\n },\n // Internal\n $_getTransportIndex: function $_getTransportIndex(_ref) {\n var to = _ref.to,\n from = _ref.from;\n\n for (var i in this.transports[to]) {\n if (this.transports[to][i].from === from) {\n return +i;\n }\n }\n\n return -1;\n }\n }\n});\nvar wormhole = new Wormhole(transports);\n\nvar _id = 1;\nvar Portal = Vue.extend({\n name: 'portal',\n props: {\n disabled: {\n type: Boolean\n },\n name: {\n type: String,\n default: function _default() {\n return String(_id++);\n }\n },\n order: {\n type: Number,\n default: 0\n },\n slim: {\n type: Boolean\n },\n slotProps: {\n type: Object,\n default: function _default() {\n return {};\n }\n },\n tag: {\n type: String,\n default: 'DIV'\n },\n to: {\n type: String,\n default: function _default() {\n return String(Math.round(Math.random() * 10000000));\n }\n }\n },\n created: function created() {\n var _this = this;\n\n this.$nextTick(function () {\n wormhole.registerSource(_this.name, _this);\n });\n },\n mounted: function mounted() {\n if (!this.disabled) {\n this.sendUpdate();\n }\n },\n updated: function updated() {\n if (this.disabled) {\n this.clear();\n } else {\n this.sendUpdate();\n }\n },\n beforeDestroy: function beforeDestroy() {\n wormhole.unregisterSource(this.name);\n this.clear();\n },\n watch: {\n to: function to(newValue, oldValue) {\n oldValue && oldValue !== newValue && this.clear(oldValue);\n this.sendUpdate();\n }\n },\n methods: {\n clear: function clear(target) {\n var closer = {\n from: this.name,\n to: target || this.to\n };\n wormhole.close(closer);\n },\n normalizeSlots: function normalizeSlots() {\n return this.$scopedSlots.default ? [this.$scopedSlots.default] : this.$slots.default;\n },\n normalizeOwnChildren: function normalizeOwnChildren(children) {\n return typeof children === 'function' ? children(this.slotProps) : children;\n },\n sendUpdate: function sendUpdate() {\n var slotContent = this.normalizeSlots();\n\n if (slotContent) {\n var transport = {\n from: this.name,\n to: this.to,\n passengers: _toConsumableArray(slotContent),\n order: this.order\n };\n wormhole.open(transport);\n } else {\n this.clear();\n }\n }\n },\n render: function render(h) {\n var children = this.$slots.default || this.$scopedSlots.default || [];\n var Tag = this.tag;\n\n if (children && this.disabled) {\n return children.length <= 1 && this.slim ? this.normalizeOwnChildren(children)[0] : h(Tag, [this.normalizeOwnChildren(children)]);\n } else {\n return this.slim ? h() : h(Tag, {\n class: {\n 'v-portal': true\n },\n style: {\n display: 'none'\n },\n key: 'v-portal-placeholder'\n });\n }\n }\n});\n\nvar PortalTarget = Vue.extend({\n name: 'portalTarget',\n props: {\n multiple: {\n type: Boolean,\n default: false\n },\n name: {\n type: String,\n required: true\n },\n slim: {\n type: Boolean,\n default: false\n },\n slotProps: {\n type: Object,\n default: function _default() {\n return {};\n }\n },\n tag: {\n type: String,\n default: 'div'\n },\n transition: {\n type: [String, Object, Function]\n }\n },\n data: function data() {\n return {\n transports: wormhole.transports,\n firstRender: true\n };\n },\n created: function created() {\n var _this = this;\n\n this.$nextTick(function () {\n wormhole.registerTarget(_this.name, _this);\n });\n },\n watch: {\n ownTransports: function ownTransports() {\n this.$emit('change', this.children().length > 0);\n },\n name: function name(newVal, oldVal) {\n /**\r\n * TODO\r\n * This should warn as well ...\r\n */\n wormhole.unregisterTarget(oldVal);\n wormhole.registerTarget(newVal, this);\n }\n },\n mounted: function mounted() {\n var _this2 = this;\n\n if (this.transition) {\n this.$nextTick(function () {\n // only when we have a transition, because it causes a re-render\n _this2.firstRender = false;\n });\n }\n },\n beforeDestroy: function beforeDestroy() {\n wormhole.unregisterTarget(this.name);\n },\n computed: {\n ownTransports: function ownTransports() {\n var transports = this.transports[this.name] || [];\n\n if (this.multiple) {\n return transports;\n }\n\n return transports.length === 0 ? [] : [transports[transports.length - 1]];\n },\n passengers: function passengers() {\n return combinePassengers(this.ownTransports, this.slotProps);\n }\n },\n methods: {\n // can't be a computed prop because it has to \"react\" to $slot changes.\n children: function children() {\n return this.passengers.length !== 0 ? this.passengers : this.$scopedSlots.default ? this.$scopedSlots.default(this.slotProps) : this.$slots.default || [];\n },\n // can't be a computed prop because it has to \"react\" to this.children().\n noWrapper: function noWrapper() {\n var noWrapper = this.slim && !this.transition;\n\n if (noWrapper && this.children().length > 1) {\n console.warn('[portal-vue]: PortalTarget with `slim` option received more than one child element.');\n }\n\n return noWrapper;\n }\n },\n render: function render(h) {\n var noWrapper = this.noWrapper();\n var children = this.children();\n var Tag = this.transition || this.tag;\n return noWrapper ? children[0] : this.slim && !Tag ? h() : h(Tag, {\n props: {\n // if we have a transition component, pass the tag if it exists\n tag: this.transition && this.tag ? this.tag : undefined\n },\n class: {\n 'vue-portal-target': true\n }\n }, children);\n }\n});\n\nvar _id$1 = 0;\nvar portalProps = ['disabled', 'name', 'order', 'slim', 'slotProps', 'tag', 'to'];\nvar targetProps = ['multiple', 'transition'];\nVue.extend({\n name: 'MountingPortal',\n inheritAttrs: false,\n props: {\n append: {\n type: [Boolean, String]\n },\n bail: {\n type: Boolean\n },\n mountTo: {\n type: String,\n required: true\n },\n // Portal\n disabled: {\n type: Boolean\n },\n // name for the portal\n name: {\n type: String,\n default: function _default() {\n return 'mounted_' + String(_id$1++);\n }\n },\n order: {\n type: Number,\n default: 0\n },\n slim: {\n type: Boolean\n },\n slotProps: {\n type: Object,\n default: function _default() {\n return {};\n }\n },\n tag: {\n type: String,\n default: 'DIV'\n },\n // name for the target\n to: {\n type: String,\n default: function _default() {\n return String(Math.round(Math.random() * 10000000));\n }\n },\n // Target\n multiple: {\n type: Boolean,\n default: false\n },\n targetSlim: {\n type: Boolean\n },\n targetSlotProps: {\n type: Object,\n default: function _default() {\n return {};\n }\n },\n targetTag: {\n type: String,\n default: 'div'\n },\n transition: {\n type: [String, Object, Function]\n }\n },\n created: function created() {\n if (typeof document === 'undefined') { return; }\n var el = document.querySelector(this.mountTo);\n\n if (!el) {\n console.error(\"[portal-vue]: Mount Point '\".concat(this.mountTo, \"' not found in document\"));\n return;\n }\n\n var props = this.$props; // Target already exists\n\n if (wormhole.targets[props.name]) {\n if (props.bail) {\n console.warn(\"[portal-vue]: Target \".concat(props.name, \" is already mounted.\\n Aborting because 'bail: true' is set\"));\n } else {\n this.portalTarget = wormhole.targets[props.name];\n }\n\n return;\n }\n\n var append = props.append;\n\n if (append) {\n var type = typeof append === 'string' ? append : 'DIV';\n var mountEl = document.createElement(type);\n el.appendChild(mountEl);\n el = mountEl;\n } // get props for target from $props\n // we have to rename a few of them\n\n\n var _props = pick(this.$props, targetProps);\n\n _props.slim = this.targetSlim;\n _props.tag = this.targetTag;\n _props.slotProps = this.targetSlotProps;\n _props.name = this.to;\n this.portalTarget = new PortalTarget({\n el: el,\n parent: this.$parent || this,\n propsData: _props\n });\n },\n beforeDestroy: function beforeDestroy() {\n var target = this.portalTarget;\n\n if (this.append) {\n var el = target.$el;\n el.parentNode.removeChild(el);\n }\n\n target.$destroy();\n },\n render: function render(h) {\n if (!this.portalTarget) {\n console.warn(\"[portal-vue] Target wasn't mounted\");\n return h();\n } // if there's no \"manual\" scoped slot, so we create a ourselves\n\n\n if (!this.$scopedSlots.manual) {\n var props = pick(this.$props, portalProps);\n return h(Portal, {\n props: props,\n attrs: this.$attrs,\n on: this.$listeners,\n scopedSlots: this.$scopedSlots\n }, this.$slots.default);\n } // else, we render the scoped slot\n\n\n var content = this.$scopedSlots.manual({\n to: this.to\n }); // if user used