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 @@
-->
-